My OnCanExecute event happens repeatedly - wpf

I've got a TextBox in xaml that I want to be validated using the CanExecute option of the commands. Here I have the xaml:
<UserControl.Resources>
<RoutedUICommand x:Key="CloseCommand" Text=""/>
<RoutedUICommand x:Key="NextCommand" Text=""/>
<RoutedUICommand x:Key="BackCommand" Text=""/>
</UserControl.Resources>
<UserControl.CommandBindings>
<CommandBinding Command="{StaticResource CloseCommand}" Executed="Close" />
<CommandBinding Command="{StaticResource NextCommand}" Executed="Next" CanExecute="OnCanExecute" />
<CommandBinding Command="{StaticResource BackCommand}" Executed="Back" />
</UserControl.CommandBindings>
These are the CommandBindings that are called, where the Next command is tied to a CanExecute restriction. The button looks like this:
<Button
DockPanel.Dock="Right"
Name="ButtonNext"
Margin="0,0,15,0"
Style="{DynamicResource NextButton}"
Command="{StaticResource NextCommand}">
</Button>
And the .cs for the validation:
private void OnCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = ValidateFields();
}
Where the ValidateFields() method looks like this:
public bool ValidateFields()
{
if (string.IsNullOrEmpty(AutoCompleteBox.Text)) return false;
return IsValid(MyTextBox);
}
So my OnCanExecute is tied to a TextBox validation. But I've got a problem. Even if a leave the Window where this code is written, I noticed that this OnCanExecute is still being called.
Why is this? Why my OnCanExecute is being called even if it's not visible?

NextCommand is a RoutedCommand.
If you have a look at the implementation, you'll notice that CanExecuteChanged is fired whenever CommandManager.RequerySuggested is fired.
This will be in many circumstances. I don't know exactly but it may be as often as the mouse moves or a key is pressed (or other user interactions)!
You could add a counter variable in your Eventhandler could to have a look how often this happens.
If your RoutedCommand.CanExecute depends on something from "code-behind" and you don't move the mouse, press a key or some other user interaction, the CanExecuteChanged may not fire although it should.
You can test this if you make your CanExecute depend on a periodically switching bool.

Related

Issues with ApplicationCommands between modules in Wpf with Prism

I am using WPF with Prism.
I have a Custom Canavs DrawingCanvas.cs in Module A where i have set ApplicationCommands.Delete as commandbinding as follows.
this.CommandBindings.Add(newCommandBinding(ApplicationCommands.Delete, Delete_Executed, Delete_Enabled));
I have another Module B where i have a MenuItem delete, and another Button for delete. I have set command from xaml for both as follows.
<MenuItem Header="Delete" x:Name="menuItemDelete" Command="{x:Static ApplicationCommands.Delete}"
<Button x:Name="buttonDelete" Background="Black" Height="25" Width="25" ToolTip="Delete" Command="{x:Static ApplicationCommands.Delete}"/>
Here MenuItem for Delete works fine as expected.(MenuItem will be enabled when some drawing is selected in DrawingCanvas from UI and "Delete_Executed" in DrawingCanvas.cs will be called when on Delete menu item click)
But Delete button is always Disabled. It doesn't get enabled when some drawing is selected in DrawingCanvas from UI.
I am wondering why the same command working for MenuItem but not for Button in same view and same Module.
Can anybody help me with this? Am i doing anything wrong?
Thanks in advance.
The problem here is that the CanExecute for the ApplicationCommands.Delete returns false all the time. There is one way to solve this - through the Window's command bindings:
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Delete" CanExecute="CommandBinding_CanExecute"/>
</Window.CommandBindings>
The event handler would look like:
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
It can always be true if you want it to be enabled all the time or just add your condition as in when you want it enabled.

How to use Commands to react to a ListBox

