Catch custom event by Interactivity.EventTrigger - c#

I have a simple user-control with an event:
using System.Windows;
namespace TriggersTest
{
public partial class MyControl
{
public MyControl()
{
InitializeComponent();
}
public static readonly RoutedEvent ButtonPressedEvent =
EventManager.RegisterRoutedEvent("ButtonPressed", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyControl));
public event RoutedEventHandler ButtonPressed
{
add { AddHandler(ButtonPressedEvent, value); }
remove { RemoveHandler(ButtonPressedEvent, value); }
}
private void OnButtonClick(object sender, RoutedEventArgs e)
{
RaiseEvent(new RoutedEventArgs { RoutedEvent = ButtonPressedEvent });
}
}
}
And I want to catch this event in the other view:
<Window x:Class="TriggersTest.MainWindow"
Name="Root"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:TriggersTest"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:MyControl>
<i:Interaction.Triggers>
<i:EventTrigger EventName="ButtonPressed">
<i:InvokeCommandAction Command="{Binding MyCommand, ElementName=Root}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</local:MyControl>
</Grid>
</Window>
The event has been raised, but i:EventTrigger doesn't invoke a command.
How can I fix this?

Its all fine with your code. The binding is not working. I can tell just by looking at this.
Do not use ElementName inside InvokeCommandAction because InvokeCommandAction is not part of LogicalTree in WPF.
Just change your Binding or use x:Reference and everything should be fine.

Related

WPF MVVM Key Binding

I'm new to WPF, and the code I've written doesn't seem to be working. I'm trying to move a rectangular paddle to the left when the left arrow key is pressed, but I get no response when I press the Left arrow key. What am I doing wrong here?
Here is the View:
<UserControl.InputBindings>
<KeyBinding Key="Left" Command="{Binding MovePaddleLeftCommand}"/>
</UserControl.InputBindings>
and here is the code in the ViewModel:
private ICommand _movePaddleLeftCommand;
public ICommand MovePaddleLeftCommand
{
get
{
return new DelegateCommand(ExecuteCommand, CanExecute);
}
}
private void ExecuteCommand()
{
MessageBox.Show("Left");
MovePaddleLeft();
}
private bool CanExecute()
{
return true;
}
Below is a more complete picture of the code in case it's relevant.
GameView.xaml:
<UserControl x:Class="Arkanoid_MkII.Views.GameView"
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:Arkanoid_MkII.Views"
mc:Ignorable="d"
Width="1500" Height="800">
<Canvas Name="GameArea" ClipToBounds="True">
<Rectangle Canvas.Left="{Binding Path=Paddle.PaddleX}" Canvas.Top="750" Width="{Binding Path=Paddle.Width}" Height="{Binding Path=Paddle.Height}" Fill="{Binding Path=Paddle.Colour}"/>
</Canvas>
<UserControl.InputBindings>
<KeyBinding Key="Left" Command="{Binding MovePaddleLeftCommand}"/>
</UserControl.InputBindings>
</UserControl>
GameView.xaml.cs:
public partial class GameView : UserControl
{
public GameView()
{
InitializeComponent();
}
}
MainWindow.xaml:
<Window x:Class="Arkanoid_MkII.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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:local="clr-namespace:Arkanoid_MkII"
xmlns:views="clr-namespace:Arkanoid_MkII.Views"
mc:Ignorable="d"
Title="MainWindow" SizeToContent="WidthAndHeight" ResizeMode="NoResize" Background="Black" Padding="100">
<Grid>
<Border BorderBrush="Black" BorderThickness="5">
<views:GameView x:Name="GameViewControl" Loaded="GameViewControl_Loaded" />
</Border>
</Grid>
</Window>
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void GameViewControl_Loaded(object sender, RoutedEventArgs e)
{
Arkanoid_MkII.ViewModels.GameViewModel gameViewModelObject = new Arkanoid_MkII.ViewModels.GameViewModel();
GameViewControl.DataContext = gameViewModelObject;
}
}
GameViewModel.cs:
private ICommand _movePaddleLeftCommand;
public ICommand MovePaddleLeftCommand
{
get
{
return new DelegateCommand(ExecuteCommand, CanExecute);
}
}
private void ExecuteCommand()
{
MessageBox.Show("Left");
MovePaddleLeft();
}
private bool CanExecute()
{
return true;
}
The UserControl must be focusable and focused for the command to be executed:
private void GameViewControl_Loaded(object sender, RoutedEventArgs e)
{
Arkanoid_MkII.ViewModels.GameViewModel gameViewModelObject =
new Arkanoid_MkII.ViewModels.GameViewModel();
GameViewControl.DataContext = gameViewModelObject;
GameViewControl.Focusable = true;
Keyboard.Focus(GameViewControl);
}

c# mouse left click on listbox

