Passing objects in ItemsControl using a DataTemplate - c#

So basically I have a class Step.
public class Step : INotifyPropertyChanged
{
public int Index { get; set; }
public int Time { get; set; }
}
In my xaml, I want to represent Steps using one DataTemplate and an ItemsControl. My DataTemplate looks like this:
<DataTemplate x:Key="StepTemplate" DataType="data:Step">
<Label Content="{Binding Index}"/>
<Label Content="{Binding Time}"/>
</DataTemplate>
And my ItemsControl looks like this.
<ItemsControl Name="ListSteps" ItemsSource="{Binding Steps}" Grid.IsSharedSizeScope="True" ItemTemplate="{StaticResource StepTemplate}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding Path=DataContext.StepClick, ElementName=ListSteps}" CommandParameter="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ItemsControl>
So pretty much what should happen: Whenever I click on a Step, the method StepClick gets called and the Step object that I clicked on is passed as a parameter.
What is actually happening: Whenever I click on a Step, the method StepClick gets called, but not the Step Object gets passed as a parameter but an object of my view gets passed. Which is not at all what I want.
How can I pass an object of Step whenever I click on a Step?

Move the interaction trigger to the root element of the ItemTemplate:
<DataTemplate x:Key="StepTemplate" DataType="data:Step">
<StackPanel Background="Transparent">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding Path=DataContext.StepClick, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Label Content="{Binding Index}"/>
<Label Content="{Binding Time}"/>
</StackPanel>
</DataTemplate>

Related

Binding double click in nested listview

I've got a ListView nested into another Listview. Now I want to bind an double-click event to the ListViewItems of the inner ListView
<UserControl>
<UserControl.Resources>
<DataTemplate x:Key="DefaultTemplate">
<ListView Name="jobsView" ItemsSource="{Binding jobs}" SelectedItem="{Binding Path=SelectedProduction}" >
<ListView.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding Path=DataContext.ItemSelectedCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" CommandParameter="{Binding ElementName=jobsView, Path=SelectedItem}" />
</ListView.InputBindings>
</ListView>
</DataTemplate>
</UserControl.Resources>
<ListView Name="weekView" ItemsSource="{Binding dayList}" ItemTemplate="{StaticResource DefaultTemplate}" >
</ListView>
</UserControl>
I created a RelayCommand called ItemSelectedCommand in my ViewModel.
public RelayCommand ItemSelectedCommand { get; private set; }
The RelayCommand is not getting triggered. I guess I'm setting the wrong RelativeSource. How would it look correct?
Where is your ListView inserted. Is there in a Visual Tree a parent with type of UserControl?
Also, what's quite good to fix Binding errors is to take a look at the Console. There should be Binding errors that might point you wherte is the mistake. Usually is writes down where it is trying to search for object and property :)
Also, I am not sure if private get; is actually allowed when binding to property.
<StackPanel Grid.Column="1">
<StackPanel.Resources>
<DataTemplate x:Key="DefaultTemplate" DataType="{x:Type sys:String}">
<StackPanel>
<TextBlock Text="{Binding .}"/>
<ListView>
<ListView.ItemsSource>
<CompositeCollection>
<sys:String>Sub Item</sys:String>
</CompositeCollection>
</ListView.ItemsSource>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}">
<TextBlock.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding DataContext.RenameCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}, PresentationTraceSources.TraceLevel=High}" CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}}"/>
</TextBlock.InputBindings>
</TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</DataTemplate>
</StackPanel.Resources>
<ListView ItemTemplate="{StaticResource DefaultTemplate}">
<ListView.ItemsSource>
<CompositeCollection>
<sys:String> First Item</sys:String>
</CompositeCollection>
</ListView.ItemsSource>
</ListView>
</StackPanel>
Reason why it wasn't working for you is because the double Click was on an actual ListViewItem and NOT the ListView.

Context menu event or trigger of a control inside an itemtemplate of a listview is not fired