I'm trying to learn Commanding and have set up a simple wpf project to use a custom command. I have a ListBox and a Button on a Window. When the ListBox has the focus and an Item is selected, I want the Button to be enabled, otherwise it should be disabled.
I define a CustomCommand in a separate CustomCommands class:
Public Shared ReceivedFocus As New RoutedCommand
and in my Window I set it up as follows:
<CommandBinding
Command="{x:Static local:CustomCommands.ReceivedFocus}"
CanExecute="CanActivate"
Executed="ChangeSelection">
</CommandBinding>
and use the command for the ListBox as follows:
<ListBox
x:Name="lstInactive">
<i:Interaction.Triggers>
<i:EventTrigger
EventName="GotFocus">
<i:InvokeCommandAction
Command="{x:Static local:CustomCommands.ReceivedFocus}"
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
and, finally, the CanActivate routine is:
Private Sub CanActivate(sender As Object, e As CanExecuteRoutedEventArgs)
If lstInactive.SelectedIndex >= 0 Then
e.CanExecute = True
Else
e.CanExecute = False
End If
End Sub
This is not working. The major problem is that I don't understand how to relate the CanExecute value to the Button. Should I ignore the CanExecute value in the CanActivate routine and instead just set the Enabled property of the Button? If so, what is the value of the CanExecute paramter of the CanExecuteRoutedEventArgs?
A second problem is that the GotFocus event is not firing until I select an item in the ListBox a second time.
Or maybe I don't have a grasp on Commanding at all and this is not the right approach. This small project is not important in itself, it is intended to make sure I understand Commanding after reading numerous articles about it before I start to use Commands in "real" projects. Sadly, at this stage it is clear I don't.
This is not working. The major problem is that I don't understand how to relate the CanExecute value to the Button.
Bind its Command property to the same command:
<Button Content="Button" Command="{x:Static local:CustomCommands.ReceivedFocus}" />
The Button should then be enabled or disabled based on the value that you set the CanExecute property to in your CanActivate event handler.
You probably also want to listen to the SelectionChanged event. This works as expected:
<StackPanel>
<StackPanel.CommandBindings>
<CommandBinding
Command="{x:Static local:CustomCommands.ReceivedFocus}"
CanExecute="CanActivate"
Executed="ChangeSelection">
</CommandBinding>
</StackPanel.CommandBindings>
<ListBox x:Name="lstInactive">
<ListBoxItem>first</ListBoxItem>
<ListBoxItem>second</ListBoxItem>
<ListBoxItem>third</ListBoxItem>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{x:Static local:CustomCommands.ReceivedFocus}">
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
<Button Content="Button" Command="{x:Static local:CustomCommands.ReceivedFocus}" />
</StackPanel>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void CanActivate(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = lstInactive.SelectedIndex >= 0;
}
private void ChangeSelection(object sender, ExecutedRoutedEventArgs e)
{
}
}

System.Windows.Input.KeyBinding doesn't check CanExecute

So I have a simple KeyBinding on an input element that executes a command to kick off some analysis.
<dxe:TextEdit.InputBindings>
<KeyBinding Key="Enter" Command="{Binding StartAnalysisCommand}" />
</dxe:TextEdit.InputBindings>
There are a few other simple input elements that, when changed, call RaiseCanExecuteChanged on the command. This propagates to the UI button, disabling it and preventing it from executing. But this CanExecute state seems to be entirely ignored by the KeyBinding event, before and after the RaiseCanExecuteChanged is called.
Tested using a normal WPF TextBox, and it calls CanExecute once you press Enter. Must indeed be an issue in the 3rd party control.
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.New" CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed"/>
</Window.CommandBindings>
<TextBox>
<TextBox.InputBindings>
<KeyBinding Key="Enter" Command="ApplicationCommands.New"/>
</TextBox.InputBindings>
</TextBox>
Edit: Example with a RelayCommand:
public class ViewModel
{
private RelayCommand _cmd;
public RelayCommand Cmd {
get { return _cmd ?? (_cmd = new RelayCommand(Executed, CanExecute)); }
}
public void Executed() { throw new NotImplementedException(); }
public bool CanExecute()
{
return true;
}
}
And the binding with the ViewModel as the context.
<KeyBinding Key="Enter" Command="{Binding Cmd}"/>
Okay, I figured out what the problem was. Thanks everyone for helping out--your answers caused me to realize the problem. Turns out it wasn't a matter of the CanExecute being called, but rather the timing of when the binding was updated. CanExecute was being called, but with the previous value of the binding.
I used the solution found on this SO answer to accept the value on Enter and the program now works as I had originally expected.

Is there any way to alias commands in WPF?

