Как упростить InputBindings в XAML?
Во-первых, это мой код:
<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>
В этом коде НЕТ ОШИБОК, поэтому он работает просто отлично. Но, похоже, что InputBindings
может получить много строк, если в моем приложении много InputBindings
,
Итак, можно ли их упростить/ сократить (каким-либо образом)? Это потому, что моему приложению потребуется много InputBindings
/ KeyBinding
Это комбинация модификаторов, и вы почувствуете, что ввод ее один за другим будет выглядеть "не аккуратно". M
Или, может быть, это единственный способ (с MVVM)?
Пожалуйста, уточните все, что нужно:D
На всякий случай, это связанные методы в классах Command и ViewModel:
public void Execute(object parameter)
{
Notes note;
if (Enum.TryParse(parameter.ToString(), out note))
{
_vm.AddOrUpdateNote(note, Keyboard.Modifiers);
}
else
{
...
}
}
Часть моей ViewModel:
public void AddOrUpdateNote(Notes note, ModifierKeys mKeys)
{
if (mKeys == ModifierKeys.Control)
{
...
}
else if (mKeys == ModifierKeys.Shift)
{
...
}
else
{
...
}
}
Таким образом, есть небольшая разница в поведении в зависимости от того, какая клавиша-модификатор нажата. (Расщепление на разные методы мне ужасно)
ОБНОВЛЕНИЕ: я прочитал некоторые объяснения на InputGestures
, В http://msdn.microsoft.com/en-us/library/ms752308%28v=vs.110%29.aspx сказано this.InputBindings.Add(Blabla)
(из xaml.cs, я думаю), это можно сделать во ViewModel? Или это строго необходимо сделать с помощью XAML, поэтому, если в моем приложении много комбинаций клавиш, например, в приведенном выше примере, это все равно нужно сделать таким "длинным" способом?
Пожалуйста, предоставьте несколько примеров кода, если это возможно, чтобы я мог понять это лучше. Спасибо
(Не совсем уверен, как спросить это, поэтому, пожалуйста, не стесняйтесь уточнить)
2 ответа
Если ваша единственная цель - сократить количество ручных привязок клавиш, вы можете сделать что-то вроде этого:
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});
]
Но мне не нравится этот подход, так как он требует от вас осуществления делегирования вашей команды, а не WPF. Вам нужно будет включить CommandParameter и клавишу-модификатор.
Все вышеперечисленное будет работать, но выполнение всей команды одной командой кажется мне нелогичным. Я бы реализовал несколько команд (например, AddNoteCommand), чтобы каждая из них могла содержать свои собственные методы Execute и CanExecute.
Да, у вас будет несколько дополнительных строк кода на команду, но ваша реализация команды не должна знать, КАК вы получили доступ к команде. Представьте, что вы хотите добавить эти команды как MenuItems или Кнопки в вашем пользовательском интерфейсе.
Может быть, я чего-то упускаю (я не знаю, что делают команды), но я не могу себе представить сценарий, в котором я бы выбрал этот подход.
Это не обязательно делает это неправильно, хотя;)
На самом деле есть трюк, который вы можете использовать для прикрепленных свойств. Синтаксис для применения прикрепленного свойства на самом деле является просто «синтаксическим сахаром» для вызова статической функции для определенного класса из XAML. Зная это, вы можете создать класс «Утилиты», который имеет набор методов, которые вы можете вызывать из XAML, используя синтаксис прикрепленного свойства.
Таким образом, вы можете сделать что-то вроде этого...
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.
}
}
}
а можно так использовать...
<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>
Заметки:
Имя метода для поддержки прикрепленных свойств — статический метод с именем
Set[PropertyName]
на «владеющем» классе. Когда вы используете его в XAML, это выглядит такOwningClass.PropertyName="bla"
. В моем примере, вызывая классUtil
и статический методSetSet
"имя свойства" становитсяSet
и, таким образом, в XAML (где «мой» — импортированное значение xmlns) это выглядит так:my:Util.Set="bla"
что более ясно говорит о моих намерениях.Я предпочитаю тип данных
string
поэтому вы можете передать массу информации в одном значении, а затем просто проанализировать ее по своему усмотрению. Но вы можете использовать любой тип данных, какой захотите. Например, вы можете взятьCommandInfo
класс, который раскрывает вашModifiers
,Key
а такжеParameter
свойства, затем создайтеMarkupExtension
чтобы «создать» этот класс и принять их в качестве параметров конструктора, а затем установить его с помощью синтаксиса прикрепленного свойства, например:
<InputBinding my:Util.Set="{my:CommandInfo Control, D1, N1}" ... />
Этот синтаксис вложенных свойств оказался довольно удивительным в упрощении нашего XAML. Например, я использую эти методы, чтобы упростить настройку макетов сетки. Например, вместо этого...
<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>
Теперь я могу просто сделать это...
<Grid is:GridUtil.Layout="Auto,Auto,Auto|Auto,*">
<TextBlock is:GridUtil.Cell="1,1s2" />
</Grid>
Заметки:
- Макет представляет собой список значений высоты строк, разделенных запятыми, вертикальную черту, а затем список значений ширины столбцов, разделенных запятыми.
- Ячейка - это строка (и, возможно, диапазон строк с ' N '), запятая, затем столбец (и, возможно, диапазон столбцов с ' N '). Надеюсь, это поможет!