Event trigger is not working inside ItemControl - wpf

I have a Item control which fills by a list, and list is collection of two parameters 'Time' and 'Description'. For it, i am using a HyperLinkButton for time and a Label for description.
What i want is that, i want to create click event using EventTrigger of hyperLink button in Main viewModel. My code is:
<ItemsControl
x:Name="transcriptionTextControl"
ItemsSource="{Binding MyCollectionOfTranscription, Mode=TwoWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<HyperlinkButton Content="{Binding Time}">
<ToolTipService.ToolTip>
<ToolTip Content="Time"/>
</ToolTipService.ToolTip>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction
Command="{Binding HyperLinkButtonCommand}"
CommandParameter="{Binding
ElementName=transcriptionTextControl }" />
</i:EventTrigger>
</i:Interaction.Triggers>
</HyperlinkButton>
<sdk:Label Content="{Binding Description}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
When i build project, it doesn't give error but ICommand for hyperLink, shows warning as 'Cannot resolve symbol HyperLinkButtonCommand', while this event trigger is working fine outside this .
Not getting, what is actual problem behind it, plz give your valuable suggestion...

First off,
<i:InvokeCommandAction
Command="{Binding HyperLinkButtonCommand}"
CommandParameter="{Binding
ElementName=transcriptionTextControl }" />
The Binding is trying to locate a property called HyperLinkButtonCommand on the instance of the type that is contained within MyCollectionOfTranscription (you don't need to bind to this two-way).
(Side note, sending an ItemsControl into your Command is not MVVM.)
The ItemsControl iterates through each element in this collection, creates a copy of the template defined in ItemsControl.ItemTemplate, and sets the BindingContext equal to this element (I assume its a Transcript). You can tell this from the warnings you get from the binding failing to find your HyperLinkButtonCommand if you crank up databinding debug settings.
Assuming
HyperLinkButtonCommand is a command defined in your ViewModel, and
The root of this xaml is a Window (could be a UserControl, but am assuming here)
Your ViewModel is the DataContext of the Window
you can change the binding to the following and it should work (or you should get a clue from it)
<i:InvokeCommandAction
Command="{Binding HyperLinkButtonCommand,
RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"
CommandParameter="{Binding
ElementName=transcriptionTextControl }" />
I prefer just to give my root an x:Name of "root" and use "ElementName=root" in cases like this.

Related

Binding only works using ElementName, why?

Here is some code snippets of my scenario. On the .xaml side I have an ItemsControl:
<ItemsControl ItemsSource="{Binding StatList}" ItemTemplate="{StaticResource statBox}"></ItemsControl>
The StatList is simply a List of objects.
The DataTemplate contains this TextBlock
<TextBlock x:Name="DataTextBlock" Background="Transparent" DockPanel.Dock="Top" HorizontalAlignment="Center"
Foreground="White" FontSize="11">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<iBehaviors:InvokeMethodTrigger Method="UpdateBinding"
Target="{Binding DataContext, RelativeSource={RelativeSource TemplatedParent}}"
Parameter="{Binding ., RelativeSource={RelativeSource AncestorType=TextBlock}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBlock>
This works the first time I set my StatList on my ViewModel, the TextBlock itself is passed as the Parameter to the InvokeMethodTrigger. However, if I change the StatList on my viewmodel, the next time the Loaded event occurs, the Parameter is null when the InvokeMethodTrigger is triggered.
Oddly enough, when I change the Parameter binding to:
Parameter="{Binding ElementName=DataTextBlock}"
it works the second time when I change my StatList, with the TextBlock being passed as the parameter. I for my life can't figure out why! Any explanations?
You can do it like this in code behind:
SlatList slat = new SlatList();
ItemControl.ItemSource = slat;
ItemControl.DataContext = slat;
make sure to specify a name for the itemcontrol and replace "ItemControl" with the name.
In XAML:
<ItemsControl ItemsSource="{Binding StatList}" DataContext="{Binding SlatList} ItemTemplate="{StaticResource statBox}"> </ItemsControl>

