How to subscribe event in datatemplate of listview - wpf

In WPF I have a listview that is bound to an ObservableCollection.
XAML:
<ListView Name="listView" DockPanel.Dock="Top" ItemsSource="{Binding Path=ListOfOldData}" SelectedItem="{Binding Path=SelectedOldData, Mode=TwoWay}" SelectionMode="Single">
<ListView.ContextMenu>
<ContextMenu>
<Button Content="Load" Command="{Binding Path=LoadCommand}" Name="loadButton" Height="23" Width="75" DockPanel.Dock="Left"/>
<!-- Is working just fine -->
</ContextMenu>
</ListView.ContextMenu>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock MouseLeftButtonDown="TextBlock_MouseLeftButtonDown"
Text="{Binding Path=Name}" FontWeight="Bold"><TextBlock Text=" - " FontWeight="Normal"/><TextBlock Text="{Binding Path=UpdateDatum}" FontWeight="Normal"/></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
What I actually wanted to receive is a double-click on the selected-item. As I can't bind a command to a textblock in xaml (can I?) I tried doing this via the MouseLeftButtonDown-Event. But the event is never received!
C# (in code behind):
private void TextBlock_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Console.WriteLine("MouseLeftButtonDown received!");
}
What am I doing wrong? How can I receive the event? Btw.: The command of the contextmenu is working just fine :)
UPDATE I found my error --> I added the event in the wrong usercontrol. Damn my missing concentration. Sorry for bugging you all.

you can simply use InvokeCommandAction from blend sdk (System.Windows.Interactivity.dll)
<ListView x:Name="lvw" ItemsSource="{Binding ListOfOldData}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding Path=OpenCommand}"
CommandParameter="{Binding ElementName=lvw, Path=SelectedItem}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListView>
EDIT:
viewmodel should look something like this:
public List<object> ListOfOldData{ get; set; }
private DelegateCommand<object> _openCommand;//or RelayCommand
public DelegateCommand<object> OpenCommand
{
get { return _openCommand?? (this._openCommand= new DelegateCommand<object>(this.Execute)); }
}
private void Execute(object obj)
{
//obj is your selectedItem
}
ps: dunno your type thats why object

The ListView has a DoubleClick MouseEvent.
This should do it :
<ListView MouseDoubleClick="DoubleClickOnIt">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}" FontWeight="Bold">
<TextBlock Text=" - " FontWeight="Normal"/>
<TextBlock Text="{Binding Path=UpdateDatum}" FontWeight="Normal"/>
</TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
<ListViewItem>
dddd
</ListViewItem>
<ListViewItem>
eeeee
</ListViewItem>
</ListView>
And the code behind :
private void DoubleClickOnIt(object sender, MouseButtonEventArgs e)
{
var listView = sender as ListView;
var selectedItem = listView.SelectedItem;
Console.WriteLine("received!");
}

Related

TextBox's Text Property returns null when using CommandParameter

I managed to create a Command, like so:
Code-behind
public static RoutedCommand GetValueCommand = new RoutedCommand();
private void ExecutedGetValueCommand(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("Custom Command Executed");
Button b = (sender) as Button;
MessageBox.Show(b.CommandParameter.ToString());
}
private void CanExecuteGetValueCommand(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
XAML
<UserControl.CommandBindings>
<CommandBinding Command="{x:Static local:ReturnsUserControl.GetValueCommand}"
Executed="ExecutedGetValueCommand"
CanExecute="CanExecuteGetValueCommand" />
</UserControl.CommandBindings>
<ListView>
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<TextBlock Text="{Binding ProductDescription}"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<TextBox x:Name="txtExchangeQuantity"
Tag="{Binding ProductBarcode}"/>
<Button Content="Add"
Tag="{Binding ProductBarcode}"
Command="{x:Static local:ReturnsUserControl.GetValueCommand}"
CommandParameter="{Binding Text,ElementName=txtExchangeQuantity}"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
When I clicked the button, CommandParameter always return null, even when I put something in the text box, and I'm sure that the command is working because Custom Command Executed shows.
What I want to achieve here is to get the value of the TextBox that has the same Tag value as the Button's Tag (same barcode), because there will be multiple instances of both the TextBox and the Button, and the Tag is the only one that can pair them.
As we spoke in the comments, you should be looking for ExecutedRoutedEventArgs.Parameter instead of checking the sender in your event handler.

InvokeCommandAction not calling

I have a combobox and I need a command in my view model to bind to its ContextMenuOpening event. I've tried referencing System.Windows.Interactivity and using InvokeCommandAction, but the command is not calling. Does anyone see where I'm going wrong?
<ComboBox x:Name="comboBoxAs" Grid.Column="0" VerticalAlignment="Top" Margin="928,62,0,0" Height="25"
ItemsSource="{Binding Source={StaticResource sas}}"
SelectedItem="{Binding Path=as, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource ComboBoxDefault}" HorizontalAlignment="Left" Width="212" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="ContextMenuOpening">
<i:InvokeCommandAction Command="{Binding ContextMenuOpeningCommand, Mode=OneWay}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
ViewModel:
public ICommand ContextMenuOpeningCommand
{
get
{
if (_contextMenuOpeningCommand == null)
{
_contextMenuOpeningCommand = new RelayCommand<object>(param => this.ContextMenuOpening(),
null);
}
return _contextMenuOpeningCommand;
}
}
public void ContextMenuOpening()
{
System.Windows.MessageBox.Show("test", "test");
}
private ICommand _contextMenuOpeningCommand;
Please try DropDownOpened to see whether the command gets hit. I tried it and it works here. Hope this helps :)