How can I execute an function when mouse left click on a Item in listbox ?
I Can't use SelectChanged because I listen also right click, and when I right click on the item it's execute the fuction SelectChanged also.
Or how to detect in SelectChange method, if event it's right click or left
listBoxG.AddHandler(UIElement.MouseLeftButtonUpEvent, new RoutedEventHandler(OnMouseLeftButtonUp_listBoxG), true);
public void OnMouseLeftButtonUp_listBoxG(Object sender, RoutedEventArgs e)
{
// something
}
WPF:
XAML: install System.Windows.Interactivity.WPF nuget library
<Window x:Class="WpfApp1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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:local="clr-namespace:WpfApp1"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:test="clr-namespace:Test"
mc:Ignorable="d"
Title="Window1">
<Window.DataContext>
<test:MainViewModel/>
</Window.DataContext>
<Grid>
<ListBox ItemsSource="{Binding ListBoxItems}" HorizontalAlignment="Left" Width="216" Height="235" Margin="10,10,0,0" VerticalAlignment="Top">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonUp" >
<i:InvokeCommandAction Command="{Binding ListBoxLeftClickCommand}" CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource FindAncestor, AncestorType=ListBox}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
</Grid>
</Window>
ViewModel.cs using the MVVMLight library for RelayCommand and ViewModelBase
using System;
using System.Collections.ObjectModel;
using System.Windows.Input;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;
namespace Test
{
public class MainViewModel : ViewModelBase
{
public class TestModel
{
public string Value { get; set; }
public TestModel(string value)
{
this.Value = value;
}
}
public ObservableCollection<string> ListBoxItems { get; set; }
public ICommand ListBoxLeftClickCommand { get; }
public MainViewModel()
{
ListBoxLeftClickCommand = new RelayCommand<object>(DoSomething, selectedItem => true);
ListBoxItems = new ObservableCollection<string>() { "Test1", "Test2" };
}
private void DoSomething(object selectedItem)
{
throw new NotImplementedException();
}
}
}
WinForms:
taken from Right Click to select items in a ListBox
this.ListBox.MouseUp += new System.Windows.Forms.MouseEventHandler(this.List_LeftClick);
private void List_LeftClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
int index = this.listBox.IndexFromPoint(e.Location);
if (index != ListBox.NoMatches)
{
// Do something
}
}
}

WPF UserControl, with Buttons and KeyBindings

