WPF ICommand over a button - wpf

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.

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.

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

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.
}
}

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.

CanExecute for KeyBindings to control

I have a reportViewer with default toolbar button for decrease zoom binded to command NavigationCommands.DecreaseZoom. I want to disable it in some situation so i bind CanExecute method to return false for that command which works perfectly fine and disable the button as expected. But, still zoom out works if i use shortcut key "Ctrl + Subtract key". I tried to set KeyBinding to the same command assuming CanExecute will work but it doesn't. Since, CanExecute is not provided in KeyBinding. Can someone suggest how can i disable KeyGesture "Ctrl -" for some situation(logic in CanExecute) and not permanently.
Relevant code -
<DocumentViewer Name="documentViewer1"
Margin="0,0,0,30"
Style="{DynamicResource DocumentViewerStyle1}">
<DocumentViewer.CommandBindings>
<CommandBinding Command="NavigationCommands.DecreaseZoom"
CanExecute="DecreaseZoom_CanExecute" />
</DocumentViewer.CommandBindings>
<DocumentViewer.InputBindings>
<KeyBinding Command="NavigationCommands.DecreaseZoom"
Key="OemMinus"
Modifiers="Control" />
</DocumentViewer.InputBindings>
</DocumentViewer>
Code behind -
private void DecreaseZoom_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
if (((DocumentViewer)e.Source).PageViews.Count >= 3)
{
e.CanExecute = false;
e.ContinueRouting = false;
e.Handled = true;
}
}
You can either create your custom command for this or you can create your own InputGesture, and override its behavior,
<KeyBinding.Gesture>
<CustomInputGesture/>
</KeyBinding.Gesture>
I solved my problem extending DocumentViewer and overriding method OnDecreaseZoomCommand. I tried using Custom Command but its event handler is not getting hit in case i use shortcut key "Ctrl -". But this works for me -
public class ExtendedDocumentViewer : DocumentViewer
{
protected override void OnDecreaseZoomCommand()
{
if (PageViews.Count < 3)
{
base.OnDecreaseZoomCommand();
}
}
}

How to force binding evaluation every time a ContextMenu is opened?

I have a ContextMenu with a MenuItem in it:
<Grid>
<Button Content="{Binding Test}">
<Button.ContextMenu>
<ContextMenu>
<StackPanel>
<MenuItem Header="{Binding Test}"/>
</StackPanel>
</ContextMenu>
</Button.ContextMenu>
</Button>
</Grid>
The Test property looks like the following:
private Random rand;
public string Test
{
get
{
return "Test " + this.rand.Next(50);
}
}
When I right click the button, I have, for instance "Test 41". Next times I open the menu I have the same value. Is there a way to force the Menu to evaluate the binding each time ? (and then having "Test 3", "Test 45", "Test 65"...
Here is a hack i use in the same situation:
Name your context menu and create your own RoutedCommand, i use these for all buttons and menus as they have a CanExecute method which enables or disables the control and an Execute method that gets called to do the work. every time a context menu opens the CanExecute method gets called. that means you can do custom processing to see if it should be enabled, or you can change the contents of the menu, good for changing menu's when saving different things. we use it to say, Save xyx.. when the user is editing an xyx.
anyway if the menu is named you can modify its content on the CanExecute. (if the command originates on the menu you will have it as the sender of the event CanExecute anyway, but sometimes i like to scope them higher as you can assign keyboard shortcuts to them which can be executed from anywhere they are scoped.)
Your Test property needs to inform other components whenever its value changes, e.g. by implementing the INotifyPropertyChanged interface in the containing class like this:
class Window1 : Window, INotifyPropertyChanged {
...
private string m_Test;
public string Test {
get {
return m_Test;
}
set {
m_Test = value;
OnPropertyChanged("Test");
}
}
}
You can then modify the value of Test from anywhere by using the property (Test = "newValue";) and the changes will be reflected on the UI.
If you really need to change the value of the property when the ContextMenu is shown, use the Opend event of the ContextMenu:
Xaml:
<ContextMenu Opened="UpdateTest">
<MenuItem Header="{Binding Test}" />
</ContextMenu>
Code-behind:
private void UpdateTest(object sender, RoutedEventArgs e) {
// just assign a new value to the property,
// UI will be notified automatically
Test = "Test " + this.rand.Next(50);
}

Resources