Selectionchanged event firing for every row

I am using a cascading comboboxes inside datagrid.I am able to get the datas based on selectionchanged but that event is firing for every row.
Here is my code:
<sdk:datagridtemplatecolumn header="Category" width="110">
<sdk:datagridtemplatecolumn.celltemplate>
<datatemplate>
<combobox foreground="Black" height="30" isenabled="{Binding Source={StaticResource EffortViewModel}, Path=ComboBoxStatus}" itemssource="{Binding Source={StaticResource EffortViewModel},Path=ProjTypeTaskCtry}" displaymemberpath="TaskCtgyName" selectedvaluepath="TaskCtgy_FK" selectedvalue="{Binding Source={StaticResource EffortViewModel}, Path=TaskCtgy_FKField,Mode=TwoWay}" />
</datatemplate>
</sdk:datagridtemplatecolumn.celltemplate>
</sdk:datagridtemplatecolumn>
<sdk:datagridtemplatecolumn header="SubCategory" width="110">
<sdk:datagridtemplatecolumn.celltemplate>
<datatemplate>
<combobox foreground="Black" height="30" isenabled="{Binding Source={StaticResource EffortViewModel}, Path=ComboBoxStatus}" itemssource="{Binding Source={StaticResource EffortViewModel},Path=SubCtry,Mode=OneWay}" displaymemberpath="TaskSubCtgyName" selectedvaluepath="{Binding TaskSubCtgy_PK, Mode=TwoWay}" selectedvalue="{Binding TaskSubCtgy_FKField,Mode=OneTime}" selectedindex="{Binding TaskSubCtgy_FKField}" />
</datatemplate>
</sdk:datagridtemplatecolumn.celltemplate>
</sdk:datagridtemplatecolumn>
I had the same problem in Silverlight MVVM. I found a solution for this from somewhere. Hope this will help you.
namespace Test
{
public class ComboBoxSelectionChange : TriggerAction<DependencyObject>
{
public ComboBoxSelectionChange()
{
}
public ComboBox DayComboBox
{
get { return (ComboBox)GetValue(DayComboBoxProperty); }
set { SetValue(DayComboBoxProperty, value); }
}
public static readonly DependencyProperty DayComboBoxProperty =
DependencyProperty.Register("DayComboBox",
typeof(ComboBox),
typeof(ComboBoxSelectionChange),
new PropertyMetadata(null, OnDayComboBoxPropertyChanged));
private static void OnDayComboBoxPropertyChanged(DependencyObjectd, DependencyPropertyChangedEventArgs e)
{
var source = d as ComboBoxSelectionChange;
if (source != null)
{
var value = (ComboBox)e.NewValue;
}
}
protected override void Invoke(object o)
{
if (this.DayComboBox != null)
{
//this method will execute when the selection is changed
}
}
}
}
Use the Test namespace in Usercontrol assembly
xmlns:Common="clr-namespace:Test"
<UserControl.Resources>
<Common:ComboBoxSelectionChange x:Name="ComboBoxItem"/>
</UserControl.Resources>
<DataTemplate x:Key="EditMondayDataTemplate">
<ComboBox x:Name="cmbMonday" Height="26" Margin="3" ItemsSource="{Binding Monday,Mode=OneTime}" DisplayMemberPath="displayText" SelectedItem="{Binding Path=MonSelected,Mode=TwoWay}" HorizontalAlignment="Center" VerticalAlignment="Center" Width="80">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<Common:ComboBoxSelectionChange DayComboBox="{Binding ElementName=cmbMonday}" TextParam="Monday"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
</DataTemplate>

ListBox with hyperlink -> selection changed