I'm trying to implement a dialer in WPF. I have a window, and inside it a user control. The user control has lots of buttons, but the user can also use the num pad to enter numbers.
I created a small sample project to show where I'm at:
MainWindow.xaml
<Window x:Class="wpf_dialer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:wpf_dialer"
Title="MainWindow" Height="200" Width="525">
<Window.Resources>
<local:DialerViewModel x:Key="myViewModel" />
</Window.Resources>
<Grid DataContext="{StaticResource myViewModel}">
<local:Dialer />
</Grid>
</Window>
Dialer.xaml
<UserControl x:Class="wpf_dialer.Dialer"
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:wpf_dialer"
mc:Ignorable="d"
d:DesignHeight="200" d:DesignWidth="300"
d:DataContext="{d:DesignInstance local:DialerViewModel}"
Loaded="UserControl_Loaded">
<UserControl.InputBindings>
<KeyBinding Key="A" Command="{Binding CommandDialValue, Mode=OneTime}" CommandParameter="." />
<KeyBinding Key="Back" Command="{Binding CommandDialValue, Mode=OneTime}" CommandParameter="Back" />
<KeyBinding Key="Enter" Command="{Binding CommandAcceptValue, Mode=OneTime}" CommandParameter="KeyBinding" />
</UserControl.InputBindings>
<UniformGrid Columns="1">
<TextBox IsEnabled="False" Text="{Binding DialedValue, Mode=OneWay}" MinWidth="200" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10"/>
<Button Content="OK" Margin="60,30" Command="{Binding CommandAcceptValue, Mode=OneTime}" CommandParameter="Button" />
</UniformGrid>
</UserControl>
DialerViewModel.cs
using System;
using System.ComponentModel;
using System.Windows.Input;
namespace wpf_dialer
{
class DialerViewModel : INotifyPropertyChanged
{
private Random RAND = new Random();
private string _dialed_value = "00";
public string DialedValue
{
get { return _dialed_value; }
set
{
if (_dialed_value == value) return;
_dialed_value = value;
RaisePropertyChanged("DialedValue");
}
}
public ICommand CommandDialValue { get { return new CommandImpl(DialValue); } }
public ICommand CommandAcceptValue { get { return new CommandImpl(Alert); } }
private void DialValue(object parameter)
{
if (parameter.ToString() == "Back")
{
if (DialedValue.Length > 0)
{
DialedValue = DialedValue.Substring(0, DialedValue.Length - 1);
}
}
else
{
DialedValue += RAND.Next(0, 10).ToString();
}
}
private void Alert(object parameter)
{
System.Windows.MessageBox.Show(parameter.ToString());
}
#region INotifyPropertyChanged
protected void RaisePropertyChanged(string property_name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property_name));
}
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
private class CommandImpl : ICommand
{
private readonly Action<object> _action = null;
public CommandImpl(Action<object> action)
{
_action = action;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter) { return true; }
public void Execute(object parameter) { _action(parameter); }
}
}
}
Objectives:
As soon as the window is loaded, when the user presses the A key, the CommandDialValue is executed;
When the user presses Enter, a message box is displayed with the text "KeyBinding". The CommandAcceptValue must be called from the KeyBinding, and NOT from the button;
Problems:
When the window is loaded, the KeyBindings don't execute. They are executed when I click a button somewhere in the UserControl;
When I press Enter, the button's command is executed, but I want the user control's KeyBinding to be executed;
This dialer must be held in a UserControl (or a ControlTemplate, or DataTemplate), because it's contained in a very elaborate window.
I don't want to put the KeyBindings on the Window, because then the UserControl is not reusable, and because its DataContext is not the same as the user control.
UPDATE:
I solved the second problem by setting Focusable="False" on all buttons.
To prevent the buttons from gaining focus, I set Focusable="False" for all buttons.
To set the focus when the window opens, I set Focusable="True" on the UserControl, and on the Loaded event I called Focus().
Dialer.xaml
d:DataContext="{d:DesignInstance local:DialerViewModel}"
Loaded="UserControl_Loaded" Focusable="True">
<UserControl.InputBindings>
Dialer.xaml.cs
private void UserControl_Loaded(object sender, RoutedEventArgs e) {
Focus();
}
I found no combination of FocusManager.FocusedElement that worked. I tried {Binding ElementName=myUserControl}, and {Binding RelativeSource={RelativeSource Self}}.
It's a question of Focus. When your window is loaded for the first time, your user control does not have Focus. So key bindings' gesture will not intercept your keypress. You have, at the first app loading time, to give Focus to your user control. (Focusable ="True"(i don't know if this helps but i am sure the FocusManager will helps)). Then your key gestures will work well.

How can I write something in a TextBox that's also used as a KeyBinding?

I have a global input binding for the key period (.). I'd still like to be able to enter it in a TextBox? Is there a way to accomplish this?
Here's a simple sample case. The command is executed when I type the period in the TextBox.
XAML:
<Window x:Class="UnrelatedTests.Case6.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.InputBindings>
<KeyBinding Key="OemPeriod" Command="{Binding Command}" />
</Window.InputBindings>
<Grid>
<TextBox >Unable to type "." here!</TextBox>
</Grid>
</Window>
C#:
using System;
using System.Windows;
using System.Windows.Input;
namespace UnrelatedTests.Case6
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
DataContext = this;
}
public ICommand Command
{
get { return new CommandImpl(); }
}
private class CommandImpl : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
MessageBox.Show("executed!");
}
public event EventHandler CanExecuteChanged;
}
}
}
You can bind the Key in your KeyBinding and change it's value to Key.None when your TextBox got focus:
Xaml:
<Window x:Class="UnrelatedTests.Case6.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
GotFocus="Window_GotFocus">
<Window.InputBindings>
<KeyBinding Key="{Binding MyKey}" Command="{Binding Command}" />
</Window.InputBindings>
<Grid>
<TextBox/>
</Grid>
</Window>
MainWindow.cs: (with INotifyPropertyChanged implemented)
Key _myKey;
public Key MyKey
{
get
{
return _myKey;
}
set
{
_myKey = value;
OnPropertyChanged("MyKey");
}
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
MyKey = Key.OemPeriod;
}
private void Window_GotFocus(object sender, RoutedEventArgs e)
{
if (e.OriginalSource is TextBox)
MyKey = Key.None;
else
MyKey = Key.OemPeriod;
}

WPF RoutedCommand CanExecute event is not fired

I have created a UserControl that has a command (DeleteCommand) inside:
public partial class TestControl : UserControl
{
public static RoutedCommand DeleteCommand = new RoutedCommand();
private void DeleteCommandExecute(object sender, ExecutedRoutedEventArgs e)
{
}
private void DeleteCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
public TestControl()
{
InitializeComponent();
CommandBinding deleteCommandBinding = new CommandBinding(DeleteCommand, DeleteCommandExecute, DeleteCommandCanExecute);
this.CommandBindings.Add(deleteCommandBinding);
}
}
I have put this UserControl inside a Window:
<Window x:Class="TestRoutedCommand.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestRoutedCommand"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Content="Fire event" Margin="156,29,205,254" Command="{x:Static local:TestControl.DeleteCommand}" />
<local:TestControl Margin="126,135,135,46"/>
</Grid>
</Window>
There is also a Button which is using the DeleteCommand. My problem is that this button is always disabled and the DeleteCommandCanExecute handler is never called, although e.CanExecute is always set to true.
I have tried to call:
CommandManager.InvalidateRequerySuggested();
but nothing happens. The event is never fired. Maybe I am doing the CommandBinding wrong.
What I want to achieve is that when the user clicks on the button that the DeleteCommandExecute handler is fired. My goal is to create commands for my MenuButtons which will trigger some methods in my UserControls which can be deep in the Visual Tree.
Slightly change your XAML:
<Grid>
<Button Content="Fire event" Margin="156,29,205,254" Command="{x:Static local:TestControl.DeleteCommand}" CommandTarget="{Binding ElementName=Control1}" />
<local:TestControl x:Name="Control1" Margin="126,135,135,46"/>
</Grid>
CommandTarget says where to find needed handlers.

Categories

Resources