System.Windows.Interactivity allows commands to be invoked when a specific event is triggered, without having to write code behind. However, I couldn't find out how to invoke a command when the middle mouse button (scroll wheel) is clicked on an element.
<StackPanel>
<i:Interaction.Triggers>
<i:EventTrigger EventName="...">
<i:InvokeCommandAction Command="{Binding CloseCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
...
</StackPanel>
Since Ash pointed out that clicking the wheel button was a thing, it prompted me to look into how that would work.
You can just use a mousebinding.
<StackPanel.InputBindings>
<MouseBinding Gesture="WheelClick" Command="{Binding WheelClickCommand}" />
</StackPanel.InputBindings>
As Andy's answer showed, you can use mouse binding. However the WheelClick gesture indicates a scroll action, use MiddleClick instead.
<StackPanel>
<StackPanel.InputBindings>
<MouseBinding Gesture="MiddleClick" Command="{Binding CloseCommand}" />
</StackPanel.InputBindings>
...
</StackPanel>
You could create a custom EventTrigger that handles this for you:
public class MouseWheelButtonEventTrigger : System.Windows.Interactivity.EventTrigger
{
public MouseWheelButtonEventTrigger()
{
EventName = "MouseDown";
}
protected override void OnEvent(EventArgs eventArgs)
{
MouseButtonEventArgs mbea = eventArgs as MouseButtonEventArgs;
if (mbea != null && mbea.ChangedButton == MouseButton.Middle)
base.OnEvent(eventArgs);
}
}
Sample usage:
<StackPanel Background="Yellow" Width="100" Height="100">
<i:Interaction.Triggers>
<local:MouseWheelButtonEventTrigger>
<i:InvokeCommandAction Command="{Binding CloseCommand}" />
</local:MouseWheelButtonEventTrigger>
</i:Interaction.Triggers>
</StackPanel>
The built in one doesn't as there is no specific event raised for the mouse wheel button.
Related
I can close a window using Interaction Triggers like
<Button Content="X" Height="20" Width="20">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:CallMethodAction MethodName="Close"
TargetObject="{Binding RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType=Window}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
But when I replace MethodName="Close" with MethodName="Maximize" or MethodName="Minimize" to maximize or minimize window it doesn't work.
How do I do this using .Net 4.5 without breaking mvvm pattern ?
MethodName="Close" works because there is Close() method in Window type, but there is no Maximize() or Minimize() methods out there, these are states, there is WindowState property that you can set to Minimize or Maximize..
You can change it explicitly when the button is clicked
void Button_OnClick(object sender, RoutedEventArgs e)
{
this.WindowState = Maximize;
}
Or you can define your own window that has Minimize() and Maximize() methods..
public class MyWindow : Window {
public void Maximize(){
this.WindowState = Maximize;
}
public void Minimize(){
this.WindowState = Minimize;
}
}
And in .xaml
<views:MyWindow [views namespace here]..
<!-- -->
<Button Content="X" Height="20" Width="20">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:CallMethodAction MethodName="Maximize"
TargetObject="{Binding RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType=MyWindow}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<!-- -->
</Window>
I am currently working with WPF to create a simple minigame which requires pressing keys. I have done something similar with mouse clicking, yet I am struggling with keys. I have searched for a quite some time and I have found out that the most common way to work with keys is to define each key to its own event. But thats not my case and I want it to be able to fire it everytime any key is pressed. I have found out that this is possible to be done with MVVMLight and EventToCommand, but for some uknown reason to me, the KeyDown event will not fire (neighter KeyUp), but PreviewMouseLeftButtonDown will do.
xaml file:
<i:Interaction.Triggers>
// will not fire
<i:EventTrigger EventName="KeyDown">
<cmd:EventToCommand Command="{Binding onKeyDown, Mode=OneWay}" PassEventArgsToCommand="True" />
</i:EventTrigger>
// will not fire
<i:EventTrigger EventName="PreviewKeyDown">
<cmd:EventToCommand Command="{Binding onKeyDown, Mode=OneWay}" PassEventArgsToCommand="True" />
</i:EventTrigger>
// will fire
<i:EventTrigger EventName="PreviewMouseLeftButtonDown">
<cmd:EventToCommand Command="{Binding onKeyDown, Mode=OneWay}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
ViewModel:
public DelegateCommand onKeyDown
{
get
{
MessageBox.Show("Down");
return new DelegateCommand(() => MessageBox.Show("Down"));
}
}
(full xaml file)
<UserControl x:Class=".......AsteroidsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Platform"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
Background="{DynamicResource ThemeBackgroundColor}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyDown">
<cmd:EventToCommand Command="{Binding onKeyDown, Mode=OneWay}" PassEventArgsToCommand="True" />
</i:EventTrigger>
<i:EventTrigger EventName="PreviewKeyDown">
<cmd:EventToCommand Command="{Binding onKeyDown, Mode=OneWay}" PassEventArgsToCommand="True" />
</i:EventTrigger>
<i:EventTrigger EventName="PreviewMouseLeftButtonDown">
<cmd:EventToCommand Command="{Binding onKeyDown, Mode=OneWay}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source=".......Module.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="100" />
<RowDefinition Height="200*" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="100*" />
<ColumnDefinition Width="100*" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0"
Style="{StaticResource ReturnBackButtonStyle}"
Command="{Binding ReturnFromSearchingCommand, Mode=OneWay}" />
<TextBlock Grid.Row="1" Grid.Column="1"
Text="Game"
FontSize="48"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
<TextBlock Grid.Row="1" Grid.Column="2"
Text="Controls"
FontSize="48"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
<Canvas Grid.Row="2" Grid.Column="1"
Background="LightBlue"
Name="MyCanvas"
Focusable="True">
<Rectangle Name="player" Height="50" Width="60" Fill="Yellow" Canvas.Left="222" Canvas.Top="495"/>
<Label Name="scoreText" Content="Score: 0" FontSize="18" FontWeight="Bold" Foreground="White"/>
<Label Name="damageText" Content="Damage: 0" FontSize="18" FontWeight="Bold" Canvas.Right="0" Foreground="White"/>
</Canvas>
</Grid>
</UserControl>
Thanks in advance!
EDIT + UPDATE 1:
I have tried to force it and I have noticed something strange.
<UserControl.InputBindings>
<KeyBinding Key="Delete"
Command="{Binding onKeyDown, Mode=OneWay}" />
</UserControl.InputBindings>
Using the code above, I am still UNABLE to fire the onKeyDown event. I have no idea why, but I think its something a way way deeper in the code
There is a PreviewKeyDown event that you can try. The most common reason why it won't work with KeyDown and MouseDown is that the control already handles key presses and mouse interactions internally.
The PreviewKeyDown event will only be fired when the control is focused so you'll also have to set the Focusable property of the UserControl to true.
A better way to make sure that you always capture keypresses in a UserControl would be to handle the keypress event of the parent window programmatically:
public partial class AsteroidsView : UserControl
{
public AsteroidsView()
{
InitializeComponent();
Loaded += AsteroidsView_Loaded;
}
private void AsteroidsView_Loaded(object sender, RoutedEventArgs e)
{
Window parentWindow = Window.GetWindow(this);
parentWindow.PreviewKeyDown += ParentWindow_PreviewKeyDown;
}
private void ParentWindow_PreviewKeyDown(object sender, KeyEventArgs e)
{
//TODO: handle...
}
}
Just in case someone needs the "handle" part, I have noticed that the core problem I had was that I had a different window focused. So, what I have done is that I have focused the keyboard to the Canvas that I needed and those events work like a charm. Here is a code:
public partial class AsteroidsView : UserControl
{
public AsteroidsView()
{
InitializeComponent();
Loaded += AsteroidsView_Loaded;
}
private void AsteroidsView_Loaded(object sender, RoutedEventArgs e)
{
Window parentWindow = Window.GetWindow(this);
parentWindow.PreviewKeyDown += ParentWindow_PreviewKeyDown;
}
private void ParentWindow_PreviewKeyDown(object sender, KeyEventArgs e)
{
Keyboard.Focus(this.MyCanvas); // `this` keyword is redundant
}
}
So, mm8 gave a correct solution, there was just this small piece of a missing puzzle for me. The missing piece of this puzzle was found here -> https://social.msdn.microsoft.com/Forums/vstudio/en-US/20d7dc78-53a0-494a-a3cc-b463a23b8196/keydown-does-not-get-fired-up?forum=wpf where I noticed that you need to have the element you want to use focused and visible
I've an EventTrigger:
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding Modify}"
CommandParameter="{Binding SelectedItem, ElementName=lv}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
and it invokes the command on MouseDoubleClick, doesn't matter whether it's Left or Right! I want it to Invoke the command only on Left MouseDoubleClick. I've tried by chaning the EventName as LeftMouseDoubleClick and LeftDoubleClick but those don't work! Under the Event Section of Properties Inspector It looks like there're two MouseDoubleClick and PreviewMouseDoubleClick BUT nothing for LeftMouseDoubleClick or RighMouseDoubleClick!
Is there any such Event? If so, what's the name?
If there's none, how such case is handled?
You can use InputBindings to handle this. There are specific events for left double click and right double click.
<Window.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick"
Command="{Binding MessageCommand}"
CommandParameter="Left Double Click"/>
<MouseBinding MouseAction="MiddleDoubleClick"
Command="{Binding MessageCommand}"
CommandParameter="Middle Double Click"/>
<MouseBinding MouseAction="RightDoubleClick"
Command="{Binding MessageCommand}"
CommandParameter="Right Double Click"/>
<MouseBinding MouseAction="WheelClick"
Command="{Binding MessageCommand}"
CommandParameter="Wheel Click"/>
</Window.InputBindings>
This is discussed in detail in this article
You can handle it in the event handler.
void OnMouseDoubleClick(Object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
// Left button double-click
}
}
I tried with
<RadioButton Content="Boom" Command={Binding MyCommand} IsEnabled="{Binding IsChecked, Converter={StaticResource InverseBooleanConverter}, RelativeSource={RelativeSource Mode=Self}}"/>
but nothing happens. Why is that and how to fix it?
Step 1: Add System.Windows.Interactivity reference
Step 2: Add Namespace in XAML xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Step 3:
<RadioButton Content="Boom" IsEnabled="{Binding IsChecked, Converter={StaticResource InverseBooleanConverter}, RelativeSource={RelativeSource Mode=Self}}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction Command="{Binding MyCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</RadioButton>
The following code works as expected:
<RadioButton Content="Boom" Command="{Binding MyCommand}" />
That is, as in case of a regular Button, MyCommand is fired every time you click a RadioButton. If you're using RadioButtons, this, understandably, may not be what you want.
More useful will be passing some sort of data as a CommandParameter to know which option was checked:
<RadioButton Content="AAA" Command="{Binding MyCommand}" CommandParameter="AAA" GroupName="MyGroup"/>
<RadioButton Content="BBB" Command="{Binding MyCommand}" CommandParameter="BBB" GroupName="MyGroup"/>
Example command method:
private ICommand _MyCommand;
public ICommand MyCommand
{
get { return _MyCommand ?? (_MyCommand = new DelegateCommand(a => MyCommandMethod(a))); }
}
private void MyCommandMethod(object item)
{
Console.WriteLine("Chosen element: " + (string)item);
}
In one of my views, I have 2 event triggers on a TabControl:
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<cmd:EventToCommand Command="{Binding TestCommand}"/>
</i:EventTrigger>
<i:EventTrigger EventName="TabClosed">
<cmd:EventToCommand Command="{Binding CloseCurrentWorkspaceCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Here is the code for the command bindings:
RelayCommand _t;
public RelayCommand TestCommand { get { return _t ?? (_t = new RelayCommand(foo)); } }
void foo()
{
// This is just to see if the event is firing.
System.Windows.MessageBox.Show("Fired");
}
RelayCommand _closeWorkspaceCommand;
public RelayCommand CloseCurrentWorkspaceCommand
{
get { return _closeWorkspaceCommand ?? (_closeWorkspaceCommand = new RelayCommand(CloseWorkspace)); }
}
void CloseWorkspace()
{
// Workspaces is a ObservableCollection<WorkspaceViewModel>
Workspaces.Remove(SelectedItem);
}
The SelectedItemChanged event never fires when the selected item (or index) is changed. Also changing the event to SelectedIndexChanged has no effect. The TabClosed event fires without a problem. The SelectedIndex and SelectedItem bindings are updating accordingly.
I've checked the output Window and I don't see any binding errors or anything that might suggest the binding is wrong.
How can I start to diagnose why this event won't fire?
Full code of control:
<wpf:TabControlExt Grid.Row="1"
ItemsSource="{Binding Workspaces}"
SelectedItem="{Binding SelectedItem}"
SelectedIndex="{Binding SelectedIndex}">
<wpf:TabControlExt.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header, Mode=TwoWay}"/>
</DataTemplate>
</wpf:TabControlExt.ItemTemplate>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<cmd:EventToCommand Command="{Binding TestCommand}"/>
</i:EventTrigger>
<i:EventTrigger EventName="TabClosed">
<cmd:EventToCommand Command="{Binding CloseCurrentWorkspaceCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</wpf:TabControlExt>
Fixed it.
It turns out I was binding to the wrong event. I should have been binding to SelectionChanged. I only found this out when going through the Triggers pane in Blend. It didn't give me the option to pick SelectedIndexChanged or SelectedItemChanged.
Although in hindsight, SelectionChanged makes more sense since when the item changes, the index changes, and you can't rearrange tabs in the control as far as I know.
So the correct code is:
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand Command="{Binding TestCommand}"/>
</i:EventTrigger>