How to bind a Command to SelectedItemChanged event of a TreeView - wpf

There is a Treeview Control.
<TreeView Name="ProductsHierarchy" FontFamily="Arial"
Background="White" Margin="2"
FontSize="12" SelectedItemChanged ="ProductsHierarchy_SelectedItemChanged">
Is there a way to bind a command for SelectedItemChanged event of the treeview, avoiding the code behind event handler?

Try MVVM Toolkit's EventToCommand.
"Built-in" (from Blend) approach is to use Interactivity
<TreeView Name="ProductsHierarchy" FontFamily="Arial"
Background="White" Margin="2"
FontSize="12" SelectedItemChanged ="ProductsHierarchy_SelectedItemChanged">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<i:InvokeCommandAction Command="{Binding SelectedItemChangedCommand}" CommandParameter="argument"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TreeView>
You must include namespace:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
The disadvantage here is that you have no access to EventArgs. Here's the solution (it's in Polish, but code samples are pretty much self-explanatory).

Related

How to fire double click event in a combobox in a child user control?

I have a user control that use another user control. The child user control has a combobox and the click event works because i can open the combobox, but the double click it doesn't work.
My code is this:
Main user control:
<StackPanel>
<views:ucMyChildUserControl/>
</StackPanel>
My child user control:
<StackPanelOrientation="Horizontal">
<StackPanel Orientation="Vertical">
<Label Content="Content" Style="{StaticResource LabelDefault}"/>
<ComboBox Name="cmbMyCombobox"/>
</StackPanel>
<!--More related controls-->
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding MyCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</StackPanel>
But I have realized that if the comand of mouse double click is set in the parent user control, it works:
<views:ucChildUserControl>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding MyCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</views:ucChildUserControl>
So I guess the problem is about the handling of the event, but I don't know how to catch it in the child user control.
Thanks.
The issue is that the MouseDoubleClick event and the PreviewMouseDoubleClick are defined on Control. Since UserControl is a derivative of Control, the event is available and can be handled. However, StackPanel is not a derivative of Control so the event is not available, therefore the trigger does not work.
There are workarounds to this in code-behind that you can eventually turn into a behavior for MVVM, but simple workarounds like using input bindings on the left mouse click action only work on some elements, as others like ComboBox will already handle it and then the input bindings are not triggered.
<StackPanel Orientation="Horizontal">
<StackPanel.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick"
Command="{Binding MyCommand}"/>
</StackPanel.InputBindings>
<StackPanel Orientation="Vertical">
<Label Content="Content"/>
<ComboBox Name="cmbMyCombobox"/>
</StackPanel>
<!--More related controls-->
</StackPanel>
The most simple solution without creating additional code is to wrap the StackPanel in a control that is a derivative of Control, e.g. a ContentControl, like this.
<ContentControl>
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Vertical">
<Label Content="Content"/>
<ComboBox Name="cmbMyCombobox"/>
</StackPanel>
<!--More related controls-->
</StackPanel>
<b:Interaction.Triggers>
<b:EventTrigger EventName="MouseDoubleClick">
<b:InvokeCommandAction Command="{Binding MyCommand}" />
</b:EventTrigger>
</b:Interaction.Triggers>
</ContentControl>

WPF (mvvm, caliburn.micro) button click inside a listview item

