繁体   English   中英

如何将命令与视图 model 绑定?

[英]How to bind commands with a view model?

我已经阅读了许多关于绑定和命令的帖子,但我正在努力获得我想要的工作。

以下工作正常

public partial class TailoredReading : Window
    {

        public static RoutedUICommand myRoutingCommand = new RoutedUICommand("myCommand", "myCommand", typeof(InputGestureWindow));

        public TailoredReading()
        {
            InitializeComponent();
        }

        private void SaveResource_Click(object sender, RoutedEventArgs e)
        {
            //ViewModel.SaveResource();
        }

        void myRoutingCommandExecuted(object target, ExecutedRoutedEventArgs e)
        {
            String command = ((RoutedCommand)e.Command).Name;
            MessageBox.Show("The \"" + command + "\" command has been invoked NOW. ");
        }

        void myRoutingCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }

    }
<Window x:Class="ESL_Master_Suite.Components.Core.Resources.TailoredReading"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        xmlns:this="clr-namespace:ESL_Master_Suite.Components.Core.Resources" 
        xmlns:this1="clr-namespace:ESL_Master_Suite.Components.Controls"
        xmlns:local="clr-namespace:ESL_Master_Suite.Components.Core.Resources"
        mc:Ignorable="d"
        Title="TailoredReading" WindowStartupLocation="CenterScreen" Width="1024">

    <Window.DataContext>
        <this:ViewModel />
    </Window.DataContext>

    <Window.InputBindings>
        <KeyBinding Command="{x:Static this:TailoredReading.myRoutingCommand}" Key="F1" />
    </Window.InputBindings>

    <Window.CommandBindings>
        <CommandBinding Command="{x:Static this:TailoredReading.myRoutingCommand}" CanExecute="myRoutingCommandCanExecute" Executed="myRoutingCommandExecuted"/>
    </Window.CommandBindings>

但是,我想将命令逻辑分开,在它自己的 class 中。

public class Commands
    {
        public static readonly RoutedUICommand myRoutingCommand = new RoutedUICommand("myCommand", "myCommand", typeof(InputGestureWindow));

        void myRoutingCommandExecuted(object target, ExecutedRoutedEventArgs e)
        {
            String command = ((RoutedCommand)e.Command).Name;
            MessageBox.Show("The \"" + command + "\" command has been invoked. ");
        }

        void myRoutingCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }
    }
<Window x:Class="ESL_Master_Suite.Components.Core.Resources.TailoredReading"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        xmlns:this="clr-namespace:ESL_Master_Suite.Components.Core.Resources" 
        xmlns:this1="clr-namespace:ESL_Master_Suite.Components.Controls"
        xmlns:local="clr-namespace:ESL_Master_Suite.Components.Core.Resources"
        mc:Ignorable="d"
        Title="TailoredReading" WindowStartupLocation="CenterScreen" Width="1024">

    <Window.DataContext>
        <this:ViewModel />
    </Window.DataContext>

<Window.InputBindings>
        <KeyBinding Command="{x:Static this:Commands.myRoutingCommand}" Key="F1" />
    </Window.InputBindings>

    <Window.CommandBindings>
        <CommandBinding Command="{x:Static this:Commands.myRoutingCommand}" CanExecute="myRoutingCommandCanExecute" Executed="myRoutingCommandExecuted"/>
    </Window.CommandBindings>

当我这样做时,即使在清理和重建之后,我也会收到一个错误,指出 Commands 不在命名空间中。 即使它位于 window class 的正下方。

有任何想法吗?

保罗

myRoutingCommandCanExecutemyRoutingCommandExecuted是事件处理程序。 您不能在另一个 class 中定义这些。

事实上,如果您想将执行逻辑与视图分开,使用RoutedUICommand并不是很有用。 有关这方面的更多信息,请参阅此博客文章

你应该做的是创建一个自定义的 class 来实现ICommand并接受一个Action<object>和一个Predicate<object>

public class DelegateCommand : System.Windows.Input.ICommand
{
    private readonly Predicate<object> _canExecute;
    private readonly Action<object> _execute;

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

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

    public bool CanExecute(object parameter)
    {
        if (_canExecute == null)
            return true;

        return _canExecute(parameter);
    }

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

    public event EventHandler CanExecuteChanged;
}

然后在视图 model 中创建命令的实例,您还可以在其中定义执行逻辑:

public class ViewModel
{
    public ViewModel()
    {
        MyCommand = new DelegateCommand(MyCommandExecuted, MyCommandCanExecute);
    }

    public DelegateCommand MyCommand { get; }

    private void MyCommandExecuted(object obj)
    {
        MessageBox.Show("The command has been invoked.");
    }

    private bool MyCommandCanExecute(object obj)
    {
        return true;
    }
}

然后视图绑定到视图 model 的命令属性:

<Window x:Class="ESL_Master_Suite.Components.Core.Resources.TailoredReading"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        xmlns:this="clr-namespace:ESL_Master_Suite.Components.Core.Resources" 
        xmlns:this1="clr-namespace:ESL_Master_Suite.Components.Controls"
        xmlns:local="clr-namespace:ESL_Master_Suite.Components.Core.Resources"
        mc:Ignorable="d"
        Title="TailoredReading" WindowStartupLocation="CenterScreen" Width="1024">
    <Window.DataContext>
        <this:ViewModel />
    </Window.DataContext>

    <Window.InputBindings>
        <KeyBinding Command="{Binding MyCommand}" Key="F1" />
    </Window.InputBindings>
