wpf eventsetter handler binding in style - c#

I have a style, and I want to bind a command to the EventSetter's Handler with RelativeSource. The command is in the viewModel.
<Style x:Key="ItemTextBlockEventSetterStyle" TargetType="{x:Type TextBlock}">
<EventSetter Event="MouseLeftButtonDown"
Handler="{Binding TextBlockMouseLeftButtonDownCommand,
RelativeSource={RelativeSource Self}}"/>
</Style>
The problem is that I get an error, because something is wrong with this (maybe it's not possible to do this in such easy way)
I've googled a lot before, and I found the AttachedCommandBehaviour, but I think it doesn't work with style.
Could you give some hints on how to solve this problem?
Update 13/10/2011
I found this in the MVVM Light Toolkit EventToCommand example program:
<Button Background="{Binding Brushes.Brush1}"
Margin="10"
Style="{StaticResource ButtonStyle}"
Content="Simple Command"
Grid.Row="1"
ToolTipService.ToolTip="Click to activate command">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cmd:EventToCommand Command="{Binding SimpleCommand}" />
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeave">
<cmd:EventToCommand Command="{Binding ResetCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
But here, the binding isn't in the style. How can I put this EventToCommand to the style of the button?

Right now you are binding the MouseLeftButtonDown Event to TextBlock.TextBlockMouseLeftButtonDownCommand. TextBlockMouseLeftButtonDownCommand is not a valid property for a TextBlock, nor does it sound like it's an Event Handler.
I use the AttachedCommandBehavior all the time in styles for hooking up a Command to an Event. The syntax usually looks like this (note the DataContextin the Command Binding):
<Style x:Key="ItemTextBlockEventSetterStyle" TargetType="{x:Type TextBlock}">
<Setter Property="local:CommandBehavior.Event" Value="MouseLeftButtonDown" />
<Setter Property="local:CommandBehavior.Command"
Value="{Binding DataContext.TextBlockMouseLeftButtonDownCommand,
RelativeSource={RelativeSource Self}}" />
</Style>
The alternative is to hook the EventSetter up to an event in the code-behind, and process the command from there:
<Style x:Key="ItemTextBlockEventSetterStyle" TargetType="{x:Type TextBlock}">
<EventSetter Event="MouseLeftButtonDown"
Handler="TextBlockMouseLeftButtonDown"/>
</Style>
Event handler in code behind...
void TextBlockMouseLeftButtonDown(object sender, MouseEventArgs e)
{
var tb = sender as TextBlock;
if (tb != null)
{
MyViewModel vm = tb.DataContext as MyViewModel;
if (vm != null && TextBlockMouseLeftButtonDownCommand != null
&& TextBlockMouseLeftButtonDownCommand.CanExecute(null))
{
vm.TextBlockMouseLeftButtonDownCommand.Execute(null)
}
}
}

As you are using MVVM, I suggest you Galasoft MVVM Light Toolkit EventToCommand

My answer on this question does the trick without any external tool kits/libraries. However, it does not use RelativeSource, and it is not 100% MVVM. It requires one line of code in a code-behind event handler.

Related

Make an event bubble up from the children upwards

AFAIK bubbling means that if an event is triggered on the children, the same event will be triggered on the parents.
I have this piece of code:
<ListView Name="lvFiles" SelectedItem="{Binding SelectedTabFilePath, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Path=SelectedItem.Files,ElementName=trvFiles, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Path=Name}"
Command="{Binding DataContext.OpenFileFromTreeView,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}}"
CommandParameter="{Binding DataContext,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}}"
Background="Transparent"
BorderBrush="Transparent"
>
</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The problem is that if I click on the button, the property SelectedTabFilePath from SelectedItem will not be filled with data (because I didn't click on the item of the ListView).
So, my question is:
Is there any way to trigger the Click event of the button and that of the ListView too?
Or if not, what would be the way to set the SelectedTabFilePath from the SelectedItem attribute when I click on that button, too?
The Click event is indeed bubbling, which means it will be routed up the elements of visual tree until the root element is reached. However, the Button itself handles the event, because its Command property was bound. That means its parent elements, including ListViewItem (the item container) will ignore the event, therfore not select the item.
There is a workaround using the fact that the Button receives keyboard focus upon clicking. You add an item container style with a trigger that sets the IsSelected property of the ListViewItem once the keyboard focus is within, denoted by the IsKeyboardFocusWithin property.
<ListView ...>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True"/>
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<!-- ...your other markup. -->
</ListView>
how to handle WPF listbox selectionchanged event using MVVM
I think you could use interaction to trigger the same command(OpenFileFromTreeView) when the listbox selection changes.
Instead of:
AncestorType={x:Type ListView}
What if you try:
AncestorType={x:Type ListViewItem}

WPF - how do I give a command to menu item with children

I have a databound hierarchical menu in WPF. All items are displayed, but the commands only fire for the leafs of the menu, not the items that have children. I'm guessing the command is overriden by expanding the child menu...
How do I get the command to execute even for the menu items with children?
What I have now is
<UserControl ...>
<WrapPanel>
<Menu>
<Menu.Resources>
<Style x:Key="MenuItemStyle" TargetType="MenuItem" d:DataContext="{d:DesignInstance local:TreeItem}">
<Setter Property="Command" Value="{Binding DataContext.AddColumnCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
<Setter Property="CommandParameter" Value="{Binding}"/>
</Style>
</Menu.Resources>
<MenuItem Header="Add ▼" ItemsSource="{Binding AvailableFields}">
<MenuItem.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:TreeItem}" ItemsSource="{Binding NestedItems}" ItemContainerStyle="{StaticResource MenuItemStyle}">
<ContentPresenter Content="{Binding Annotation}"/>
</HierarchicalDataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</Menu>
</WrapPanel>
</UserControl>
I found a question with a similar name, but the situation is different and it doesn't have a good answer anyway.
All items are displayed, but the commands only fire for the leafs of the menu, not the items that have children.
Yes, this is the expected behaviour since clicking on a MenuItem with children is supposed to expand the submenu of child items. It doesn't execute a command.
If you want to expand the child items and execute the command you could handle the PreviewMouseLeftButtonDown event of the MenuItem:
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding DataContext.AddColumnCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="OnMouseDown" />
</Style>
-
private void OnMouseDown(object sender, MouseButtonEventArgs e)
{
MenuItem mi = sender as MenuItem;
if (mi != null && mi.Command != null && mi.HasItems)
mi.Command.Execute(mi.CommandParameter);
}
Note that handling the event in the code-behind of the view doesn't really break the MVVM pattern here since you are just invoking the command of the view model from the code-behind instead of invoking it from the XAML markup of the same view. But if you don't like this approach you could use an attached behaviour: https://www.codeproject.com/articles/28959/introduction-to-attached-behaviors-in-wpf