Is there any way to effectively "alias" commands in WPF ? My situation is this : I've created an application that uses ApplicationCommands.Delete in the context of a graphical editor that has a number of customized canvases. Some of the controls that are on these canvases use TextBoxes, but here's the problem : TextBox doesn't respond to ApplicationCommands.Delete, it responds to EditorCommands.Delete. Is there any way to cleanly get TextBox to respond to ApplicationCommands.Delete without subclassing or manually setting bindings on every TextBox instance ?
To answer your specific question, I know of no way to cause two separate routed commands to be treated as the same command. But because ApplicationCommands.Delete is a routed command, after it is delivered to its target, the TextBox and there is no command binding, it will begin bubbling up. So the simplest solution that meets your requirements is to install a command binding for ApplicationCommands.Delete somewhere inbetween the TextBox all the way up to and possibly including the Window, that implements the behavior you desire.
Here's an example that installs a handler on a parent Grid that sends the "right" command the the focused element which in this case will be a TextBox:
<Grid>
<Grid.CommandBindings>
<CommandBinding Command="ApplicationCommands.Delete" CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed"/>
</Grid.CommandBindings>
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="_Edit">
<MenuItem Header="_Delete" Command="ApplicationCommands.Delete"/>
</MenuItem>
</Menu>
<StackPanel>
<TextBox Text="Some text"/>
</StackPanel>
</DockPanel>
</Grid>
and here's the code-behind:
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
EditingCommands.Delete.Execute(null, Keyboard.FocusedElement);
}

Defining MenuItem Shortcuts