</Window>

显然,您不必在视图 model class 中实现传递给命令的Action<object>Predicate<object> 。您可以在任何需要的地方实现它们。

  1. 命令的逻辑与处理数据无关,因此在 ViewModel 中实现它没有意义。
    您的命令 class 不是 ViewModel,它是 View 一部分的助手 class。

  2. 据我所知,“x: Static”标记扩展可以获取常量、枚举或 STATIC 字段和属性的值。
    但不是传统方法!

试试这个实现:

public static class Commands
{
    public static RoutedUICommand MyRoutingCommand { get; } = new RoutedUICommand("myCommand", "myCommand", typeof(Commands));

    public static ExecutedRoutedEventHandler MyRoutingCommandExecuted { get; } 
      = myRoutingCommandExecuted;

    private static void myRoutingCommandExecuted(object target, ExecutedRoutedEventArgs e)
    {
        string command = ((RoutedCommand)e.Command).Name;
        MessageBox.Show("The \"" + command + "\" command has been invoked. ");
    }

    public static CanExecuteRoutedEventHandler MyRoutingCommandCanExecute { get; } 
      = myRoutingCommandCanExecute;

    private static void myRoutingCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
    }
}

XAML:

<Window.InputBindings>
    <KeyBinding Command="{x:Static this:Commands.MyRoutingCommand}" Key="F1" />
</Window.InputBindings>

<Window.CommandBindings>
    <CommandBinding Command="{x:Static this:Commands.MyRoutingCommand}"
                    CanExecute="{x:Static this:Commands.MyRoutingCommandCanExecute}"
                    Executed="{x:Static this:Commands.MyRoutingCommandExecuted}"/>
</Window.CommandBindings>

当然,还要确保命名空间是正确的。

修改XAML后,可能会出现错误提示。
但这是因为 XAML 的信息不是从项目中检索的,而是从程序集中检索的。
因此,错误应该在构建修改后的项目后消失。

绑定XAML中的命令时: 我认为是因为CanExecuteExecuted绑定的方法必须是class后面代码的实例成员。


如果你想做你做的事情,你可以在代码中的 static 构造函数中进行:

  public class Commands
  {
    public static RoutedCommand MyCommand = new RoutedCommand("MyCommand", typeof(TailoredReading ));

    static Commands()
    {
      CommandBinding commandBinding = new CommandBinding(MyCommand, MyCommandCmd_Executed, MyCommandCmd_CanExecute);
      CommandManager.RegisterClassCommandBinding(typeof(TailoredReading ), commandBinding);
    }

    public static void MyCommandCmd_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
      e.CanExecute = true;
    }

    public static void MyCommandCmd_Executed(object sender, ExecutedRoutedEventArgs e)
    {

    }

  }

不知道问题解决了吗? 她是我解决它的建议。 如果不需要使用RoutedUICommand ,请将其更改为自己的 class,称为RelayCommand (链接: RelayCommand 实现),它派生自ICommand 然后你的Commands class 看起来像这样:

namespace WpfAppNet.Commands
{
    public class Commands
    {
        public static ICommand MyRoutingCommand = new RelayCommand(MyRoutingCommandExecuted, (o) =>
        {
            return MyRoutingCommandCanExecute();
        });

        private static void MyRoutingCommandExecuted(object target)
        {
            MessageBox.Show("The command has been invoked. ");
        }

        private static bool MyRoutingCommandCanExecute()
        {
            return true;
        }
    }
}

在您的TailoredReading Window XAML 文件中,您添加了位于 class 的命名空间。在我的示例中,它是clr-namespace:WpfAppNet.Commands (就像代码片段的第一行)。 如果您已经将它添加到 namspace-alias 中,那么您不需要这样做。

在您的代码片段中,请查看您是否已将文件Commands.csTailoredReading.cs添加到文件夹Resources中。 如果不是,那可能是您出错的原因。

<Window x:Class="WpfAppNet.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
        xmlns:local="clr-namespace:WpfAppNet"
        xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
        mc:Ignorable="d"
        xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
        <!-- add an namespace alias where the file Commands.cs is located -->
        xmlns:commands="clr-namespace:WpfAppNet.Commands"
        Title="MainWindow" Height="450" Width="800">

    <Window.InputBindings>
        <!-- Bind to the command -->
        <KeyBinding Command="{x:Static commands:Commands.MyRoutingCommand}" Key="F1" />
    </Window.InputBindings>
 ...
</Window>

RelayCommand class 的好处是避免为每个新命令执行 ExecuteCanExecute 你只需要为他们实现方法/function。

PS:我还看到您为不同的名称空间别名添加了两次相同的路径:

xmlns:this="clr-namespace:ESL_Master_Suite.Components.Core.Resources" 
xmlns:local="clr-namespace:ESL_Master_Suite.Components.Core.Resources"

其中之一可以删除。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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