简体   繁体   中英

How to simplify InputBindings in XAML?

First, this is my code :

<Window ...>
<Window.Resources>

</Window.Resources>
<Window.InputBindings>
    <KeyBinding Key="Space" Command="{Binding KeyboardCommand}" CommandParameter="Space"/>
    <KeyBinding Key="OemPeriod" Command="{Binding KeyboardCommand}" CommandParameter="Blank"/>
    <KeyBinding Key="D0" Command="{Binding KeyboardCommand}" CommandParameter="Rest"/>
    <KeyBinding Key="D1" Command="{Binding KeyboardCommand}" CommandParameter="N1"/>
    <KeyBinding Key="D2" Command="{Binding KeyboardCommand}" CommandParameter="N2"/>
    <KeyBinding Key="D3" Command="{Binding KeyboardCommand}" CommandParameter="N3"/>
    <KeyBinding Key="D4" Command="{Binding KeyboardCommand}" CommandParameter="N4"/>
    <KeyBinding Key="D5" Command="{Binding KeyboardCommand}" CommandParameter="N5"/>
    <KeyBinding Key="D6" Command="{Binding KeyboardCommand}" CommandParameter="N6"/>
    <KeyBinding Key="D7" Command="{Binding KeyboardCommand}" CommandParameter="N7"/>

    <KeyBinding Modifiers="Control" Key="Space" Command="{Binding KeyboardCommand}" CommandParameter="Space"/>
    <KeyBinding Modifiers="Control" Key="OemPeriod" Command="{Binding KeyboardCommand}" CommandParameter="Blank"/>
    <KeyBinding Modifiers="Control" Key="D0" Command="{Binding KeyboardCommand}" CommandParameter="Rest"/>
    <KeyBinding Modifiers="Control" Key="D1" Command="{Binding KeyboardCommand}" CommandParameter="N1"/>
    <KeyBinding Modifiers="Control" Key="D2" Command="{Binding KeyboardCommand}" CommandParameter="N2"/>
    <KeyBinding Modifiers="Control" Key="D3" Command="{Binding KeyboardCommand}" CommandParameter="N3"/>
    <KeyBinding Modifiers="Control" Key="D4" Command="{Binding KeyboardCommand}" CommandParameter="N4"/>
    <KeyBinding Modifiers="Control" Key="D5" Command="{Binding KeyboardCommand}" CommandParameter="N5"/>
    <KeyBinding Modifiers="Control" Key="D6" Command="{Binding KeyboardCommand}" CommandParameter="N6"/>
    <KeyBinding Modifiers="Control" Key="D7" Command="{Binding KeyboardCommand}" CommandParameter="N7"/>

    ...
</Window.InputBindings>
<Grid>
    ...
</Grid>

There's NO ERROR on those code, so it works just fine . But, it seems that the InputBindings can get a lot of lines if my application has a lot of InputBindings .

So, is it possible to simplify /shorten them (in any way)? It is because my application will need a lot of InputBindings / KeyBinding 's Modifier combination, and it feel input it one-by-one will look "not neat". M

Or maybe it is the only way to do (with MVVM)?

Please clarify anything needed :D


Just in case needed, these are the related methods in Command and ViewModel class :

public void Execute(object parameter)
{
    Notes note;
    if (Enum.TryParse(parameter.ToString(), out note))
    {
        _vm.AddOrUpdateNote(note, Keyboard.Modifiers);
    }
    else
    {
        ...
    }
}

A part of my ViewModel :

public void AddOrUpdateNote(Notes note, ModifierKeys mKeys)
{
    if (mKeys == ModifierKeys.Control)
    {
        ...
    }
    else if (mKeys == ModifierKeys.Shift)
    {
        ...
    }
    else
    {
        ...
    }
}

So, there's a minor behavior difference depending which Modifier Key is pressed. (Splitting into different methods feels awful for me)