Using an EventTriggers in a DataGridTemplateColumn

I am using Silverlight 5 and in a view I have a Button that, when clicked, loads a particular state. This is done using an EventTrigger and the GoToStateAction markup like so:
<Button x:Name="..." Content="...">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:GoToStateAction StateName="MyState"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
The above works swimmingly, but if I move the Button into a DataGridTemplateColumn it no longer works. Specifically, the app compiles without error and the DataGrid shows the button in a column, but when I click the button the state is not transitioned to.
<data:DataGrid ...>
<data:DataGrid.Columns>
<data:DataGridTemplateColumn>
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="..." Content="...">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:GoToStateAction StateName="MyState"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
</data:DataGrid.Columns>
</data:DataGrid>
Is it not possible to have EventTriggers in a DataGridTemplateColumn? Or do I need to declare them using alternate syntax?
Thanks
The GoToStateAction reference states that, if no target is specified, it will walk up the visual tree and apply the state change on the first element it finds that defines states. In your second case, I believe that some elements of the DataGrid visual tree (cells, rows, etc) may be taken in account before your usercontrol, thus the behavior you've seen.
Since you are using Silverlight 5, you can try to set the TargetObject property of the action to your desired control, using a binding with a relative source looking of a ancestor of the type of UserControl.
<ei:GoToStateAction StateName="MyState" TargetObject="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}"/>

Access my Window's DataContext inside a DataTemplate

I'm working on a WPF application and I'm using the MVVM pattern. I use MVVMLight to help me handle some Events. I need to forward the "Click" event so that I can pass the arguments as well so that I can know for sure which item that sent the event. If I use the "Command" I cant know for sure that it was the selected item that sent the event - as the item doesnt need to be selected to right click on it.
This is my code for displaying a list of "order lines". There are two types of order lines, and for one of the data types; "AccessoryOrderLine" - I want to add a context menu.
My problem is that I cannot access my Window's DataContext. I've named the root node in the Window "root", and I'm trying to access the root's DataContext, but this failes with the following error:
System.Windows.Data Error: 4 : Cannot find source for binding with
reference 'ElementName=root'.
BindingExpression:Path=DataContext.PackAccessory; DataItem=null;
target element is 'EventToCommand' (HashCode=5903270); target property
is 'Command' (type 'ICommand')
<ListBox HorizontalContentAlignment="Stretch" Margin="10,0,10,10" DockPanel.Dock="Bottom" Grid.Row="1" ItemsSource="{Binding OrderLines, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True, UpdateSourceTrigger=PropertyChanged}">
<ListBox.Resources>
<DataTemplate DataType="{x:Type m:UnitOrderLine}">
<v:OrderLine />
</DataTemplate>
<DataTemplate DataType="{x:Type m:AccessoryOrderLine}">
<v:OrderLine>
<v:OrderLine.ContextMenu>
<ContextMenu>
<MenuItem Header="Pack 1" IsCheckable="False">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cmd:EventToCommand Command="{Binding ElementName=root, Path=DataContext.PackAccessory }" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</MenuItem>
</ContextMenu>
</v:OrderLine.ContextMenu>
</v:OrderLine>
</DataTemplate>
</ListBox.Resources>
</ListBox>
I've also tried to use "TemplatedParent" and then I get access to my "OrderLine" DataContext, but I cant get one step further back to my "MainWindowModel".
http://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/
Found a solution to my problem :)
Found a solution. Updated my original post with the link to my solution.
Its not issue with the DataTemplate. Binding with ElemetName works in all cases except in the case of ContextMenu since it does not lies in the same visual tree as of your window. However, there is one hack where you can use the PlacementTarget property of your context menu.
For details refer to this link - http://social.msdn.microsoft.com/Forums/nl/wpf/thread/526ab350-8788-4bc6-a98a-1e4dee6ad33a
It contains exactly what you are trying to achieve here.
Seems like here are answers for your question:
ElementName Binding from MenuItem in
ContextMenu
WPF MenuItem.Command binding to
ElementName..

