I have a bunch of controls using identical interactions triggers across multiple user controls and viewmodels. Is it possible to place these triggers somehow into a resource dictionary for reuse? Here's an example of what a control might look like.
<TextBox x:Name="FirstName" Grid.Row="1" Grid.Column="1">
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyDown">
<cal:ActionMessage MethodName="KeyPressed" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<TextBox x:Name="Initial" Grid.Row="1" Grid.Column="1">
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyDown">
<cal:ActionMessage MethodName="KeyPressed" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<TextBox x:Name="LastName" Grid.Row="1" Grid.Column="1">
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyDown">
<cal:ActionMessage MethodName="KeyPressed" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
The cal: namespace is from the Caliburn.Micro MVVM framework and probably isn't relevant to this question.
Its not possible to re-use a single instance of Interaction.Triggers in a resource because it becomes attached a control. That attachment becomes part of its state hence a single instance can't be shared by multiple controls.
You would need to include the Interaction.Triggers in a template so that multiple instances are created. I guess something like the following might work, (warning air code).
<UserControl.Resources>
<DataTemplate x:key="MyTextBox">
<TextBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyDown">
<cal:ActionMessage MethodName="KeyPressed" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
</DataTemplate>
</UserControl.Resources>
...
<ContentPresenter x:Name="FirstName" Grid.Row="1" Grid.Column="1" ContentTemplate="{StaticResource MyTextBox}" />
<ContentPresenter x:Name="Initial" Grid.Row="1" Grid.Column="1" ContentTemplate="{StaticResource MyTextBox}" />
<ContentPresenter x:Name="LastName" Grid.Row="1" Grid.Column="1" ContentTemplate="{StaticResource MyTextBox}" />
It my opinion that this sort of stuff isn't worth it. The Interaction Triggers stuff is really aimed at empowering the designer rather than the developer. A designer isn't that worried that there is some repeatition in the "code".
i have something useful in case you don't have solve completely your issue about reusing Interaction Triggers...
<TextBox x:Name="FirstName" KeyDown="_KeyDown">
...
private void TextBlock_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
Here goes something you wanna reuse for all your TextBox controls..
}
Maybe the better option is to use a design pattern like MVVM and EventToCommand Interactions by GalaSoft
Related
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>
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>
I have the following XAML snippet:
<TextBox x:Name="FilterTB" TextChanged="FilterTB_TextChanged"/>
<Button x:Name="CancelFilterSelectionButton" FontWeight="Bold" Content="X"/>
I'd like to erase the content of the TextBox when the user presses the Button.
Of course doing so from Code Behind is a trivial task, but I wanted to accomplish it only through the use of XAML, and so using Triggers.
I tried researching on the net, but I either found uncorrect solutions, or overly-convoluted solutions, so I'd like to hear some clean and compact solutions.
Here I had a spare minute, hope this helps, cheers.
namespaces;
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ei="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
and easy peasy.
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<TextBox x:Name="ThatThangToClear" Width="250"/>
<Button x:Name="ClearThatThang" Content="Clear That Thang" Margin="5,0">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction
TargetName="ThatThangToClear"
TargetObject="{Binding ElementName=ThatThangToClear}"
PropertyName="Text" Value="{x:Null}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</StackPanel>
Oh, and P.S. - You really only need either TargetName OR TargetObject but I included both for examples sake.
I'm using the DoubleUpDown UserControl from the Extended WPF Toolkit in my current project. Now I have to bind a RelayCommand to the DoubleClick event of that DoubleUpDown, but it is not working. I've assigned that DoubleClick to all kinds of different UserControls so far and usually it was working fine or sometimes I had to wrap it into an empty UserControl-Element which would then hold the CommandBinding, but so far I always got it working.
This is what I tried so far:
<UserControl Grid.Row="7"
Grid.Column="0">
<xctk:DoubleUpDown Value="{Binding EditableDevice.SelectedLoadReceptor.DecimalPlaces,
UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay, FallbackValue='1'}"
FormatString="F0"
Minimum="0"
Maximum="10">
<xctk:DoubleUpDown.InputBindings>
<MouseBinding Command="{Binding EditDeviceCommand}"
Gesture="LeftDoubleClick" />
</xctk:DoubleUpDown.InputBindings>
<i:Interaction.Triggers>
<i:EventTrigger EventName="LeftDoubleClick">
<cmd:EventToCommand Command="{Binding EditDeviceCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</xctk:DoubleUpDown>
<UserControl.InputBindings>
<MouseBinding Command="{Binding EditDeviceCommand}"
Gesture="LeftDoubleClick" />
</UserControl.InputBindings>
</UserControl>
I'm usually fine with using the InputBindings, but not with the DoubleUpDown...
I wonder what is causing the issue. Does anyone here have an idea or a workaround?
Regards
Ralf
Hmm, still couldn't figure out what is wrong here. In the meantime I'll just use CodeBehind where ever possible by using the regular MouseDoubleClick-Event.
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).