UPDATE : I've read some explanations on InputGestures . In http://msdn.microsoft.com/en-us/library/ms752308%28v=vs.110%29.aspx it's said this.InputBindings.Add(Blabla) (from xaml.cs i guess), can it be done in the ViewModel? Or is it strictly need to be done via XAML, so, if I have a lot of key combination in my application such in the example above, it still need to be done that "long" way?

Please provide some code samples if possible, so I can understand it better. Thanks

(Not quite sure too how to ask it, so please feel free to clarify)

If your sole objective is to cut down on the number of manual KeyBindings, you can do something like this:

var keys = new List<Key> { Key.Space, Key.OemPeriod, Key.D1, [...]};
foreach(var key in keys)
{
     this.InputBindings.Add(new KeyBinding() { Command = MyCommandClass.KeyboardCommand, CommandParameter = key});
]

But I don't like this approach, as it requires you to implement delegation of your command yourself rather than having WPF do it. You will need to switch on the CommandParameter and modifierkey.

The above will absolutely work, but having a single command do all the work seems counterintuitive to me. I would implement several commands (like AddNoteCommand) so that they can each hold their own Execute and CanExecute method.

Yes, you'd have a few extra lines of code per Command, but your command implementation would not have to know HOW you accessed the command. Imagine you want to add these commands as MenuItems, or Buttons in your UI.

There might be something I'm missing (I don't know what the commands do), but I can't really imagine a scenario where I would choose this approach.

That doesn't necessarily make it wrong though ;)

There's actually a trick you can use around attached properties. The syntax for applying an attached property is really just 'syntactical sugar' to call a static function on a particular class from XAML. Knowing that, you can build up a 'Utility' class that has a bunch of methods you can call from XAML using the attached property syntax.

As such, you could do something like this...

public namespace My{

    public static class Util {

        [AttachedPropertyBrowsableForType(typeof(KeyBinding))]
        public static void SetSet(KeyBinding keyBinding, string bindingData){

            // Add code here to parse the string and do with it what you will.
            // For instance, you can split it on a pipe character to get the modifier,
            // parameter and key, then just apply it to the passed-in keyBinding here.
            // I'll leave this part as an exercise for you to research.
        }
    }
}

and you can use it like this...

<Window ...>
    <Window.InputBindings>
        <KeyBinding my:Util.Set="Control|D1|N1" />
        <KeyBinding my:Util.Set="Control|D2|N2" />
        <KeyBinding my:Util.Set="Control|D3|N3" />

        ...

    </Window.InputBindings>
</Window>

Notes:

  • The method name to support attached properties is a static method named Set[PropertyName] on an 'owning' class. When you use it in XAML, it looks like OwningClass.PropertyName="bla" . In my example, by calling the class Util and the static method SetSet the 'property name' becomes Set and thus in XAML (where 'my' is the imported xmlns value), it looks like my:Util.Set="bla" which reads more clearly as to my intent.

  • I prefer the data type string so you can pass in a ton of info in one value, then just parse it as you see fit. But you can use any data type you want. For instance, you could take a CommandInfo class that exposes your Modifiers , Key and Parameter properties, then create a MarkupExtension to 'create' that class and which takes them in as constructor parameters, then set it via the attached property syntax, like so:

<InputBinding my:Util.Set="{my:CommandInfo Control, D1, N1}" ... />

This attached property syntax has proven pretty amazing in simplifying our XAML. As an example, I use these techniques to more easily configure my grid layouts. For instance, instead of this...

<Grid>

    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>

    <TextBlock Grid.Row="1" Grid.Column="1", Grid.ColumnSpan="2" />

</Grid>

I can now just do this...

<Grid is:GridUtil.Layout="Auto,Auto,Auto|Auto,*">

    <TextBlock is:GridUtil.Cell="1,1s2" />

</Grid>

Notes:

  • Layout is a comma-separated list of row heights, a pipe character, then comma-separated list of column widths
  • Cell is a row (and possibly row-span with 's N '), a comma, then a column (and possibly column-span with 's N ') Hope this helps!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM