.net 处理用户控件(MVVM)中的输入手势

thtygnil  于 2023-02-10  发布在  .NET
关注(0)|答案(3)|浏览(127)

使用MVVM模式,我如何处理按键手势?
UserControl。InputBindings无法工作,因为它不可聚焦。
我已经定义了一个ICommand,当键入正确的键时应该调用它,但是我不知道如何将该命令与视图连接起来。
谢谢Stefan

r1zk6ea1

r1zk6ea11#

我通过创建一个DelegateCommand类解决了这个问题,它看起来和RelayCommand(见Josh Smith)完全一样,只是它允许更新回调。

public class DelegateCommand : ICommand
{
    Action<object> _execute;
    Predicate<object> _canExecute;

    #region Constructors

    public DelegateCommand()
    {

    }

    public DelegateCommand(Action<object> execute)
        : this(execute, null)
    {
    }

    public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

        _execute = execute;
        _canExecute = canExecute;
    }
    #endregion // Constructors

    public void Delegate(Action<object> execute)
    {
        _execute = execute;
    }

    public void Delegate(Action<object> execute, Predicate<object> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    #region ICommand Members

    [DebuggerStepThrough]
    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? _execute != null : _canExecute(parameter);
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void Execute(object parameter)
    {
        if (CanExecute(parameter))
            _execute(parameter);
    }

    #endregion // ICommand Members
}

然后我创建了一个类来保存静态应用程序命令。

public class CustomCommands
{
    private readonly static DelegateCommand admin;

    static CustomCommands()
    {
        admin = new DelegateCommand();
    }

    public static DelegateCommand AdminCommand
    {
        get { return admin; }
    }
}

然后,我添加了一个键绑定到主窗口,因为用户控件不接收键手势。

<Window.InputBindings>
    <KeyBinding Key="A" Modifiers="Control" Command="my:CustomCommands.AdminCommand"/>
</Window.InputBindings>

然后,在我的ViewModel中,我可以像这样处理事件:

public class OfflineViewModel : ViewModelBase
{
    public OfflineViewModel()
    {
        CustomCommands.AdminCommand.Delegate(ShowAdmin);
    }

    public override void Removed()
    {
        base.Removed();

        ReleaseAdminCommand();
    }

    public override void Hidden()
    {
        base.Hidden();

        ReleaseAdminCommand();
    }

    void HookAdminCommand()
    {
        CustomCommands.AdminCommand.Delegate(ShowAdmin);
    }

    void ReleaseAdminCommand()
    {
        // Remove handling
        CustomCommands.AdminCommand.Delegate(null, null);
    }

    void ShowAdmin(object parameter)
    {
        Navigation.Push(new AdminViewModel());
    }
}

我可以选择使用DelegateCommand中的事件。

xxb16uws

xxb16uws2#

这对我有用(.Net 4. 0)

<UserControl>
    <UserControl.InputBindings>
        <KeyBinding Gesture="CTRL+C" Command="{Binding CancelCommand}" />
        <KeyBinding Gesture="F5" Command="{Binding StartCommand}" />
        <KeyBinding Gesture="CTRL+F5" Command="{Binding FreshStartCommand}" />
        <KeyBinding Gesture="F10" Command="{Binding ContinueCommand}" />
        <KeyBinding Gesture="F9" Command="{Binding RepeatCommand}" />
        <KeyBinding Gesture="ALT+F4" Command="{Binding CloseCommand}" />
        <KeyBinding Gesture="CTRL+N" Command="{Binding NewUUTCommand}" />
    </UserControl.InputBindings>

    ....

    <StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Center">
        <Button x:Name="BtnStart" Content="STARTEN" Command="{Binding StartCommand}"/>
        <Button Content="STOP" Command="{Binding StopCommand}"/>
    </StackPanel>

    ....

    <FocusManager.FocusedElement>
        <Binding ElementName="BtnStart"/>
    </FocusManager.FocusedElement>
</UserControl>

诀窍是确保焦点被设置到UserControl中。对我来说,这不是自动发生的。一旦焦点被设置,KeyBinding就工作了。(注意焦点被设置在元素的最后,因为元素必须首先定义)
为了完整起见,下面是ViewModel代码。

public ICommand StartCommand
{
    get
    {
        if (this._startCommand == null)
        {
            this._startCommand = new Mvvm.RelayCommand(parm => DoStart(), parm => DoCanStart());
        } return this._startCommand;
    }
}

private bool DoCanStart()
{
    return !IsRunning && ReadyToRun;
}

private void DoStart()
{
    log.Debug("Start test");
    ...
}
egdjgwm8

egdjgwm83#

看这个例子:https://thomaslevesque.com/2009/03/17/wpf-using-inputbindings-with-the-mvvm-pattern/
它使用XML标记将其绑定到DataContext of the root element。希望对您有所帮助。
此解决方案有一个局限性:它只对XAML根的DataContext有效。因此,例如,您不能使用它在DataContext也被重定义的控件上定义InputBinding,因为标记扩展将访问根DataContext。

相关问题