I've got following problem. Following situation in my xaml code:
<ListView ItemsSource="{Binding ListViewItems}">
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<Label Content="Test">
<Label.ContextMenu>
<ContextMenu ItemsSource="{Binding MenuItems}">
</ContextMenu>
</Label.ContextMenu>
</Label>
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseUp">
<i:InvokeCommandAction Command="{Binding LabelMouseUpCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ListView>
After clicking a label no context menu is shown and the trigger does not work as well, LabelMouseUpCommand method is not entered. I fear the listview handles the click itself and does not pass it to the embedded controls.
Is there any way to pass it to the controls. In future i want to add several controls to the itemtemplate and everyone has it own different context menu.
I found the answer for my problem with this stackoverflow article
There the author explains that the contextmenu does not lie in the same visual tree as the listview. Therefore my initial binding can't work because the source can't be found within the visual tree.
Furthermore Sinatr was totally right, the trigger was initially defined for listview, not for the label.
Here is my working code:
<ListView ItemsSource="{Binding ListViewItems}" x:Name="listViewMain">
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<Label Content="Test">
<Label.ContextMenu>
<ContextMenu ItemsSource="{Binding DataContext.MenuItems, Source={x:Reference listViewMain}}">
</ContextMenu>
</Label.ContextMenu>
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseUp">
<i:InvokeCommandAction Command="{Binding DataContext.LabelMouseUpCommand, Source={x:Reference listViewMain}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Label>
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

How to catch a custom event with Interactivity.EventTrigger

I'm trying to catch my custom event (TextUpdateEvent) with Interactivity.EventTrigger in the way shown below. But this code gives rise to System.ArgumentException with the message that "The event "MyTextBox" was not found in the type "tool:MyTextBox.TextUpdateEvent"." Could you tell me what is wrong with this?
<UserControl x:Class="mynamespace.MyControl"
xmlns:local="clr-namespace:mynamespace"
xmlns:info="clr-namespace:mynamespace.info"
xmlns:tool="clr-namespace:mynamespace.tool"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Border>
<ItemsControl ItemsSource="{Binding MyItems}" Name="itemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="info:MyInfo">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding MyComment}"/>
<tool:MyTextBox Text="{Binding MyName}" x:Name="myTextBox">
<i:Interaction.Triggers>
<i:EventTrigger EventName="tool:MyTextBox.TextUpdateEvent"
SourceObject="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type tool:MyTextBox}}}"
SourceName="myTextBox">
<ei:CallMethodAction MethodName="UpdateMyInfo" TargetObject="{Binding Path=DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}, Mode=TwoWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</tool:MyTextBox>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
</Grid>
namespace mynamespace.tool
{
//used to judge whether or not the update suceeded
public delegate bool MyTextBoxUpdateHandler(object sender);
public partial class MyTextBox : TextBox
{
public MyTextBoxUpdateHandler TextUpdateEvent { get; set; }
}
}
Or, do I need to create a custom trigger to catch my custom event?
public class TextUpdateEventTrigger : EventTriggerBase<MyTextBox>
{
// I don't know what I should do here,
// since the event I'd like to catch is not a RoutedEvent.
}
You should change the TextUpdateEvent to an event instead of a property.
public event MyTextBoxUpdateHandler TextUpdateEvent;
Since you're calling a method in code-behind you also do not need to attach Interaction triggers:
<tool:MyTextBox
x:Name="myTextBox"
Text="{Binding MyName}"
TextUpdateEvent="UpdateMyInfo" />
The DataContext Property does not need to be bound on the UserControl itself, because it's already a partial class of the UserControl, you can remove that too.

WPF MVVM Checkbox stop Command from firing on Data Bind

