WPF RoutedCommand CanExecute event is not fired - c#

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.

Related

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 modify from C# the value of an element which is define in a XAML DataTemplate?

I created a "WPF Application Project" in Visual Studio 2013.
I opened the "MainWindow.xaml" file and I wrote the following XAML code:
<Window x:Class="TestProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="AlphaDataTemplate">
<Label
Name="LabelInDataTemplate"
Content="Good morning!" />
</DataTemplate>
</Window.Resources>
<Grid>
<ContentPresenter
Name="MyContentPresenter"
ContentTemplate="{StaticResource AlphaDataTemplate}" />
<Button
Name="MyButton"
Click="MyButton_OnClick"
Content="Change the content of the Label in the DataTemplate"
Width="320"
Height="30" />
</Grid>
In this XAML file I created a "DataTemplate" which corresponds to the key "AlphaDataTemplate". The DataTmplate contains just one label with the name "LabelInDataTemplate" where I have hardcoded the "Good morning!" string in the "Content" attribute of the label.
Then I use created a "ContentPresenter" with the name "MyContentPresenter" and I pass as content the "DataTemplate" I previously created (AlphaDataTemplate).
As next step, I created a "Button" with the name "MyButton" and I have set a "Click" event called "MyButton_OnClick"
So far so good...!
The question comes now and actually in C# in the code behind file "MainWindow.xaml.cs". See the code below:
using System.Windows;
namespace TestProject
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void MyButton_OnClick(object sender, RoutedEventArgs e)
{
LabelInDataTemplate.Content = "Bye!"; // <-- Tha does not work.
}
}
}
In this C# code behind file you can see the definition of the "Click" (MyButton_OnClick) event of the Button (MyButton) which appears in XAML.
What I am trying to do in this "Click" event, is to change the value of the "Content" of the "Label" (LabelInDataTemplate) which is in the DataTemplate (AlphaDataTemplate).
Unfortunately, that does not work.
I cannot actually access the "Name" (LabelInDataTemplate) of the "Label", because it is contained in the "DataTemplate" (AlphaDataTemplate)
If anyone has any idea, how could I modify from C# the value of an element which is define in a XAML DataTemplate, please give me feedback. I would really appreciate it.
Thank you in advance.
I strongly oppose your method of changing the content of label via DataTemplate, However your requirement is possible, but very subtle.
Code
private void MyButton_OnClick(object sender, RoutedEventArgs e)
{
var alphaDataTemplate = this.Resources["AlphaDataTemplate"] as DataTemplate;
var label = alphaDataTemplate.FindName("LabelInDataTemplate", MyContentPresenter) as Label;
label.Content = "It Works";
}
Please learn MVVM and use proper DataBinding for this purpose. For sake of solving this problem:
Implement INotifyPropertyChanged interface on your Window class and Define string property like below
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
public string _contentMsg;
public string ContentMsg
{
get { return _contentMsg; }
set
{
_contentMsg = value;
RaisePropertyChanged("ContentMsg");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propName)
{
if(PropertyChanged !=null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
In your xaml bind the ContentPresenter and update your DataTemplate label like
<ContentPresenter
Name="MyContentPresenter"
Content = "{Binding ContentMsg}"
ContentTemplate="{StaticResource AlphaDataTemplate}" />
<DataTemplate x:Key="AlphaDataTemplate">
<Label
Name="LabelInDataTemplate"
Content="{Binding}" />
Now in click handler (I would use Commands here), set ContentMsg to whatever you want
private void MyButton_OnClick(object sender, RoutedEventArgs e)
{
ContentMsg = "Bye!";
}

Trouble with simple C# WPF custom controls

I'm having some difficulty finding simple examples while using WPF when it comes to control bindings and I'm hoping you can help me get my head around it with this simple example.
Can you please explain why this doesn't work and also a simple way to get it running?
I've looked at numerous tutorials but they are all still a little advanced for me at this stage I think so any help is greatly appreciated.
Thanks!
XAML:
<Window
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" mc:Ignorable="d" x:Class="DemoProject.MainWindow"
xmlns:custom="clr-namespace:DemoProject"
Title="DemoProject" >
<TextBox x:Name="MyTextBox">
<TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{x:Static custom:MainWindow.CommandEnterKeyPressed}"
CommandParameter="{Binding Path=Text, RelativeSource={RelativeSource AncestorType={x:Type TextBox}}}" />
</TextBox.InputBindings>
</TextBox>
<Window.CommandBindings>
<CommandBinding Command="{x:Static custom:MainWindow.CommandEnterKeyPressed}"
Executed="CommandEnterKeyPressedExecuted" />
</Window.CommandBindings>
</Window>
C#:
namespace DemoProject
{
public partial class MainWindow : Window
{
private static RoutedUICommand CommandEnterKeyPressed;
public MainWindow()
{
InitializeComponent();
}
public static RoutedUICommand CommandEnterKeyPressed = new RoutedUICommand();
private void CommandEnterKeyPressedExecuted(object sender, CanExecuteRoutedEventArgs e)
{
MessageBox.Show("Enter key was pressed");
}
}
}
When I run this, I get the errors
"The member "CommandEnterKeyPressed" is not recognised or is not
accessible" and "No overload for "'ommandEnterKeyPressed' matches
delegate 'System.Windows.Input.ExecutedRoutedEventHandler'".
Is there something simple I'm missing?
Thanks.
Change CanExecuteRoutedEventArgs to ExecutedRoutedEventArgs
private void CommandEnterKeyPressedExecuted(object sender, CanExecuteRoutedEventArgs e)
Should be
private void CommandEnterKeyPressedExecuted(object sender, ExecutedRoutedEventArgs e)
CanExecuteRoutedEventArgs is used for CanExecute event. You should also remove this line
private static RoutedUICommand CommandEnterKeyPressed;
and leave only public declaration of your RoutedUICommand

Catch custom event by Interactivity.EventTrigger

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.

When to clean up a command-binded control?

I have a user control which has a CheckBox, a Button, and a CommandBinding. If the CheckBox is checked, the Button is enabled. The MainWindow uses the UserControl. When the Button in the main window is pressed, the UserControl is removed from UI, and GC.Collect() is called, but CanExecute method still runs.
I find that if I click the button in main window twice, CanExecute will no longer run. It seems that I don't call GC.Collect() at the right time.
I want to know what is the good timing to call GC to clean the unused user control, so that CanExecute will not be called.
XAML
<UserControl x:Class="WpfApplication1.UserControl1"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<RoutedUICommand x:Key="okCommand" Text="OK"/>
</UserControl.Resources>
<UserControl.CommandBindings>
<CommandBinding Command="{StaticResource okCommand}" CanExecute="CommandBinding_CanExecute_1"/>
</UserControl.CommandBindings>
<StackPanel>
<CheckBox Name="checkBox" Content="CheckBox"/>
<Button Command="{StaticResource okCommand}" Content="{Binding Path=Text, Source={StaticResource okCommand}}"/>
</StackPanel>
</UserControl>
Code behind
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
private void CommandBinding_CanExecute_1(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = checkBox.IsChecked.GetValueOrDefault(false);
System.Media.SystemSounds.Beep.Play();
}
}
MainWindow
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Loaded="Window_Loaded_1">
<StackPanel>
<Border Name="container"/>
<Button Content="Set Null" Click="Button_Click_1"/>
</StackPanel>
</Window>
Code behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
container.Child = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
private void Window_Loaded_1(object sender, RoutedEventArgs e)
{
UserControl1 uc = new UserControl1();
container.Child = uc;
}
}
Use grid as container and Container.Clear() method and forget about GC.
I find another solution. That is to call CommandBindings.Clear() in UserControl1 when it unloads.
I believe this is a neat way, since the caller of UserControl1 doesn't take care of the cleaning job of UserControl1.

Categories

Resources