WPF ICommand CanExecute get called even though there is a modal window - wpf

Let's say I have an user control with a button
<UserControl>
<Grid>
<Button x:Name="button" Content="Show Dialog" DataContext="{Binding m_btnShowDialog}" Style="{StaticResource ButtonVM}" Command="{Binding Cmd}" HorizontalAlignment="Left" Margin="29,56,0,0" VerticalAlignment="Top" Width="75" >
</Grid>
</UserControl>
The command class implements ICommand interface.
When the focus is on the above dialog, the CanExecute sometimes get called.
That is something to be expected.
The problem is when I click the button, the Execute method get called and a new modal dialog pops up.
The focus should be on the new dialog but for some reasons, the CanExecute still get called when I interract with the new dialog.
Is that behavior normal?
And how can I override that behavior?
I don't want CanExecute method of commands attached to controls of parent dialog to be called when a child modal dialog is showing up.

This is expected. Quoting directly from WPF expert Josh Smith :
WPF will automatically ask all of the commands being used in your UI
if they can execute. This happens at various times, such as when input
focus shifts to another control, an item is selected in a list, etc.
You can also programmatically trigger this to happen by calling the
CommandManager’s InvalidateRequerySuggested static method. This all
seems magical, dreamy, and almost too good to be true.
You can get more detailed and clear explanation here
You can override behavior using CanExecuteChanged event in your command implementation.
class MyCommand : ICommand
{
public bool CanExecute(object parameter)
{
return maybeTrueOrFalse;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
// Do something awesome.
}
}

Related

Why WPF CommandBinding with one button is effecting the other button?

My following code is Implementing a custom WPF Command. I have bonded only the first button (titled Exit) with the CommandBinding so that when Exit button is clicked and e.CanExecute is true in CommandBinding_CanExecute event, the CommandBinding_Executed event closes the app. This scenario works fine with Exit button. But, when btnTest button - that is not bonded with any command - is clicked, CommandBinding_CanExecute event also gets called. This can be tested by placing a breakpoint on the btnTest_Click event and noticing that after the code exits this event the cursor goes to CommandBinding_CanExecute event.
Question: Why the btnTest button is also calling CommandBinding_CanExecute event despite that fact that CommandBinding is used only on Exit button. What I may be missing here, and how can we fix the issue?
Remarks For brevity I have simplified the issue. But in real scenario e.CanExecute value in CommandBinding_CanExecute is set to true by calling a function that performs a long complex logic that returns true or false based on certain scenario for the Exit button. And I don't want that long logic to be performed when other buttons (e.g. btnTest) is clicked.
MainWindow.Xaml:
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Content="Exit" Command="local:CustomCommands.Exit">
<Button.CommandBindings>
<CommandBinding Command="local:CustomCommands.Exit" CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed"/>
</Button.CommandBindings>
</Button>
<Button x:Name="btnTest" Content="Test" Click="btnTest_Click" Margin="10"/>
</StackPanel>
</Grid>
</Window>
MainWindow.Xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnTest_Click(object sender, RoutedEventArgs e)
{
System.Diagnostics.Debug.WriteLine("Why this event is calling ExitCommand_CanExecute");
}
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
Application.Current.Shutdown();
}
}
public static class CustomCommands
{
public static readonly RoutedUICommand Exit = new RoutedUICommand
(
"Exit",
"Exit",
typeof(CustomCommands),
new InputGestureCollection()
{
new KeyGesture(Key.F4, ModifierKeys.Alt)
}
);
}
What makes you think btnTest is calling CommandBinding_CanExecute? It doesn't.
The CanExecute method of the command is called by the CommandManager whenever it wants to know the current status of the command. You don't control when this happens. The framework does. It's not connected to the btnTest.
If you have some complex logic in CanExecute, you should consider creating a custom command class that implements the ICommand interface and raise the CanExecuteChanged event whenever you want the framework to refresh the status of the command by calling its CanExecute method. This way you can control when the command should be refreshed.
You could then bind the Command property of the Button to an instance of your custom command class. If you google for "DelegateCommand" or "RelayCommand", you should find a lot of examples. This blog post may be a good starting point.
Any interaction with the UI which is considered by the designers of wpf to be significant will indirectly initiate a check of all bound canexecute.
The idea being you changed something, did something or other. Best check if all these commands should still be enabled.
It's actually commandmanager.requerysuggested() that is invoked.
This doesn't directly invoke canexecute.
What it does is tells commands they should go check whether they can still be executed.
This isn't completely insane because whilst your button's command is invoking some code then there's a fair chance if the user clicks some other button then your viewmodel will be partly updated or in some indeterminate state,
You should never drive other logic using canexecute.
It is very common to add a bool IsBusy to a base viewmodel and check that to see if anything is doing stuff and you should not allow the user to do something else.
An extra check within commands on IsBusy is part of this pattern.

