[英]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 的正下方。
有任何想法吗?
保罗
myRoutingCommandCanExecute
和myRoutingCommandExecuted
是事件处理程序。 您不能在另一个 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>
。您可以在任何需要的地方实现它们。
命令的逻辑与处理数据无关,因此在 ViewModel 中实现它没有意义。
您的命令 class 不是 ViewModel,它是 View 一部分的助手 class。
据我所知,“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中的命令时: 我认为是因为CanExecute
和Executed
绑定的方法必须是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.cs和TailoredReading.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 的好处是避免为每个新命令执行 Execute和CanExecute 。 你只需要为他们实现方法/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.