How to implement event EventHandler ICommand.CanExecuteChanged.add? (C#) - c#

I was following this video on how to create a Windows.InputBinding for the Exit MenuItem:
https://www.youtube.com/watch?v=bdmVWGjpA_8
Here's my attempt, But I'm wondering how to implement:
event EventHandler ICommand.CanExecuteChanged add { }???
Seems this CanExecureChanged is a new requirement for ICommand Interface in DotNet 6 WPF??
Heres's part of my c# MainWindow.cs:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace WpfApp2
{
public class ExitKey : ICommand
{
private MainWindow mw;
public ExitKey(MainWindow mw0) { mw = mw0;}
event EventHandler ICommand.CanExecuteChanged {
add {
//How to Implment this???
throw new NotImplementedException();
}
remove {
throw new NotImplementedException();
}
}
bool ICommand.CanExecute(object parameter) {
return true;
}
void ICommand.Execute(object parameter) {
mw.menu1_file_exit.RaiseEvent(new
RoutedEventArgs(MenuItem.ClickEvent));
}
}
public class KeyContext
{
private MainWindow mw;
public KeyContext(MainWindow mw0) { mw = mw0; }
// {Binding MyExitCommand}
public ICommand MyExitCommand
{
get { return new ExitKey(mw); }
}
} //Class
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new KeyContext(this);
}
private void menu1_file_exit_Click(
object sender, RoutedEventArgs e)
{
MessageBox.Show("EXIT!");
}
} //Class
} //Namespace
<Window x:Class="WpfApp2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Garbo" Height="900" Width="900" MinHeight="240" MinWidth="320">
<Window.InputBindings>
<KeyBinding Key="F5"
Command="{Binding MyExitCommand}"
/>
</Window.InputBindings>
<DockPanel>
<Menu Name="menu1" DockPanel.Dock="Top">
<MenuItem Header="_File">
<MenuItem
Name="menu1_file_exit"
Header="E_xit"
Command="{Binding ExitCommand}"
InputGestureText="F5"
Click="menu1_file_exit_Click"/>
</MenuItem>
</Menu>
</DockPanel>
</Window>

You implement it by forwarding the calls to your own event handler.
However you don't really need to implement your own commands, there are plenty already done out there. The MVVM Community Toolkit has its RelayCommand and the one I use is ReactiveUI's ReactiveCommand.

In WPF you would usually delegate event subscribers to the CommandManager.RequerySuggested event:
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
This way the CommandManager will be responsible to raise the ICommand.CanExecuteChanged event implicitly.
The CommandManager would observe the UI interaction like mouse move and focus changes and will raise the CommandManager.RequerySuggested event to notify the ICommandSource, that usually listens to the ICommand.CanExecuteChanged event. The command source will then call ICommand.CanExecute.
You can trigger the CommandManager.RequerySuggested event (and therefore the ICommand.CanExecuteChanged event) explicitly by calling CommandManager.InvalidateRequerySuggested.
An alternative implementation is to bypass the CommandManager and allow the command target to raise this event explicitly:
public
public event EventHandler CanExecuteChanged;
public void InvalidateCommand() => this.CanExecuteChanged?.Invoke(this, EventArgs.Empty);
You can follow the Microsoft Docs: Relaying Command Logic example on how to implement a reusable RelayCommand. This example generally shows how to implement ICommand properly.
Note that in your case the ICommand ExitKey should be a RoutedCommand: How to: Create a RoutedCommand.
RoutedCommand also allows to register a KeyGesture with the command:
partial class MainWindow : Window
{
public static RoutedCommand MyExitCommand = new RoutedCommand();
public MainWindow()
{
InitializeComponent();
var exitCommandKeyGesture = new KeyGesture(Key.F5);
MyExitCommand.InputGestures.Add(exitCommandKeyGesture);
var commandBinding = new CommandBinding(MyExitCommand, ExecutedMyExitCommand, CanExecuteMyExitCommand);
this.CommandBindings.Add(commandBinding);
}
}
<Window>
<DockPanel>
<Menu Name="menu1" DockPanel.Dock="Top">
<MenuItem Header="_File">
<MenuItem x:Name="menu1_file_exit"
Header="E_xit"
Command="{x:Static local:MyExitCommand}"
InputGestureText="F5" />
</MenuItem>
</Menu>
</DockPanel>
</Window>