I want to do with xaml bindings such feature:
Listbox contains hyperlinks.
When hyperlink clicked - we go to another frame
But also SelectedItem must changed, and on another frame we show info about selected item.
I want it without subscribing click/selected events. Only declarative
Example of my listbox
<ListBox Grid.Row="1"
x:Name="OrderTypesListBox"
ItemsSource="{Binding OrderTypes, Mode=OneWay}"
SelectedItem="{Binding SelectedCall.OrderType, Mode=TwoWay}"
>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" />
<HyperlinkButton Style="{StaticResource LinkStyle}" NavigateUri="/WindowPage" TargetName="ContentFrame" Content="WindowPage"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Now solve like that
<ListBox Grid.Row="1"
x:Name="OrderTypesListBox"
ItemsSource="{Binding OrderTypes, Mode=OneWay}"
SelectedItem="{Binding SelectedCall.OrderType, Mode=TwoWay}"
>
<ListBox.ItemTemplate>
<DataTemplate>
<HyperlinkButton
TargetName="ContentFrame"
NavigateUri="{Binding OrderTypeNextPage}"
Content="{Binding Name}"
Click="HyperlinkButton_Click"
Tag="{Binding}"
/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
private void HyperlinkButton_Click(object sender, RoutedEventArgs e)
{
OrderTypesListBox.SelectedItem = (sender as HyperlinkButton).Tag;
}
Don't use a HyperlinkButton. Perform the needed actions when the SelectedItem changes in your ViewModel.
Edit: If you need to respond to all click events even if the item is already selected then you can use a Behavior to do this. Just create a behavior for the TextBlock that navigates on the TextBlock click event.
Edit2: Behaviors are pretty simple to code up and easy to use (and don't break the MVVM paradigm).
public class NavigatingTextBlockBehavior : Behavior<TextBlock>
{
protected override void OnAttached()
{
AssociatedObject.MouseLeftButtonDown += new MouseButtonEventHandler(OnMouseDown);
}
private void OnMouseDown(object sender, MouseButtonEventArgs e)
{
NavigationService.Navigate(new Uri("/WindowPage"));
}
}

EventTrigger not working inside ItemsControl in MVVM

I want to bind multiple buttons dynamically in MVVM.
1.I Dynamically created buttons using ItemControl
2. It did not Invoke Trigger Click Event.
Please help me on this.
<ItemsControl ItemsSource="{Binding ComponentList,Mode=TwoWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Tag="{Binding WorkFlowCompId}">
<Button.Content>
<TextBlock Text="{Binding ComponentName,Mode=TwoWay}"/>
</Button.Content>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding ComponentSelected}"
CommandParameter="{Binding WorkFlowCompId,Mode=TwoWay}" >
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Your problem is that the command is getting the context from its template and there it cannot access the root of the ViewModel. Add this class to your solution:
public class DataContextProxy : FrameworkElement
{
public DataContextProxy()
{
this.Loaded += new RoutedEventHandler(DataContextProxyLoaded);
}
void DataContextProxyLoaded(object sender, RoutedEventArgs e)
{
Binding binding = new Binding();
if (!String.IsNullOrEmpty(BindingPropertyName))
{
binding.Path = new PropertyPath(BindingPropertyName);
}
binding.Source = this.DataContext;
binding.Mode = BindingMode;
this.SetBinding(DataContextProxy.DataSourceProperty, binding);
}
public Object DataSource
{
get { return (Object)GetValue(DataSourceProperty); }
set { SetValue(DataSourceProperty, value); }
}
public static readonly DependencyProperty DataSourceProperty =
DependencyProperty.Register("DataSource", typeof(Object), typeof(DataContextProxy), null);
public string BindingPropertyName { get; set; }
public BindingMode BindingMode { get; set; }
}
then use it in you XAML like so:
<UserControl.Resources>
<library:DataContextProxy x:Key="DataContextProxy"/>
</UserControl.Resources>
Then in your command binding:
<Button Tag="{Binding WorkFlowCompId}">
<Button.Content>
<TextBlock Text="{Binding ComponentName,Mode=TwoWay}"/>
</Button.Content>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding DataSource.ComponentSelected, Source={StaticResource DataContextProxy}"
CommandParameter="{Binding WorkFlowCompId,Mode=TwoWay}" >
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
A first pass at what you Xaml should look like:-
<ItemsControl ItemsSource="{Binding ComponentList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding SelectComponent}">
<TextBlock Text="{Binding ComponentName}"/>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I suspect as Derek alludes to in his comment you have a ComponentSelected command on the container. However you should move this command ot the view model for the component. Note I've also renamed it to SelectComponent so that is sounds like an action rather than a property.
TwoWay binding has been removed it wouldn't be doing anything in this case. Assigning a Tag value from a simple binding should be setting off alarm bells that the design is having some problems.
BTW, since you are doing a form of selection would not a ListBox be more appropriate in this case?

Resources