I have WPF MVVM application and I am currently trying to use a Checkbox that is bound to a column in a list that it is bound to. I have a EventTriggers set and the command bound to the VM. Everything fires great....Except I do NOT want the events to fire when populated from the list, ONLY when the user checks or unchecks the checkbox. See Code:
<StackPanel Orientation="Vertical" Background="Transparent" DockPanel.Dock="Right" Margin="2,0" VerticalAlignment="Center">
<Label Content="Active" />
<CheckBox x:Name="CbArchiveAoi" VerticalAlignment="Center" HorizontalAlignment="Center" FlowDirection="RightToLeft" IsChecked="{Binding Path=PatientAoi.AoiIsActive}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction Command="{Binding ArchiveAoiCommand, Mode=OneWay}"
CommandParameter="{Binding ElementName=CbArchiveAoi}"/>
</i:EventTrigger>
<i:EventTrigger EventName="Unchecked">
<i:InvokeCommandAction Command="{Binding ArchiveAoiCommand, Mode=OneWay}" CommandParameter="{Binding ElementName=CbArchiveAoi}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
</StackPanel>
public ICommand ArchiveAoiCommand
{
get { return new RelayCommand<object>(ArchiveAoiExecute, AlwaysTrueCanExecute); }
}
private void ArchiveAoiExecute(object obj)
{
string dddd = obj.ToString();
}
I'd remove the Interaction.Triggers and use the CheckBox.CommandandCommandParameter properties instead.
<CheckBox x:Name="CbArchiveAoi"
VerticalAlignment="Center"
<-- snip -->
Command="{Binding ArchiveAoiCommand, Mode=OneWay}"
CommandParameter="{Binding ElementName=CbArchiveAoi}" />

Apply multiple AttachedCommandBehaviors using a style

I am trying to use AttachedCommandBehavior V2 to translate ListBoxItem events such as double click into commands that are execute against the view model.
I want to fire commands for multiple events, this is the example code I am trying to emulate:
<Border Background="Yellow" Width="350" Margin="0,0,10,0" Height="35" CornerRadius="2" x:Name="test">
<local:CommandBehaviorCollection.Behaviors>
<local:BehaviorBinding Event="MouseLeftButtonDown" Action="{Binding DoSomething}" CommandParameter="An Action on MouseLeftButtonDown"/>
<local:BehaviorBinding Event="MouseRightButtonDown" Command="{Binding SomeCommand}" CommandParameter="A Command on MouseRightButtonDown"/>
</local:CommandBehaviorCollection.Behaviors>
<TextBlock Text="MouseDown on this border to execute the command"/>
</Border>
Since I want to apply that to a ListBoxItem, I am trying to do it through a style by doing:
<ListBox.ItemContainerStyle>
<Style>
<Setter Property="acb:CommandBehaviorCollection.Behaviors">
<Setter.Value>
<acb:CommandBehaviorCollection>
<acb:BehaviorBinding Event="MouseDoubleClick" Command="{Binding DataContext, RelativeSource={RelativeSource AncestorType=ListBox}}" CommandParameter="{Binding}"/>
<acb:BehaviorBinding Event="KeyUp" Command="{Binding DataContext, RelativeSource={RelativeSource AncestorType=ListBox}}" CommandParameter="{Binding}"/>
</acb:CommandBehaviorCollection>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
But I get a compile error with that code that says error MC3089: The object 'CommandBehaviorCollection' already has a child and cannot add 'BehaviorBinding'. 'CommandBehaviorCollection' can accept only one child. Line 39 Position 11.
Also if I comment out one of the BehaviorBindings then it compiles but I get a runtime xaml load exception saying "Value cannot be null. Parameter name: property", so I'm not sure if I'm even taking the correct approach.
Can anyone provide an example of the correct syntax to set multiple behavior bindings on a ListBoxItem?
My solution uses interaction triggers and the ItemTemplate not the ItemContainerStyle.
This invokes a mouse double click or key up command in the text box, not the whole list box item.
<UserControl.Resources>
<DataTemplate DataType="{x:Type ViewModel:DataItem}" x:Key="ItemTemplate">
<ContentControl>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding DoubleClickCommand}"/>
</i:EventTrigger>
<i:EventTrigger EventName="KeyUp">
<i:InvokeCommandAction Command="{Binding KeyUpCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBox Text="{Binding Name}">
</TextBox>
</ContentControl>
</DataTemplate>
</UserControl.Resources>
<ListBox x:Name="listBox" ItemTemplate="{StaticResource ItemTemplate}" ItemsSource={Binding Items} />
Where DataItem is something like
class DataItem : INotifyPropertyChanged
{
public string Name{get;set}
.. etc
}
and the view model set on the DataContext has an IList<DataItems> Items{get; private set} property.

Categories

Resources