非常感谢您的回答和澄清!
你给了我决定性的一脚,所以我想通了。我添加了我的完整示例。
按照您的建议(Ron BI 和 Dennis),我首先想了解更多有关 ViewModel 的信息。
在http://msdn.microsoft.com/en-ca/magazine/dd419663.aspx下有非静态类的例子。所以解决方案只是在我的用户控件中添加新类(正如提到的网站上所示 - 图 3 - 更改了一些名称 - 版权属于 Josh Smith joshsmithonwpf.wordpress.com):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
namespace WpfCommandControl
{
class CommandImplementation : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
public CommandImplementation(Action<object> execute)
: this(execute, null)
{
}
public CommandImplementation(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion // ICommand Members
}
}
然后在用户控件“窗口”中
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
namespace WpfCommandControl
{
public partial class CommandControl : UserControl, INotifyPropertyChanged
{
#region [ Private Members ]
private bool _canActivated = false;
private int _counter = 0;
CommandImplementation _activateCommand;
#endregion
#region [ Properties ]
public int CommandCounter
{
get
{
return _counter;
}
set
{
_counter = value;
OnNotifyPropertyChanged("CommandCounter");
}
}
public bool CanActivated
{
get
{
return _canActivated;
}
set
{
_canActivated = value;
OnNotifyPropertyChanged("CanActivated");
}
}
#endregion
#region [ Property_Changed_Utilities ]
public event PropertyChangedEventHandler PropertyChanged;
private void OnNotifyPropertyChanged(String info)
{
// Note: Do not forget to add interface "INotifyPropertyChanged" to your class.
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#endregion
# region [ Commands ]
public ICommand ActivateCommand
{
get
{
return _activateCommand;
}
}
#endregion
#region [ Constructor ]
public CommandControl()
{
InitializeComponent();
_activateCommand = new CommandImplementation(param => this.Activate(), param => this.CanActivated);
}
#endregion
#region [ Methods ]
void Activate()
{
CommandCounter++;
}
#endregion
}
}
最重要的部分:
命令实现为属性:
public ICommand ActivateCommand
{
get
{
return _activateCommand;
}
}
所以它确保它将返回与实际实例相关的命令,该命令是在用户控件的构造函数中使用 Lambda-Expression 实例化的:
public CommandControl()
{
InitializeComponent();
_activateCommand = new CommandImplementation(param => this.Activate(), param => this.CanActivated);
}
Lambda - 表达式开始连接到逻辑:
param => this.Activate()
对于 Activate() 函数,它将在 Command 被触发时执行
void Activate()
{
CommandCounter++;
}
和
param => this.CanActivated
用于为 ICommand CanExecute 属性传递本地逻辑,从而让您控制何时可以执行命令。
在我的情况下,我使用了可以绑定到 CheckBox 的属性,但你也可以用另一种方式来做......
public bool CanActivated
{
get
{
return _canActivated;
}
set
{
_canActivated = value;
OnNotifyPropertyChanged("CanActivated");
}
}
同样如 Josh Smith joshsmithonwpf.wordpress.com 所示 - 我只是将其更改为在构造函数中实例化,而不是检查私有成员是否为 null 并在命令属性的 GET 部分中根据需要提供新实例。
其余代码只是所需的 Properties 和 OnNotifyPropertyChanged 的实现,如 MSDN 上所示。
XAML 很简单——仅用于概念证明。
<UserControl x:Class="WpfCommandControl.CommandControl"
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:local="clr-namespace:WpfCommandControl"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<Grid>
<StackPanel>
<CheckBox Content="Activate" IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, Path=CanActivated}" />
<Button Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=UserControl},
Path=ActivateCommand}"
Content="Click me"
IsEnabled="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=UserControl},
Path=CanActivated}" />
<Label Content="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, Path=CommandCounter}" IsEnabled="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, Path=CanActivated}" />
</StackPanel>
</Grid>
如您所见,只有一个 CheckBox - Binding 将提供按钮的启用/禁用。单击按钮会触发仅增加计数器的命令 - 通过绑定再次显示在标签上。
放在一起:
只需一个简单的 XAML 表单,其中包含四个用户控件:
<Window x:Class="CommandsTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CommandsTest"
xmlns:uctrl="clr-namespace:WpfCommandControl;assembly=WpfCommandControl"
Title="MainWindow"
Width="525"
Height="350">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<uctrl:CommandControl Grid.Row="0" Grid.Column="0" />
<uctrl:CommandControl Grid.Row="0" Grid.Column="1" />
<uctrl:CommandControl Grid.Row="1" Grid.Column="0" />
<uctrl:CommandControl Grid.Row="1" Grid.Column="1" />
</Grid>
在每个控件上触发命令完全符合元素内部的需要。
一切都以 WPF 方式解决 - 使用命令和绑定而不与 GUI 元素进行任何直接交互,因此无需更新后面的代码即可交换 GUI。
再次感谢您向我展示了在 WPF 中实现自定义命令的另一种(实例安全)方法。