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

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}

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}

Two way binding of IsChecked within a nested MenuItem

I have a nested context menu like this:
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Thing Count" ItemsSource="{Binding ThingsProvider}">
NB the Thing Count parent MenuItem with children from ThingsProvider.
ThingsProvider provides a List of ThingViewModel which contains properties Thing and IsChecked. I want to be able to control IsChecked from my main view model and from the user via the MenuItem but I have hit a problem; If I use an ItemContainerStyle like this.
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Path=Thing, Mode=OneWay}"/>
<Setter Property="IsChecked" Value="{Binding Path=IsChecked, UpdateSourceTrigger=PropertyChanged}"/>
</Style>
</MenuItem.ItemContainerStyle>
then (predictably I suppose as it's a style) it will observe the ViewModel's IsChecked but will not set it.
If I use an ItemTemplate like so
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Thing, Mode=OneWay}"/>
</DataTemplate>
</MenuItem.ItemTemplate>
I can't get to the MenuItem's IsChecked property as I've only got the TextBlock. If I put a MenuItem in the ItemTemplate then I end up with nested MenuItems in my UI and it looks bad.
There must be a way to do this but I'm stumped at the moment, can anyone help?
Cheers
Rich

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}}}'

wpf eventsetter handler binding in style

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.

Trouble binding WPF Menu to ItemsSource

I would like to avoid having to build a menu manually in XAML or code, by binding to a list of ICommand-derived objects. However, I'm experiencing a bit of a problem where the resulting menu has two levels of menu-items (i.e. each MenuItem is contained in a MenuItem):
My guess is that this is happening because WPF is automatically generating a MenuItem for my binding, but the "viewer" I'm using actually already is a MenuItem (it's derived from MenuItem):
<ContextMenu
x:Name="selectionContextMenu"
ItemsSource="{Binding Source={x:Static OrangeNote:Note.MultiCommands}}"
ItemContainerStyleSelector="{StaticResource separatorStyleSelector}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<Viewers:NoteCommandMenuItemViewer
CommandParameter="{Binding Source={x:Static OrangeNote:App.Screen}, Path=SelectedNotes}" />
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
(The ItemContainerStyleSelector is from http://bea.stollnitz.com/blog/?p=23, which allows me to have Separator elements inside my bound source.)
So, the menu is bound to a collection of ICommands, and each item's CommandParameter is set to the same global target (which happens to be a collection, but that's not important).
My question is, is there any way I can bind this such that WPF doesn't automatically wrap each item in a MenuItem?
Unfortunately, the best way I've found to work around this issue is to use a style for the MenuItems, rather than an ItemTemplate. Then each property in the style can be bound to properties on your object. Something like this, for example:
<Style x:Key="SelectionContextMenuStyle" TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Path=Text}" />
<Setter Property="Command" Value="{Binding Path=Command}" />
<Setter Property="CommandParameter" Value="{Binding Path=Parameter}" />
</Style>
It really seems like an ItemTemplate should work, and it would be the better way to go, but this is the only way I've found that actually works properly.
I would be inclined to subclass ContextMenu and override GetContainerForItemOverride:
public class ContextMenuWithNoteCommands : ContextMenu
{
protected virtual DependencyObject GetContainerForItemOverride()
{
return new NoteCommandMenuItemViewer();
}
}
Then set the CommandParameter binding in the NoteCommandMenuItemViewer style, or in ContextMenu.ItemContainerStyle, whichever is more appropriate.
This presumes you can't simply use ItemContainerStyle on a regular MenuItem to get the effect you want:
<ContextMenu ...>
<ContextMenu.ItemContainerStyle>
<Style>
...
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>

Categories