In WPF Skinning, I was using old school MenuItem_Click event handling. WPF has a more powerful commanding system. To implement the same functionality with WPF command, first create a command in Window1:
public static readonly RoutedCommand ChangeSkin = new RoutedCommand(
"ChangeSkin", typeof(Window1));
Bind the menu items to be sources of this command. Each binds to the same command with a different parameter. Now MenuItem_Click event handling can be removed and IsChecked binding can be one-way. I’ll use the command to change skin and only need menu items to display correct check marks.
<MenuItem Header="_Default"
Command="local:Window1.ChangeSkin" CommandParameter="Default"
IsChecked="{Binding Mode=OneWay, ElementName=mainWindow, Path=Skin,
Converter={StaticResource skinEquals},
ConverterParameter=Default}"/>
<MenuItem Header="_Green"
Command="local:Window1.ChangeSkin" CommandParameter="Green"
IsChecked="{Binding Mode=OneWay, ElementName=mainWindow, Path=Skin,
Converter={StaticResource skinEquals},
ConverterParameter=Green}"/>
<MenuItem Header="_Red"
Command="local:Window1.ChangeSkin" CommandParameter="Red"
IsChecked="{Binding Mode=OneWay, ElementName=mainWindow, Path=Skin,
Converter={StaticResource skinEquals},
ConverterParameter=Red}"/>
The command will bubble up until being handled. Create a command binding at the main window:
<Window.CommandBindings>
<CommandBinding Command="local:Window1.ChangeSkin" Executed="ChangeSkin_Executed" />
</Window.CommandBindings>
Fill the command handling code:
private void ChangeSkin_Executed(object sender, ExecutedRoutedEventArgs e)
{
Skin = (string)e.Parameter;
e.Handled = true;
}
Now this should work the same as previous version.
The menu items do not have keyboard short-cuts (KeyGestures) yet. To make keyboard inputs invoke this command, add key bindings:
<Window.InputBindings>
<KeyBinding Command="local:Window1.ChangeSkin" CommandParameter="Default"
Key="D" Modifiers="Ctrl" />
<KeyBinding Command="local:Window1.ChangeSkin" CommandParameter="Green"
Key="G" Modifiers="Ctrl" />
<KeyBinding Command="local:Window1.ChangeSkin" CommandParameter="Red"
Key="R" Modifiers="Ctrl" />
</Window.InputBindings>
This makes the key gestures work, but they don’t show up in the menu items. They need to be specified in menu items, e.g.:
<MenuItem Header="_Green"
Command="local:Window1.ChangeSkin" CommandParameter="Green"
InputGestureText="Ctrl+G"
IsChecked="{Binding Mode=OneWay, ElementName=mainWindow, Path=Skin,
Converter={StaticResource skinEquals},
ConverterParameter=Green}"/>
Alternatively, I can create 3 different commands and omit the above InputGestureText and even Header text:
public static readonly RoutedCommand DefaultSkin = new RoutedUICommand(
"_Default", "Default", typeof(Window1),
new InputGestureCollection(
new KeyGesture[] { new KeyGesture(Key.D, ModifierKeys.Control) }));
public static readonly RoutedCommand GreenSkin = new RoutedUICommand(
"_Green", "Green", typeof(Window1),
new InputGestureCollection(
new KeyGesture[] { new KeyGesture(Key.G, ModifierKeys.Control) }));
public static readonly RoutedCommand RedSkin = new RoutedUICommand(
"_Red", "Red", typeof(Window1),
new InputGestureCollection(
new KeyGesture[] { new KeyGesture(Key.R, ModifierKeys.Control) }));
private void ChangeSkin_Executed(object sender, ExecutedRoutedEventArgs e)
{
Skin = ((RoutedUICommand)e.Command).Name;
e.Handled = true;
}
This time UI xaml is much cleaner:
<Window.CommandBindings>
<CommandBinding Command="local:Window1.DefaultSkin" Executed="ChangeSkin_Executed" />
<CommandBinding Command="local:Window1.GreenSkin" Executed="ChangeSkin_Executed" />
<CommandBinding Command="local:Window1.RedSkin" Executed="ChangeSkin_Executed" />
</Window.CommandBindings>
<DockPanel x:Name="framePanel" LastChildFill="True">
<Menu IsMainMenu="True" DockPanel.Dock="Top">
<MenuItem Header="_Skins">
<MenuItem Command="local:Window1.DefaultSkin"
IsChecked="{Binding Mode=OneWay, ElementName=mainWindow, Path=Skin,
Converter={StaticResource skinEquals}, ConverterParameter=Default}"/>
<MenuItem Command="local:Window1.GreenSkin"
IsChecked="{Binding Mode=OneWay, ElementName=mainWindow, Path=Skin,
Converter={StaticResource skinEquals}, ConverterParameter=Green}"/>
<MenuItem Command="local:Window1.RedSkin"
IsChecked="{Binding Mode=OneWay, ElementName=mainWindow, Path=Skin,
Converter={StaticResource skinEquals}, ConverterParameter=Red}"/>
</MenuItem>
</Menu>
<Grid Background="{DynamicResource bgbrush}">
<TextBlock
Text="{Binding ElementName=mainWindow, Path=Skin}"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</DockPanel>