I have a simple listview and listviewitem structure.
The Heart of the ListView.xaml is like the following
<StackPanel>
<ListBox x:Name="Movies" SelectedItem="{Binding SelectedMovie}">
<ListBox.ItemTemplate>
<DataTemplate>
<v:ListItemView/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
and the heart of the ListTtemView.xaml is the following:
<StackPanel>
<TextBlock Text="{Binding Id}"/>
<TextBlock Text="{Binding Title}"/>
<Button cal:Message.Attach="RunOperation" cal:Action.TargetWithoutContext="{Binding ElementName=UserControl, Path=DataContext}" Content="Run"/>
</StackPanel>
in the ListViewItemViewModel, I have a method called RunOperation and the scenario is when the user clicks the button in a listviewitem, the method RunOperation should be called. However, I get an exception: 'No target found for method RunOperation.'
I have read that the caliburn micro doesn't work in a case like this and if this is the case, I realised that I still don't know how to make it work the simple WPF way.
Apparently, RunOperation method cannot be found so I tried few combinations of cal:Action.TargetWithoutContext="{Binding ...}" but no help.
Thanks
If someone is still looking for a solution, here is what I did, With reference to caliburn.micro and interactivity in the xaml like below,
xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro.Platform"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
<Button Content="Run">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="RunOperation">
<cal:Parameter Value="{Binding}" />
</cal:ActionMessage>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
The view model button click method will be
public void RunOperation(object obj)
{
//Do your things
}
Since your xaml involving button is not available in question, am not sure if you have already tried this, but following should help you hit the function on Button's click event.
<Button cal:Message.Attach="[Event Click] = [Action RunOperation]"/>
I don't know anything about caliburn.micro.
But the normal WPF approach would be this
Step 1:
Create a Command in your ViewModel
class ViewModel
{
...
public ICommand RunOperationCommand { get; }
...
}
Step 2: Bind to it
<Button Command="{Binding RunOperationCommand }" />
That's not the caliburn.micro solution. And I don't know if there is a valid caliburn.micro solution for situations like this.
But the following works:
<Button Command="{Binding DataContext.RunOperationCommand, RelativeSource={RelativeSource AncestorType=ListBox}}" />
I was having the same issue, plus I was trying to get around it using a normal WPF approach.
I had Buttons being created dynamically inside a List Control which a source of was Bindable Collection of Sensors (my class).
You need to make sure you add a reference to the "xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" and
"xmlns:cal="http://www.caliburnproject.org" on your UserControl or Window.
Can find all the information and examples on Caliburn Micro Docs:
CaliburnActions Docs
<UserControl x:Class="BeckerGasApp.Views.MapView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:BeckerGasApp.Views"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cal="http://www.caliburnproject.org">
<Grid>
<Grid>
<ItemsControl x:Name="Sensors" Background="Transparent" ItemsSource="{Binding Path=Sensors, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
<ItemsControl.ItemTemplate>
<DataTemplate >
<Button Background="Transparent" Content="{Binding SensorName}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="Sensor">
<cal:Parameter Value="{Binding SensorName}" />
</cal:ActionMessage>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Viewbox>
</Grid>
</UserControl>
To do it inside a listview I had the following extra tag cal:Action.Target to be able to access ÀctionMessage for an item in listview.
<ListView
x:Name="LV"
Background="Transparent"
BorderBrush="Transparent"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListViewItem>
<Button x:Name="StartPage">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:Action.Target>
<cal:ActionMessage MethodName="StartPage">
<cal:Parameter Value="{Binding}" />
</cal:ActionMessage>
</cal:Action.Target>
</i:EventTrigger>
</i:Interaction.Triggers>
<Button.Template>
<ControlTemplate>
<StackPanel Width="210" Orientation="Horizontal">
<Image Source="../Assets/img_home.png" Stretch="None" />
<TextBlock Style="{StaticResource font_style}" Text="Home" />
</StackPanel>
</ControlTemplate>
</Button.Template>
</Button>
</ListViewItem>

Binding a TabItem in WPF

In WPF, a button can be bound to a command.
<Button Command="{Binding DoSomething}">Click me!</Button>
Now I want to do the same to a TabItem:
<TabItem Header="A little tab" ???="{Binding DoSomething}">...</TabItem>
What should ??? be? Or is there another way to do it?
It depends on what do you want to achieve. TabItems have the IsSelected property
IsSelected="{Binding IsSelected}"
that can be bounded TwoWay, and can be used to signal stuff to the ViewModel.
You could also use the fact that you can override the header of the TabItem, and bound it to a command, using Interactivity.
<TabItem TabIndex="0"
Tag="{Binding CurrentPrinterStatus}">
<TabItem.Header>
<Grid Background="Transparent">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown">
<i:InvokeCommandAction Command="{Binding DoSomething}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBlock Style="{StaticResource TextBlockSelectedStyle}"
Text="Printers"/>
</Grid>
</TabItem.Header>
Other solutions are to use the SelectionChanged event of the TabControl, and that could allow you to find the ViewModel of the TabItem currently selected.
Hope this ideas atleast help you get a solution to your problem.

WPF Bingmaps pushpin event binding mvvm

I would like to trigger a command in my ViewModel when the pushpin is clicked on the map. How can I achieve this using databinding?
Here is the DataTemplate I am using for the pinpoints:
<DataTemplate x:Key="PushPinTemplate">
<map:Pushpin Cursor="Hand"
map:MapLayer.Position="{Binding Location}">
</map:Pushpin>
</DataTemplate>
A colleague found the solution:
After adding the Nuget Package System.Windows.Interactivity.WPF
and adding the xml namespace
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
you can add an EventTrigger to the template, here the full template code, where
CachePushPinClicked is an ICommand
<DataTemplate x:Key="PushPinTemplate">
<map:Pushpin Cursor="Hand"
map:MapLayer.Position="{Binding Location}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding CachePushPinClicked}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</map:Pushpin>
</DataTemplate>

Routed Events of TextBlock

How to fire RoutedEvents of TextBlock in ViewModel from code behind in wpf.
Kindly let me know, how I can bind routed events in wpf code behind. Thanks
Well I use this (you will need System.Windows.Interactivity in Silverlight, but I suspect it's similar in WPF):
xmlns:GalaSoft_MvvmLight_Command="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.SL4"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
<TextBlock Text="{Binding InputValue, Mode=TwoWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="GotFocus">
<GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding InputValueGotFocusCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBlock>
You can do this easy in Blend

Resources