Related

Keep a MenuItem's IsChecked property synced with a bool

I have a simple WPF Application that has a menu on top. I want to add an option to make the main window to stay on top of other windows.
I created a bool named setTopMost in Property > Settings tab for users to save this setting. So, the setting will be remembered even after the app is terminated.
Everything is working as intended, I can click on the option or use the shortcut of Ctrl+T to make the window to stay on top, but I cannot get a check mark to appear next to the option when the window is on top of other windows.
I've read several articles regarding binding IsChecked to a bool, but I could not solve this problem on my own.
Here are my codes.
MainWindow.xaml
<Window.InputBindings>
<KeyBinding Gesture="Ctrl+T" Command="{Binding TopMostCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
</Window.InputBindings>
<MenuItem Header="_Options">
<MenuItem x:Name="Menu_AlwaysOnTop" Header="Always On _Top" IsCheckable="True" IsChecked="{Binding isTopMost}" Command="{Binding TopMostCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}" InputGestureText="Ctrl+T" />
</MenuItem>
MainWindow.xaml.cs
namespace WPF_Practice
{
public partial class MainWindow : Window
{
public bool isTopMost;
public MainWindow()
{
InitializeComponent();
DataContext = new PracticeDataContext();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
isTopMost = Properties.Settings.Default.setTopMost;
Topmost = Properties.Settings.Default.setTopMost;
}
}
public class PracticeDataContext
{
public ICommand TopMostCommand { get; } = new TopMostCommand();
}
public class TopMostCommand : ICommand
{
public void Execute(object parameter)
{
var TopMostClass = new MainWindow();
TopMostClass.WindowTopMost();
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
}
}
Please mind that I am doing this as a hobby and quite new to this.
The main reason why your MenuItem isn't updating properly is because you set the DataContext of the Window to PracticeDataContext.
public MainWindow()
{
InitializeComponent();
DataContext = new PracticeDataContext(); <--
}
This means that your bindings in MainWindow.xaml are going to be looking for properties in PracticeDataContext.
In this case you would want to have an IsTopMost property in your PracticeDataContext class in order for the binding to work.
Since IsTopMost isn't set until the Loaded event handler fires, you should implement INotifyPropertyChanged in your PracticeDataContext class so that your IsTopMost binding will get notified when it is set from settings.
A quick search on INotifyPropertyChanged will show you lots of examples. It's pretty easy.

Click-Event from MainWindow.xaml.cs in App.xaml

so i want to outsource some things from MainWindow.xaml to App.xaml like this for example :
<Application x:Class="SVGTesting.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type ContentControl}" x:Key="Test1">
<StackPanel Orientation="Horizontal">
<Button Content="Button1" Click="Button_Click" x:Name="Button1"/>
<Button Content="Button2" Click="Button_Click" x:Name="Button2"/>
</StackPanel>
</DataTemplate>
</ResourceDictionary>
</Application.Resources>
</Application>
In MainWindow.xaml then i have something like this
<ContentControl ContentTemplate="{StaticResource Test1}"/>
But now VS says that i cannot use the function "Button_Click" because its not in the codebehind from App.xaml. So how can i call this function from MainWindow in App.xaml?
Is there any way? I don't want answers like MVVM or Command. If it's not possible to solve then WPF is unfortunately useless for me.
Thanks and Greetings.
This is not the easiest thing to do as WPF expect things to be done in its own way. But there's few options, from easiest to hardest.
1. Don't do anything
Easiest way is to keep your data templates inside the MainWindow.xaml.
2. Use Commands instead of event handlers
You currently have event handlers defined like this:
<Button Content="Button1" Click="Button_Click"
"More-WPF way" of doing this would be to replace Click's event handler with a command with this quite cumbersome syntax:
<Button Content="Test" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.OnClickCommand}"></Button>
And then define the command in your MainWindow:
public ICommand OnButtonClick
{
get
{
return new Command(() =>
{
this.Text.Text = "Updated";
});
}
}
3. Define the event handlers in App.xaml.cs and use that to route the event handlers
I don't recommend this as it get tiresome to keep things synced but it's possible. Create and event handler in App.xaml.cs:
public partial class App : Application
{
private void Button_Click(object sender, RoutedEventArgs e)
{
}
}
Then use the sender to access the MainWindow instance and call it's method:
private void Button_Click(object sender, RoutedEventArgs e)
{
var mainWindow = (MainWindow)Window.GetWindow((DependencyObject)sender);
mainWindow.DoWork();
}
In my second example Command is defined like the following:
public class Command : ICommand
{
public delegate void ICommandOnExecute();
private ICommandOnExecute _execute;
public event EventHandler CanExecuteChanged;
public Command(ICommandOnExecute onExecuteMethod)
{
_execute = onExecuteMethod;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_execute?.Invoke();
}
}
You can't do it. See MSDN documentation for Code-Behind:
The event handlers you write must be instance methods defined by the
partial class within the namespace identified by x:Class. You cannot
qualify the name of an event handler to instruct a XAML processor to
look for that handler in a different class scope. You also cannot use
a static method as an event handler.
In WPF you can use a behaviors instead.
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<Button Content="btnWithBehavior">
<i:Interaction.Behaviors>
<local:HandleButtonClick/>
</i:Interaction.Behaviors>
</Button>
public class HandleButtonClick : Behavior<Button>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Click += AssociatedObject_Click; ;
}
private void AssociatedObject_Click(object sender, RoutedEventArgs e)
{
//Move your MainWindow.Button_Click here;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Click -= AssociatedObject_Click;
}
}