I need a simple way to set a shortcut for menu items.
But this donĀ“t work with shortcut, just with click:
<MenuItem Header="Editar">
<MenuItem Header="Procurar" Name="MenuProcurar"
InputGestureText="Ctrl+F"
Click="MenuProcurar_Click">
<MenuItem.ToolTip>
<ToolTip>
Procurar
</ToolTip>
</MenuItem.ToolTip>
</MenuItem>
</MenuItem>
I am using WPF 4.0
H.B. was right... I just wanted to add more precisions.
Remove the Click event on your MenuItem and associate it with a Command instead.
1 - Add/create your commands:
<Window.CommandBindings>
<CommandBinding Command="Open" Executed="OpenCommandBinding_Executed"/>
<CommandBinding Command="SaveAs" Executed="SaveAsCommandBinding_Executed"/>
</Window.CommandBindings>
The commands are refering to the following code:
private void OpenCommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
Open();//Implementation of open file
}
private void SaveAsCommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
SaveAs();//Implementation of saveAs
}
2 - Associate the commands with the wanted keys:
<Window.InputBindings>
<KeyBinding Key="O" Modifiers="Control" Command="Open"/>
<KeyBinding Key="S" Modifiers="Control" Command="SaveAs"/>
</Window.InputBindings>
3 - Finally assign the commands with you menu item (InputGestureText is just a decorating text):
<Menu Name="menu1">
<MenuItem Header="_File">
<MenuItem Name="menuOpen" Header="_Open..." Command="Open" InputGestureText="Ctrl+O"/>
<MenuItem Name="menuSaveAs" Header="_Save as..." Command="SaveAs" InputGestureText="Ctrl+S"/>
</MenuItem>
</Menu>
That way multiple inputs may be associated to the same command.
You need to use KeyBindings (and CommandBindings if you (re)use RoutedCommands such as those found in the ApplicationCommands class) for that in the controls where the shortcuts should work.
e.g.
<Window.CommandBindings>
<CommandBinding Command="New" Executed="CommandBinding_Executed" />
</Window.CommandBindings>
<Window.InputBindings>
<KeyBinding Key="N" Modifiers="Control" Command="New"/>
</Window.InputBindings>
For custom RoutedCommands:
static class CustomCommands
{
public static RoutedCommand DoStuff = new RoutedCommand();
}
usage:
<Window
...
xmlns:local="clr-namespace:MyNamespace">
<Window.CommandBindings>
<CommandBinding Command="local:CustomCommands.DoStuff" Executed="DoStuff_Executed" />
</Window.CommandBindings>
<Window.InputBindings>
<KeyBinding Key="D" Modifiers="Control" Command="local:CustomCommands.DoStuff"/>
</Window.InputBindings>
...
</Window>
(It is often more convenient to implement the ICommand interface rather than using RoutedCommands. You can have a constructor which takes delegates for Execute and CanExecute to easily create commands which do different things, such implementations are often called DelegateCommand or RelayCommand. This way you do not need CommandBindings.)
In my humble opinion is much easier just to use _ at the Header. This will create automatically the desired HotKey.
For example:
<MenuItem Header="_Editar">
<MenuItem Header="_Procurar" Name="MenuProcurar"
InputGestureText="Ctrl+F"
Click="MenuProcurar_Click">
<MenuItem.ToolTip>
<ToolTip>
Procurar
</ToolTip>
</MenuItem.ToolTip>
</MenuItem>
</MenuItem>
I'm overly biased by Windows.Forms & gulp VB 6, so I kind of agree with Jonathan and Jase that there's got to be a more straightforward/procedural method to statically wire up event handlers that aren't necessarily CommandBindings. And there is, I think.
A good tutorial for using non-CommandBinding handlers like this, but with an emphasis on buttons, can be found in this MSDN blog post, I believe. I'll distill and target MenuItems...
Creating the ICommand
First, create a class implementing ICommand. You can put this anywhere, of course, even in your MainWindow.xaml.cs file if you wanted, to keep your demo code insanely simple. You'll probably want to make CanExecute more complicated when you want to dis/en/able menu items later, but for now, we'll just always have our menu items enabled.
public class HelloWorldCommand : ICommand
{
public void Execute(object parameter)
{
MessageBox.Show(#"""Hello, world!"" from "
+ (parameter ?? "somewhere secret").ToString());
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
}
As the tutorial helpfully points out, you could call this command from anywhere already, with code like...
var hwc = new HelloWorldCommand();
if (hwc.CanExecute(this))
hwc.Execute(this);
Declaring your command in the Window
So let's add a sort of "declaration" for the HelloWorldCommand to our Window so that we can use it later. Inside of your Window tags, register the command as a resource:
<Window.Resources>
<local:HelloWorldCommand x:Key="hwc"/>
</Window.Resources>
Now we have a neat shortcut for linking to this "locally namespaced" command, "hwc", though you can obviously use any string you want. We'll use that a lot in our xaml.
Wiring up (and reusing!) the command
Let's add our MenuItems to our xaml. I've replaced the stock Grid with a DockPanel because that's the easiest way (for me) to have equi-spaced widgets that fill the Window, though I've left all of the rest of my UI out.
Note the Command="{StaticResource hwc}"s sprinkled into each MenuItem declaration. The key is the hwc in there - remember that that's our shortcut for the HelloWorldCommand that we set up at the Window level. And, of course, StaticResource says just to look it up the Window's resources. We're not binding anything; we're just using our shortcut.
<DockPanel LastChildFill="True">
<Menu DockPanel.Dock="Top">
<MenuItem Header="_File">
<MenuItem
Header="_Open"
Command="{StaticResource hwc}"
>
<MenuItem.CommandParameter>
<!-- so you could make this object as complex as you wanted,
like, say, your entire Window. See magic incantation, below. -->
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType=Window}" />
</MenuItem.CommandParameter>
</MenuItem>
<MenuItem
Header="_Close"
Command="{StaticResource hwc}"
CommandParameter="Close"
InputGestureText="Ctrl+G" />
<MenuItem
Header="_Save"
Command="{StaticResource hwc}"
CommandParameter="Save" />
<Separator />
<MenuItem
Header="_Quit"
Command="{StaticResource hwc}"
CommandParameter="Quit" />
</MenuItem>
</DockPanel>
CommandParameters to distinguish event sources
Note that we're using the same Command for everything! But how can we tell which widget threw the event? For that, you need to use the CommandParameter -- remember our Execute method's signature: Execute(object parameter). That CommandParameter parameter is what we can use to know how to handle the event. Try running this and note that the MessageBox will use whatever's in CommandParameter to let you know the source of the event. We're doing it all manually, but that's not too bad.
Also note that you can make these objects as complicated as you'd like. You can use a property in the MenuItem tag to define the parameter, or can use "real" <MenuItem.CommandParameter> tags, like in the Open menu item, above, to define something complex. In this case, we're passing the entire parent Window object, which was the easiest (though not the cleanest) way to throw our VB6-ish context into the event handler code.
Adding keyboard shortcuts to MenuItems (aka, "Answering the OP")
And now we can finally answer the original question! Let's finally wire up a keyboard shortcut for the Close menu item. You'll note that we've already declared an InputGestureText. By itself, InputGestureText is only cosmetic. If we were overly picky, we could argue the mechanism for creating the keyboard shortcut doesn't have any direct, inherent relationship with the MenuItem at all!
We need to instead (or additionally) register a listener for Ctrl-G at the Window level to catch the keystroke. So at the top level of your Window tags, insert this (taken essentially from here):
<Window.InputBindings>
<KeyBinding Modifiers="Control"
Key="G"
Command="{StaticResource hwc}"
CommandParameter="window input binding"
/>
</Window.InputBindings>
Note that you could put CommandParameter tags in your KeyBinding by moving it from a self-closing piece of XML to "real" open and close KeyBinding tags.
And we're done. Run your app, and press Ctrl-G. Whaddup.
Pretty straightforward, once you've got the players straight, and much less magic binding-y than most intros to commands and MenuItems, I think.
Possible pro tip:
The whole CommandBinding thing confused me for a while. This is just for specific command types, I believe. That is, you can't just wire up any Command you like. Stuff like what's bragged about here (in what's admittedly a decent intro tutorial!)...
It might not be completely obvious, but by using commands, we just got a whole bunch of things for free: Keyboard shortcuts, text and InputGestureText on the items and WPF automatically enables/disables the items depending on the active control and its state. In this case, Cut and Copy are disabled because no text is selected, but Paste is enabled, because my clipboard is not empty!
... is kind of magic-y and not necessarily good, and can be confusing when you're new to WPF menus.
You can also declare RoutedUICommand in XAML:
<Window.Resources>
<RoutedUICommand x:Key="BuildCmd" Text="Build">
<RoutedUICommand.InputGestures>
<KeyGesture>CTRL+SHIFT+B</KeyGesture>
</RoutedUICommand.InputGestures>
</RoutedUICommand>
</Window.Resources>
Do the binding
<Window.CommandBindings>
<CommandBinding Command="{StaticResource BuildCmd}" Executed="BuildCmdExecuted"/>
</Window.CommandBindings>
And in MenuItem
<MenuItem Command="{StaticResource BuildCmd}"/>
Another solution is discussed here.
Here is solution in PowerShell:
Define your XAML file:
<Window x:Class="WpfApp1.Window1"
xmlns:local="clr-namespace:WpfApp1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="My App" Height="620" Width="950" >
<Window.InputBindings>
<KeyBinding Key="S" Modifiers="Ctrl"/>
</Window.InputBindings>
<Grid x:Name="MainGrid">
<!--Your GUI is here-->
<Menu Margin="0">
<MenuItem Header="_File">
<MenuItem x:Name="SaveProfile" Header="_Save Profile" InputGestureText="Ctrl+S"/>
</MenuItem>
</Menu>
</Grid>
</Window>
Add new type to be able to create Command(s)
Add-Type #"
public class DelegateCommand : System.Windows.Input.ICommand
{
private System.Action<object> _action;
public DelegateCommand(System.Action<object> action)
{
_action = action;
}
public bool CanExecute(object parameter)
{
return true;
}
public event System.EventHandler CanExecuteChanged = delegate { };
public void Execute(object parameter)
{
_action(parameter);
}
}
"#
Create and assign new Command to the previously defined KeyBinding, it is first key binding, that is why I'm addressing it with [0].
Note:in my case I saved the handle to the main Window in $hash.Window variable, you should put here the link to your main window object, that you created with [Windows.Markup.XamlReader]::Load($xamlXmlNodeReader) command or other window creation command.
$hash.Window.InputBindings[0].Command = New-Object DelegateCommand( { Save-Profile } )
Create your function that you put into command
function Save-Profile {
Write-Host "Save Profile"
# Your logic goes here
}
Thanks to Nicholas Wolverson for the tips on how to create the Type for Command.
This work for me
<ContextMenu PreviewKeyUp="ContextMenu_PreviewKeyUp">
<MenuItem Header="Delete" Click="DeleteID" />
</ContextMenu>
Code behind:
private void ContextMenu_PreviewKeyUp(object sender, KeyEventArgs e)
{
ContextMenu contextMenu = sender as ContextMenu;
if (e.Key == Key.D)
{
//DELETE ID
}
contextMenu.IsOpen = false;
}

Resources