How does NotifyOnValidationError end up calling CanExecute (and why doesn't it work with MVVM Light RelayCommand)

Maybe the longest question title of all time! Because this is a two part question.
(1) I do not understand how setting NotifyOnValidationError="True" can trigger updates to my CanExecute. There is a bit of magic involved here that I need to understand. Someone(thing) subscribes to the CanExecuteChanged event of my ICommand but the call stack points to External code, so I can not figure out what is going on.
(2) Maybe the most important follow up questions is: Why does it not work in MVVM Light RelayCommand! The CanExecute is only called once at initialization and then never again. Looking at the source code for RelayCommand in MVVM Light does not reveal any chocking differences compared to my own implementation. I should mention that Prism's DelegateCommand does not seem to work either.
(Bonus) Maybee I am approaching this problem the wrong way? I just basically want to enable/disable buttons based on Validation failiures.
XAML (snippet):
<TextBox Grid.Column="1" Grid.Row="0">
<Binding Path="X" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True">
<Binding.ValidationRules>
<ExceptionValidationRule></ExceptionValidationRule>
</Binding.ValidationRules>
</Binding>
</TextBox>
<Button Grid.Column="1" Grid.Row="3" Command="{Binding CalculateCommand}">
Calculate
</Button>
RelayCommand:
public class MyRelayCommand : ICommand
{
readonly Action<object> Execute_;
readonly Predicate<object> CanExecute_;
public MyRelayCommand(Action<object> Execute, Predicate<object> CanExecute)
{
if (Execute == null)
throw new ArgumentNullException("No action to execute for this command.");
Execute_ = Execute;
CanExecute_ = CanExecute;
}
public bool CanExecute(object parameter)
{
return (CanExecute_ == null) ? true : CanExecute_(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
Execute_(parameter);
}
}
ViewModel:
private DelegateCommand _calculateCommmand;
public DelegateCommand CalculateCommand
{
get
{
return _calculateCommmand ?? (_calculateCommmand = new DelegateCommand(
() =>
{
Sum = X + X;
},
() =>
{
try
{
Convert.ChangeType(X, TypeCode.Byte);
return true;
}
catch
{
return false;
}
}));
}
}
PS: If you wanna buy my X + X program when it is done email me at sales#xplusx.com
(2) I figured this one out myself. You can choose to include RelayCommand from two different namespaces, make sure you use
using GalaSoft.MvvmLight.CommandWpf;
I am still looking for a good answer to (1), how the plumbing works that raises CanExecutetChanged based on validation error.
I don't think it depends on the ICommand implementation. In yours, I see a public event EventHandler CanExecuteChanged, where you tell the CommandManager to handle the invocation of your command's CanExecute() method. Without the CommandManager, you would have to handle this yourself, e.g. by providing your ICommand implementation with a public void RaiseCanExecuteChanged() method that your ViewModel has to call for every command it considers to be needed to recalculated, e.g. inside the ViewModel's OnPropertyChanged. Example: https://codereview.stackexchange.com/questions/124361/mvvm-am-i-doing-it-right
So the CommandManager does the magic for you. As soon as you invoke your ViewModel's PropertyChanged event, the "external code" handles the affected commands and asks them for a fresh CanExecute() value.
(1) I think it goes like this.
When we bind a RelayCommand : ICommand to Button.Command, the binding process will also attach an eventhandler to the ICommand.CanExecuteChanged. This is default behavior.
The eventhandler passed to CanExecutedChanged will be passed along and attached to the static event CommandManager.RequerySuggested.
When a validation error occurs and the NotifyOnValidationError is set an external force, or a jedi, will raise the RequerySuggested event which will broadcast to ALL active commands.
The Button recieves the event and consequently calls CanExecute to know if it shold disable/enable the button.
I would like to know more about the third bullet point above, so I will keep the question open for a little longer to give the experts a chance to chime in.

Jump to navigation inside a UserControl

I am currently trying to make a "jump to" menu in my app. The way I would like it to look and work is such as one at Google design book here at the top, where you have the subtitles of every part of that page in a menu (to be precise, that navigation part with such links as Contents, Baseline Grids, Keylines and Spacing etc.). I'd like it to work without splitting it into new MVVM Views if possible, all inside one UserControl. Is it possible to create some keywords and just make a menu with "goto" links like in HTML?
Baseline Grids
Maybe a custom ScrollViewer? But how would one know the height of every element in the UI?
Every framework element has a BringIntoView method. You can define a hyperlink, or button, menu, whatever that is able to invoke a command. The command would take an element as a parameter, and the command execution would call BringIntoView();
A simple example:
public class JumpToElementCommand : ICommand
{
public void Execute(object parameter)
{
FrameworkElement frameworkElement = parameter as FrameworkElement;
if (frameworkElement != null)
{
frameworkElement.BringIntoView();
}
}
public bool CanExecute(object parameter)
{
return parameter is FrameworkElement;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
Xaml Usage:
<Window.Resources>
<commands:JumpToElementCommand x:Key="JumpToElementCommand" />
</Window.Resources>
<Button Command="{StaticResource JumpToElementCommand}" CommandParameter="{Binding ElementName=FirstJump}" />
<Grid x:Name="FirstJump">
</Grid>
Edit: Added CommandManager.RequerySuggested so that the command parameter is reevaluated after loading.
Now (with great help of Michael G) I have the answer.
First of all make an ihnerited panel for the menu (for me it was parent ItemsControl with StackPanel host). Than subscribe to all Click events of the Button Items inside ItemsControl. As a CommandParameter bind to an element you want to jump to:
<Button CommandParameter="{Binding ElementName=jumpTarget}"/>
And in the Click event handler you use a solution presented here where istead of
Control_GotFocus(object sender, RoutedEventArgs e)
you pass the CommandParameter like here:
Control_GotFocus(FrameworkElement targetControl)
You also have to get the parent ScrollViewer and put it as AssociatedObject in the example above (here is explained how to get parent of selected type). Of course you can avoid an event subsciption by simply using a command with parameter added to every Button from the ItemsControl.

implement CanExecute MVVM light using ICommand

I have a program, where a button should only be active if the corresponding userControl has focus.
I am using MVVM light, and got a command implementing the ICommand interface.
I have tried using the Keyboard.FocusedElement, but this returns nothing.
This is the code for the command (Note that it just returns true right now to get it working, this sis of course what i'm trying to fix):
class AddItemToNodeCommand<T> : ICommand
{
public bool CanExecute(object parameter)
{
Debug.WriteLine("fokuselement er: " + Keyboard.FocusedElement);
return true;
// throw new NotImplementedException();
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
Debug.WriteLine("Parameter er: " + parameter);
Debug.WriteLine("fokuselement er: " + Keyboard.FocusedElement);
//throw new NotImplementedException();
}
}
From the viewmodel:
public ICommand AddItemToNodeCommand { get; private set; }
AddItemToNodeCommand = new AddItemToNodeCommand<object>();
And finally some of the XAML:
<RibbonButton SmallImageSource="../Images/whatever.png" Label="Attribute" Command="{Binding AddItemToNodeCommand}" CommandParameter="Attribute"/>
I haven't posted the xaml for the userControl, but the idea is that when the userControl has focus, the CanExecute should be true.. I thought it would work with the Keyboard.FokusedElement, but I was wrong. What can I do?
Thank you in advance.
Seems that Keyboard.FocusedElement is a bit fickly.
Have a look here for a solution involving attached behavior and overriding the Keyboard.GotKeyboardFocusEvent. I tried it, and it seems to work.
Otherwise, you can bind to IsKeyboardFocused or IsKeyboardFocusWithin. Just put this in your xaml for a quick example:
<StackPanel>
<TextBox Name="test_txtbx" >Hullo</TextBox>
<TextBox Name="test_txtbx_2">Hullo 2</TextBox>
<Label Content="{Binding ElementName=test_txtbx, Path=IsKeyboardFocused}"></Label>
<Label Content="{Binding ElementName=test_txtbx_2,Path=IsKeyboardFocusWithin }"></Label>
<Button Click="TestClick">test me </Button>
</StackPanel>
The label shows if the matching textbox has focus.
(Here's an old article which says focus is funny as well .. dunno if still relevant though).

WPF ICommand over a button

I have implemented a custom IComand class for one of my buttons. The button is placed in a page 'MyPage.xaml' but its custom ICommand class is placed in another class, not in the MyPage code behind. Then from XAML I want to bind the button with its custom command class and then I do:
MyPage.xaml:
<Page ...>
<Page.CommandBindings>
<CommandBinding Command="RemoveAllCommand"
CanExecute="CanExecute"
Executed="Execute" />
</Page.CommandBindings>
<Page.InputBindings>
<MouseBinding Command="RemoveAllCommand" MouseAction="LeftClick" />
</Page.InputBindings>
<...>
<Button x:Name="MyButton" Command="RemoveAllCommand" .../>
<...>
</Page>
and the custom command button class:
// Here I derive from MyPage class because I want to access some objects from
// Execute method
public class RemoveAllCommand : MyPage, ICommand
{
public void Execute(Object parameter)
{
<...>
}
public bool CanExecute(Object parameter)
{
<...>
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
My problem is how to say MyPage.xaml that Execute and CanExecute methods for the button is in another class and not the code behind where is placed the button. How to say these methods are in RemoveAllCommand Class in XAML page.
Also I want to fire this command when click mouse event is produced in the button so I do an input binding, is it correct?
Thanks
Since you have an ICommand, you can bind it to the Button through the Command property, and the button will use it, i.e. it will call CanExecute to enable/disable itself and the Execute method when the button is pressed. There is no need for any other input binding.
Now the problem is that the button has to find the command. A simple solution is to put an instance of the command in the DataContext of the button (or of its parents).
If the DataContext has a property called RemoveAll of type RemoveAllCommand, you can simply change your XAML button to:
<Button Command="{Binding RemoveAll}" .. />
and remove the CommandBinding and InputBinding
oK, Thanks, tomorrow I'll try it.
Now to avoid problems I have moved all in RemoveAllCommandClass Class to the code behind of MyPage and I have done some modifications.
1.- I added this to MyPage.xaml:
xmlns:local="clr-namespace:GParts"
then I have done:
<Page.CommandBindings>
<CommandBinding Command="{x:Static local:Pages.MyPage._routedCommand}"
Executed="Execute"
CanExecute="CanExecute"/>
</Page.CommandBindings>
<Button Command="{x:Static local:Pages.MyPage._routedCommand}" .../>
And all is ok and works. When I press button it executes background worker (bw) that is called in Execute method. bw is into another class. In background worker class I have a variable (isRunning) that indicates if the bw is executing. Before executing DoWork event I set it to true and when bw completes, at RunWorkerCompleted, I set it to false. So from CanExecute I check isRunning in bw class and I set to true e.canExecute if isRunning is false, and e.canExecute to false if isRunning is true. So the button is disabled by WPF automatically when bw is running but when bw finishes the button continues disabled and not returns to enabled until I press it again. Why is WPF not updating the button state to enabled when bw finishes until I press again the button?
Thanks.

Resources