How to take a WPF CommandParameter to the ViewModel in MVVM?

I hope somebody can help me out here. Simplified the code for posting.
We have a main window (MvvmTestView) with a menu, and a 2nd window (SettingsView) which holds several tabs. I can open the SettingsView window alright. I can even select which Tab to open by setting this in the code.
How can I get back the correct value with the command parameter from the XAML code so that the correct tab opens?
MvvmTestView.xaml:
<Window x:Class="MvvmTest.Views.MvvmTestView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:MvvmTest.ViewModels"
WindowStartupLocation="CenterScreen"
Title="MvvmTestView"
Height="500"
Width="500">
<Window.DataContext>
<vm:MvvmTestViewModel/>
</Window.DataContext>
<Grid>
<DockPanel>
<Menu>
<MenuItem Header="Menu">
<MenuItem
Header="Tab01"
Command="{Binding SettingsViewCommand}"
CommandParameter="0"/>
<MenuItem
Header="Tab02"
Command="{Binding SettingsViewCommand}"
CommandParameter="1"/>
</MenuItem>
</Menu>
</DockPanel>
<DockPanel>
<Label Content="MainView" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</DockPanel>
</Grid>
</Window>
SettingView.xaml
<Window x:Class="MvvmTest.Views.SettingsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:tabData="clr-namespace:MvvmTest.Views"
xmlns:vm="clr-namespace:MvvmTest.ViewModels"
WindowStartupLocation="CenterScreen"
Title="SettingsView"
Height="400"
Width="400">
<Window.DataContext>
<vm:MvvmTestViewModel/>
</Window.DataContext>
<Grid>
<TabControl
SelectedIndex="{Binding SettingsSelectedIndex, Mode=TwoWay}">
<tabData:Tab01View/>
<tabData:Tab02View/>
</TabControl>
</Grid>
</Window>
SettingsViewModel.cs
using System.ComponentModel;
namespace MvvmTest.ViewModels
{
public class SettingsViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
private int _settingsSelectedIndex;
public int SettingsSelectedIndex
{
get
{
return _settingsSelectedIndex;
}
set
{
_settingsSelectedIndex = value;
OnPropertyChanged("SettingsSelectedIndex");
}
}
}
}
MvvmTestViewModel.cs
using MvvmTest.Commands;
using MvvmTest.Views;
using System.ComponentModel;
using System.Windows.Input;
namespace MvvmTest.ViewModels
{
internal class MvvmTestViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private SettingsViewModel SettingsViewModel;
public MvvmTestViewModel()
{
SettingsViewModel = new SettingsViewModel();
SettingsViewCommand = new SettingsViewCommand(this);
}
public ICommand SettingsViewCommand
{
get;
private set;
}
public void SettingsWindow()
{
SetIndex();
SettingsView settingsView = new SettingsView()
{
DataContext = SettingsViewModel
};
settingsView.ShowDialog();
}
public int SetIndex()
{
SettingsViewModel.SettingsSelectedIndex = 1;
return SettingsViewModel.SettingsSelectedIndex;
}
}
}
SettingsViewCommand.cs
using MvvmTest.ViewModels;
using System;
using System.Windows.Input;
namespace MvvmTest.Commands
{
internal class SettingsViewCommand : ICommand
{
private MvvmTestViewModel settingsViewModel;
public SettingsViewCommand(MvvmTestViewModel settingsViewModel)
{
this.settingsViewModel = settingsViewModel;
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
settingsViewModel.SettingsWindow();
}
}
}
I suggest to avoid creating multiple command classes like SettingsViewCommand : ICommand. Instead use some general-purpose command class (e.g. RelayCommand from MvvmFoundation NuGet package)
assuming you added MvvmFoundation to your project, refactor MvvmTestViewModel class like this:
internal class MvvmTestViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private SettingsViewModel SettingsViewModel;
public MvvmTestViewModel()
{
SettingsViewModel = new SettingsViewModel();
SettingsViewCommand = new RelayCommand<int>(SettingsWindow);
}
public ICommand SettingsViewCommand
{
get;
private set;
}
public void SettingsWindow(int index)
{
SettingsViewModel.SettingsSelectedIndex = index;
SettingsView settingsView = new SettingsView()
{
DataContext = SettingsViewModel
};
settingsView.ShowDialog();
}
}
CommandParameter from a view is passed to SettingsWindow method in a viewModel and used to change selected index

