Как привязать жесты клавиш в Caliburn.Micro?

Как я могу заставить Caliburn.Micro сопоставить ключевой жест с методом действия в моей ViewModel?

Например, я хочу реализовать интерфейс с вкладками, и я хочу, чтобы моя ShellViewModel имела метод NewTab, который пользователь должен иметь возможность вызывать, нажимая Ctrl+T на клавиатуре.

Я знаю, что полный фреймворк Caliburn поддерживает жесты, но как это сделать с помощью Caliburn.Micro? Возможно, есть какой-то способ связать действие с RoutedCommand (поскольку RoutedCommands уже поддерживает жесты ввода)? Или какой-то другой способ получить поддержку жестов?


person Joe White    schedule 15.11.2010    source источник


Ответы (5)


Вы можете сделать это, наследуя от System.Windows.Interactivity.TriggerBase. Вот пример.

person Felice Pollano    schedule 02.05.2011
comment
Я так и не заставил это работать, но снова начал смотреть на это, потому что мне это действительно нужно сейчас. Мне нужно сфокусировать элемент меню, чтобы жест сработал. Я хочу, чтобы он срабатывал, если окно сфокусировано, как я могу это исправить? Спасибо - person Anders; 30.10.2012
comment
Если поместить логику примера в главное окно, он заморозит весь вид! - person ender; 17.02.2014
comment
Этот обходной путь на самом деле не работает, если подумать. Например, когда вы присоединяете команду к «MenuItem», и эта команда имеет жест (ярлык), тогда WPF покажет комбинацию клавиш справа (после текста меню). - person JobaDiniz; 23.07.2016

Я изменил пример, чтобы включить поддержку глобальных привязок клавиш. Вам просто нужно добавить следующий код в ваше представление:

<i:Interaction.Triggers>
        <common:InputBindingTrigger>
            <common:InputBindingTrigger.InputBinding>
                <KeyBinding Modifiers="Control" Key="D"/>
            </common:InputBindingTrigger.InputBinding>
            <cl:ActionMessage MethodName="DoTheMagic"/>
        </common:InputBindingTrigger>
    </i:Interaction.Triggers>

И всякий раз, когда нажимается Ctr+D, будет выполняться метод DoTheMagic. Вот измененный код InputBindingTrigger:

public class InputBindingTrigger : TriggerBase<FrameworkElement>, ICommand
  {
    public static readonly DependencyProperty InputBindingProperty =
      DependencyProperty.Register("InputBinding", typeof (InputBinding)
        , typeof (InputBindingTrigger)
        , new UIPropertyMetadata(null));

    public InputBinding InputBinding
    {
      get { return (InputBinding) GetValue(InputBindingProperty); }
      set { SetValue(InputBindingProperty, value); }
    }

    public event EventHandler CanExecuteChanged = delegate { };

    public bool CanExecute(object parameter)
    {
      // action is anyway blocked by Caliburn at the invoke level
      return true;
    }

    public void Execute(object parameter)
    {
      InvokeActions(parameter);
    }

    protected override void OnAttached()
    {
      if (InputBinding != null)
      {
        InputBinding.Command = this;        
        AssociatedObject.Loaded += delegate {
          var window = GetWindow(AssociatedObject);
          window.InputBindings.Add(InputBinding);
        };
      }
      base.OnAttached();
    }

    private Window GetWindow(FrameworkElement frameworkElement)
    {
      if (frameworkElement is Window)
        return frameworkElement as Window;

      var parent = frameworkElement.Parent as FrameworkElement;      
      Debug.Assert(parent != null);

      return GetWindow(parent);
    }
  }
person Gregor Slavec    schedule 23.11.2011
comment
// действие все равно заблокировано Caliburn на уровне вызова Это неверно, оно все равно срабатывает - person Anders; 02.04.2012
comment
да, это срабатывает, у вас есть идеи, как вы могли бы проверить соответствующий метод Can, если действие разрешено срабатывать. - person Gregor Slavec; 06.05.2012
comment
Нет, я пытался отладить ваш код, чтобы найти ссылку на свойство Can, но безуспешно:/ - person Anders; 06.05.2012
comment
то же, что и принятый ответ: этот подход быстро отключит UserControl. - person ender; 17.02.2014

Механизм действий Caliburn.Micro построен на основе System.Windows.Interactivity. Таким образом, вы можете создать собственный триггер на основе TriggerBase, чтобы делать все, что вы хотите, включая глобальные жесты клавиатуры. Затем просто подключите ActionMessage к вашему триггеру и альт!

person EisenbergEffect    schedule 15.11.2010
comment
Я ничего не знаю о TriggerBase или о том, как он может быть связан с InputGesture... с чего бы мне вообще начать? - person Joe White; 15.11.2010
comment
System.Windows.Interactivity является частью Blend SDK. Если вы поищите в Blend Behaviors, то найдете много блогов с примерами. - person EisenbergEffect; 16.11.2010
comment
Эй, EisenbergEffect, есть ли способ масштабировать это? Я заметил, что когда я делаю это в пользовательском элементе управления, это также запускает вещи в родительском окне, если у меня есть такая же привязка ключа. - person Haacked; 25.09.2012
comment
@Haacked - я поместил триггеры в пользовательский элемент управления, и он направил ActionMessage на правильную виртуальную машину в элементе управления DataContext. - person codekaizen; 29.12.2012
comment
@EisenbergEffect Привет, есть ли у вашей команды планы на будущее по включению базовой поддержки действий с клавиатуры? - person ender; 17.02.2014

Наследуйте от ActionMessage Caliburn (который является TriggerAction), присоедините производный триггер к событию KeyDown в XAML и задайте свойство ActionMessage.MethodName. Добавьте в производный триггер свойство, определяющее искомую комбинацию клавиш, и переопределите метод Invoke для фильтрации по этой комбинации клавиш, вызывая base.Invoke(...), если клавиша совпадает.

person foson    schedule 08.07.2011

Если вы маршалируете команду через представление в модель представления, вы можете управлять CanExecute из модели представления. Я использовал этот метод в нескольких проектах Caliburn. Возможно, это не так «гладко», как использование интерактивности, но CanExecute работает.

<UserControl x:Class="MyView"
      ...
      Name="View"
>

  <UserControl.InputBindings>
    <KeyBinding Key="F5" 
                Command="{Binding RefreshCommand, ElementName=View, Mode=OneWay}" />
  </UserControl.InputBindings>

  <Button Command="{Binding Path=RefreshCommand, ElementName=View, Mode=OneWay}"/>

В своем классе представления вы связываете команду с моделью представления, на которую ссылается свойство MyView.DataContext.

Class MyView

    Public Property RefreshCommand As _
    New RelayCommand(AddressOf Refresh,
                     Function()
                         If ViewModel Is Nothing Then
                             Return False
                         Else
                             Return ViewModel.CanRefresh
                         End If
                     End Function)

    Private Sub Refresh()
        ViewModel.Refresh()
    End Sub

    Private ReadOnly Property ViewModel As MyViewModel
        Get
            Return DirectCast(DataContext, MyViewModel)
        End Get
    End Property

End Class
person bdeem    schedule 21.04.2016