using an event to command trigger inside a style

I am trying to use the Mvvm Light toolkit to bind an event to a command from within a style.
I currently have the style:
<Style
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:controls='clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:d='http://schemas.microsoft.com/expression/blend/2008'
xmlns:mc='http://schemas.openxmlformats.org/markup-compatibility/2006'
xmlns:ig='http://schemas.infragistics.com/xaml'
xmlns:i='http://schemas.microsoft.com/expression/2010/interactivity'
xmlns:Command='clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4'
TargetType='ig:EventSpan'>
<Setter Property='Template'>
<Setter.Value>
<ControlTemplate TargetType='ig:EventSpan'>
<Grid Margin='0,2,0,2'>
<i:Interaction.Triggers>
<i:EventTrigger EventName='MouseEnter'>
<Command:EventToCommand Command='{Binding EventSpan_MouseEnter1}' CommandParameter='{Binding RelativeSource={RelativeSource AncestorType={x:Type ig:EventSpan}}}'/>
</i:EventTrigger>
<i:EventTrigger EventName='MouseLeave'>
<Command:EventToCommand Command='{Binding EventSpan_MouseLeave1}' CommandParameter='{Binding RelativeSource={RelativeSource AncestorType={x:Type ig:EventSpan}}}'/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Rectangle RadiusX='0' RadiusY='0' Fill='{TemplateBinding Fill}' Stroke='{TemplateBinding Stroke}' StrokeThickness='0' Height='0' Margin='0, 0, 0, 0' VerticalAlignment='Top' />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and an example of the code behind is:
private RelayCommand<string> _eventSpan_MouseEnter;
public RelayCommand<string> EventSpan_MouseEnter1
{
get
{
return _eventSpan_MouseEnter
?? (_eventSpan_MouseEnter = new RelayCommand<string>(
sender =>
{
MouseOverLayer = sender;
}));
}
}
But this command never fires and I cannot work out why?
PS. The reason that the libraries are referenced in the styles is because this style is loaded dynamically from code behind. It is legacy code that I am currently trying to convert to mvvm.
So There were two problems with the way I had bound before. Firstly the main reason was that the style had no datacontext. And apparently didnt inherit the datacontext from the template implementing the style. and also that the command parameter was incorrect.
CommandParameter='{Binding RelativeSource={RelativeSource AncestorType={x:Type ig:EventSpan}}}'
was binding to a relaycommand expecting a string parameter and so crashed. Therefore the correct commandparameter was
CommandParameter='{Binding Path=EventEntry.Series.Tag, RelativeSource={RelativeSource AncestorType={x:Type ig:EventSpan}}}'

Set VM property based on a mouse over event in the Xaml

I would like to set the value of a property in my VM to true once the MouseOver event on a DockPanel is raised. So far no luck with the triggers. Would you have any clue? Thanks
<DockPanel.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=MyProperty}" Value="True">
<Setter Property="??" Value="??"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DockPanel.Style>
I use the Interactivity namespace xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
to do things like this ...
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseEnter">
<i:InvokeCommandAction Command="{Binding DoSomethingCommand, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
So now you have triggered a command in the VM based on an event in the view. Obviously the datacontext at this point must be the VM so that DoSomethingCommand can be triggered on the VM, but if not then just change it bubble out to a place where the data context is the VM ...
e.g.
Command=(Binding DataContext.DoSomethingCommand , RelativeSource={RelativeSource AncestorType=sdk:DataGrid}}
or whatever kind of element has your VM context.
I think if you assign datacontext to your whole page it would be picked by style as well.
set something like a class and set property as it's value.
property = {Binding YourDataContext.YournewValue}

XAML Is there a way to bind an Event to a Method using Style?

I am using style to define the template of a predefined control. The control is part of a Framework which I cannot modify. This control has a DataGrid and for that I want to add a MouseDoubleClick event. The event handler (HandleGridDoubleClick) is already specified in the framework.
Is there a way to bind to this method ? I understand it would have been easy if the Framework provided a Command for that.
<Style
TargetType="xyz:FileMessageControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate
TargetType="xyz:FileMessageControl">
<Grid>
.....
<DataGrid MouseDoubleClick = HandleGridDoubleClick>
.....
</DataGrid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Try something like this (you need to use Interaction dll)
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<action:ExecuteCommandAction Command="{Binding DataContext.HandleGridDoubleClick}" />
</i:EventTrigger>
</i:Interaction.Triggers>

Categories