Access methods from the view in the viewmodel

I'm making a simple WPF program that reads from the clipboard. The program will have a notification icon with a context menu. And now for my question.
How can I call the methods CloseCBViewer(), InitCBViewer() from MainWindow from NotifyIconViewModel, so I can use them in ExitCommand to disconnect from clipboard and add a disconnect and connect options in a context menu.
For the system tray icons I'm using this: http://www.codeproject.com/Articles/36468/WPF-NotifyIcon
NotifyIconViewModel.cs
public class NotifyIconViewModel
{
public ICommand ExitCommand
{
get
{
return new DelegateCommand
{
CommandAction = () =>
{
Application.Current.Shutdown();
}
};
}
}
}
public class DelegateCommand : ICommand
{
public Action CommandAction { get; set; }
public Func<bool> CanExecuteFunc { get; set; }
public void Execute(object parameter)
{
CommandAction();
}
public bool CanExecute(object parameter)
{
return CanExecuteFunc == null || CanExecuteFunc();
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
MainWindow.cs
public partial class MainWindow : Window
{
public void InitCBViewer()
{
WindowInteropHelper wih = new WindowInteropHelper(this);
hWndSource = HwndSource.FromHwnd(wih.Handle);
// start processing window messages
hWndSource.AddHook(this.WinProc);
// set this window as a viewer
hWndNextViewer = Win32.SetClipboardViewer(hWndSource.Handle);
}
enter code here
public void CloseCBViewer()
{
// remove this window from the clipboard viewer chain
Win32.ChangeClipboardChain(hWndSource.Handle, hWndNextViewer);
hWndNextViewer = IntPtr.Zero;
hWndSource.RemoveHook(this.WinProc);
}
}
ClipboardResources.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:tb="http://www.hardcodet.net/taskbar"
xmlns:local="clr-namespace:ClipboardTextChecker">
enter code here
<ContextMenu x:Shared="false" x:Key="SysTrayMenu">
<MenuItem Header="Show" Command="{Binding ShowWindowCommand}" />
<MenuItem Header="Hide" Command="{Binding HideWindowCommand}" />
<Separator/>
<MenuItem Header="Exit" Command="{Binding ExitCommand}" />
</ContextMenu>
<tb:TaskbarIcon x:Key="NotifyIcon"
ToolTipText="Double-click for show checker, right-click for menu"
DoubleClickCommand="{Binding ShowWindowCommand}"
ContextMenu="{StaticResource SysTrayMenu}">
<tb:TaskbarIcon.DataContext>
<local:NotifyIconViewModel/>
</tb:TaskbarIcon.DataContext>
</tb:TaskbarIcon>
</ResourceDictionary>
App.xaml
<Application x:Class="ClipboardTextChecker.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ClipboardTextChecker"
ShutdownMode="OnExplicitShutdown">
<Application.Resources>
enter code here
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ClipboardTextCheckerResources.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
Before I answer, just want to let you know that this breaks the rules of MVVM:
public ICommand ExitCommand
{
get
{
return new DelegateCommand
{
CommandAction = () =>
{
Application.Current.Shutdown(); // <-- Don't do this
}
};
}
}
}
Your view-models are not supposed to know anything about the UI. By accessing the Application directly, you're breaking the rules. I will leave that up to you to discover later.
To answer your question
Your application is the Application.Current. Assuming it's the default name, you can cast it like this:
var myApp = (App)Application.Current;
In your App.xaml.cs, you can create a public method that does everything for you.
public partial class App : Application
{
public void CustomShutdown()
{
// Not 100% sure if this is how you get access to your main window.
var main = (MainWindow)Window.Current;
main.CloseCBViewer(); // This is your custom shutdown.
Shutdown(); // This is equivalent to Application.Current.Shutdown()
}
}
And in your original ExitCommand:
public ICommand ExitCommand
{
get
{
return new DelegateCommand
{
CommandAction = () =>
{
((App)Application.Current).CustomShutdown();
}
};
}
}
}
So, the MVVM way of doing this is to make sure that your view-model doesn't have links to your view. The appropriate way to do this might be to introduce an Exit event in your VM, then when you create your VM in the MainWindow you attach an handler to that event, which then detaches your clipboard.
I would recommend that you use a decoupled publish-subscribe event methodology. There is one called the EventAggregator that ships with Microsoft's Prism.
Note that recently Microsoft open sourced Prism - don't be concerned about that specifically, the Event Aggregator pattern (and the EventAggregator library) is still the appropriate pattern to use here. This will enable you to define events, then separately and independantly register subscribers (listeners) for specific published events (the event aggregator acts as a broker and register for subscribers).