Treeview context menu command not firing

I have a treeview bound to a Observable collection of some property type. There is a HierarchicalDataTemplate that shows the data in treeview. Now i need to show specific context menu for each HierarchicalDataTemplate item.
I am using the following XAML to show context menu:
<HierarchicalDataTemplate ItemsSource="{Binding Collections}">
<TextBlock Text="{Binding Path=Name}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Create" Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.AddCommand}" CommandParameter="{Binding}"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</HierarchicalDataTemplate>
Here the AddCommand is written in the view model that is bound to this under control..
I am able to see the context menu, but event is not firing on click on menu item.
Please help..
Your command binding will not work because the ContextMenu is not on the same logical tree as your UserControl is, therefore it will not find the UserControl ancestor. However your ContextMenu should inherit its container's datacontext automatically.
So this should work -
<ContextMenu>
<MenuItem Header="Create" Command="{Binding AddCommand}" CommandParameter="{Binding}"/>
</ContextMenu>
However the AddCommand property should exist on your HierarchicalDataTemplate bound item.
EDIT:
If your Command is not defined in your HierarchicalDataTemplate's bound item and instead in your UserControl. Then another think you may try is giving your UserControl a name, and then bind the command to it by ElementName. Like this
Updated again:
<ContextMenu>
<MenuItem Header="Create" Command="{Binding ElementName="MyUserControl" Path="DataContext.AddCommand"}" CommandParameter="{Binding}"/>
</ContextMenu>

Handling listbox item selected

Seems like this would be a common problem...
I'm using MVVM Light
I have a listbox defined in my xaml
<ListBox ItemsSource="{Binding Events}" SelectedItem="{Binding SelectedEvent, Mode=TwoWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<cmd:EventToCommand Command="{Binding EventPageCommand, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.ItemTemplate>
<DataTemplate>
<Border Padding="0,0,0,22">
<StackPanel>
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Preview}" TextWrapping="Wrap"/>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This works all fine and dandy, except the user is able to click below this list and the event is still fired.
I can modify it to use SelectionChanged such as:
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand Command="{Binding EventPageCommand, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
But then if I use the back button, I cannot select the same item twice since the selection hasn't actually changed. I could probably go into my codebehind and set the selected index = -1 on page load, but this seems like quite a hack, and I use list boxes in quite a few places and I would hate to have to remember to do this hack everywhere.
Any ideas?
edit
I added background color to my listbox and my listbox items and while my listbox items only take up what space the text occupies, the listbox takes up the entire screen, extending below the last listbox item. Perhaps one way would be to shore up this extra space.
update
what seems to work best so far is to set the VerticalAlignmnet="Top" on the listbox (this shores up the extra space on the bottom, not sure why, but then scrolling ends at the bottom of the list and not the bottom of the screen). I then disabled the scrolling on the listbox itself ScrollViewer.VerticalScrollBarVisibility="Disabled" and wrapped the whole thing in a <ScrollViewer>
Currently you are looking for the Tap event on the whole of the list box, not a individual item.
This is how I typically I use interaction triggers:
<DataTemplate DataType="{x:Type ViewModel:DataItem}" x:Key="ItemTemplate">
<ContentControl>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<i:InvokeCommandAction Command="{Binding Tap}"/>
</i:EventTrigger>
<i:EventTrigger EventName="KeyUp">
<i:InvokeCommandAction
Command="{Binding RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type ListBox}},
Path=DataContext.KeyUpCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBox Text="{Binding Name}"/>
</ContentControl>
</DataTemplate>
In this example the Tap event binds to a command on the DataContext (a data item) of an ItemsControl, whereas the KeyUp event binds to a command on the parent DataContext of the ListBox.
If all you want is to get an event when the user taps an item, why don't you use the trigger code you have on the Border element of the template?
You probably will have to use the Source property to bind the command to the ViewModel, but that can also be easely achieved (will just depend on how you are setting the Page DataContext right now)

Resources