views:

24

answers:

1

I've created a ToggleButton to show and hide a piece of UI and I've sent its Content to an icon.

I now want to add a shortcut key to the ToggleButton but I'm unsure how to do with without binding a command as well. Since all I am doing is binding to the IsChecked state, I don't need a command to do any other functionality and creating an empty one seems incorrect.

Here is my ToggleButton as it stands currently non-functional and not responding when I press the indicated shortcut key.

<ToggleButton ToolTip="Command History"
              MinWidth="24"
              IsChecked="{Binding IsShowHistoryChecked}"
              Margin="7">
    <ToggleButton.InputBindings>
        <KeyBinding Gesture="Ctrl+H" />
    </ToggleButton.InputBindings>
    <Image Source="/Amuse;component/Images/ComHistory256.png"
           Width="24" />
</ToggleButton>
+1  A: 

An InputBinding on the ToggleButton itself won't do the trick, but there are two good WPF solutions for your problem:

  1. Use a RoutedCommand that updates the model.
  2. Register an access key.

Why your InputBinding solution won't work

The InputBinding you have defined currently won't work because it doesn't list a command. It is easy to create a command that toggles a button, as follows:

public void Execute(object parameter)
{
  ((ToggleButton)parameter).IsChecked = !((ToggleButton)parameter).IsChecked;
}

However this will not achieve what you are looking for. You want Ctrl-H to toggle your button even when the button is not focused. An InputBinding will not accomplish this for you, since it only works when the button has focus. I will now discuss two solutions you can use.

Option 1: Use a RoutedCommand that updates the model

The whole point of WPF's architecture is that you never will need to "toggle a button" in the first place: Conceptually all keyboard and mouse actions in WPF should serve to toggle a bound property in your model or view model. The ToggleButton then just becomes the mechanism for accepting mouse clicks, but need not be the only one.

The name you chose for your "IsShowHistoryChecked" property indicates a fundamental problem in the way you're conceptualizing your view model. Your view model should not be designed around the view - rather, it should expose logical concepts such as a "ShowHistory" property. The view may bind this to a CheckBox or ToggleButton, or it may choose some other mechanism, or it may not expose it at all. The whole point of data binding and view models is that when you create the view model you don't care what the actual view will be like. In fact, during automated unit testing there will be no checkbox so "IsShowHistoryChecked" would clearly be a real misnomer.

So let's say you've properly separated your view from your view model and you have a "ShowHistory" property. First implement a "ToggleShowHistory" command in your view model that, when executed, toggles the ShowHistory property. Now all you have to do is assign this command an InputBinding of Ctrl-H at the view level and you're done. Even if the ToggleButton is removed from the view entirely the InputBinding will still take effect and Ctrl-H will still work. Nirvanna.

Option 2: Register an access key

Windows has a standard mechanism for associating keys with arbitrary buttons and labels, which is the "access key" concept. If you register an access key of "h" on the ToggleButton, pressing Alt-H will toggle the button, and so will just plain H if you don't have a TextBox or another control accept it first.

It is very simple to register the access key in code:

AccessKeyManager.Register("h", togleButton);

This registers "h" as the access text. Now if the user presses Alt-H anywhere in scope (or plain "h" if isn't handled by a TextBox), your button will toggle.

You can also do it in XAML. If you're showing text in your button, just use an underline before the access key letter:

<Button Text="Show _History" ... />

If you're showing something other than just text, include a hidden AccessText element in your button content:

<Button ...>
  <Grid>
    <AccessText Text="_h" Visibility="Collapsed" />
    <Image ...>
  </Grid>
</Button>

In case you're wondering, WPF has no built in mechanism to request that AccessKey registrations respond to Ctrl instead of Alt, so this will not allow you to set Ctrl-H as the access key.

Ray Burns