Toggle button command not executed

The problem is this. Let's say I have 3 toggle buttons and I want just one being checked at the time using Command. When one button is checked others should be disabled. (I don't want to use radio buttons).
So I created this simple code but the strange thing is, that when checked button is clicked commands Execute is not executed (no MessageBox is shown).
<Window x:Class="ToggleButtonsProblem.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">
<StackPanel>
<ToggleButton Command="{Binding ToggleCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}}">A</ToggleButton>
<ToggleButton Command="{Binding ToggleCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}}">B</ToggleButton>
<ToggleButton Command="{Binding ToggleCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}}">C</ToggleButton>
</StackPanel>
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Controls.Primitives;
namespace ToggleButtonsProblem {
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
this.DataContext = new ViewModel();
}
}
public class ViewModel {
public static ICommand ToggleCommand { get { return new ToggleCommand(); } }
}
public class ToggleCommand : ICommand {
public static bool isSomeChecked = false;
public static ToggleButton currentCheckedButton;
public bool CanExecute(object parameter) {
if (currentCheckedButton == null) return true;
return (parameter as ToggleButton).IsChecked == true;
}
public event EventHandler CanExecuteChanged {
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter) {
currentCheckedButton = null;
ToggleButton button = parameter as ToggleButton;
MessageBox.Show(button.IsChecked.ToString());
if (button.IsChecked == true) {
currentCheckedButton = button;
}
else {
currentCheckedButton = null;
}
}
}
}
Commands are executed only when button is pressed. You need to hook the Unchecked event of the ToggleButton, for example like this:
<ToggleButton Command="{Binding ToggleCommand}" Unchecked="ToggleButton_Unchecked" CommandParameter="{Binding RelativeSource={RelativeSource Self}}">A</ToggleButton>
And add method handler to the code-behind class:
public void ToggleButton_Unchecked(object sender, RoutedEventArgs e) {
(sender as ToggleButton).Command.Execute(sender);
}
This should work, perhaps you can find some prettier way of adding the method handler, maybe as a part of ToggleCommand class.
EDIT:
Try implementing your CanExecute() method like this:
public bool CanExecute(object parameter) {
if (currentCheckedButton == null) return true;
return currentCheckedButton == parameter;
}
For me it works. Here is what I think caused the problem: you click (uncheck) the button, so IsChecked changed to false. Then WPF attempts to invoke the Execute() method, but as always, calls CanExecute() first. However, CanExecute() returns false, because the check state has already been changed, so the Execute() methods is not invoked.
ToggleCommand should not be static. Try to define the command as a property.

Categories