ICommand 实现静默失败
ICommand implemtation fails silently
更新
我尝试使用的按钮位于 <DataTemplate>
内,这显然导致了问题。一旦我在 <ItemsControl>
区域外的按钮上尝试了代码,它就可以工作。谁能告诉我,为什么它在像 <ItemsControl>
和 <DataTemplate>
这样的重复按钮中不起作用?
我正在尝试根据 TutorialsPoints.com 中的一篇文章实现 MVVM 通信模式。我稍微修改了代码,但总的来说它仍然与文章中的代码非常相似。我想要做的是,单击按钮后在控制台中写一行。
通过我的实现(见下面的代码),当我点击按钮时没有任何反应。我也试过在OnClick()
函数中加一个断点,看看是不是运行,这个不是。但是 MyICommand()
的构造函数中的断点显示 class 实际上已初始化。那我做错了什么?
按钮
<Button Content="Do stuff!"
Command="{Binding FakeCommand}"
Cursor="Hand"
Background="Red"
Foreground="White"
BorderThickness="0"
Padding="10 0 10 0" />
视图模型
public class AgreementViewModel : INotifyPropertyChanged
{
public MyICommand FakeCommand { get; set; }
public AgreementViewModel ()
{
LoadAgreements();
FakeCommand = new MyICommand(OnClick, CanClick);
FakeCommand.RaiseCanExecuteChanged();
}
private void OnClick()
{
Console.WriteLine("Something was clicked...");
}
private bool CanClick()
{
return true;
}
}
ICommand的实现
public class MyICommand : ICommand
{
Action _TargetExecuteMethod;
Func<bool> _TargetCanExecuteMethod;
public event EventHandler CanExecuteChanged = delegate {};
public MyICommand(Action executeMethod)
{
_TargetExecuteMethod = executeMethod;
}
public MyICommand(Action executeMethod, Func<bool> canExecuteMethod)
{
_TargetExecuteMethod = executeMethod;
_TargetCanExecuteMethod = canExecuteMethod;
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged(this, EventArgs.Empty);
}
bool ICommand.CanExecute(object parameter)
{
if (_TargetCanExecuteMethod != null)
{
return _TargetCanExecuteMethod();
}
if (_TargetExecuteMethod != null)
{
return true;
}
return false;
}
void ICommand.Execute(object parameter)
{
_TargetExecuteMethod?.Invoke();
}
}
从 CanExecute 和 Execute 方法中删除 ICommand.
。
如果您有一个 ItemsControl
(正如您在更新版本中提到的那样),那么 DataTemplate
的每个实例化的 DataContext
将在ItemsSource
。要绑定到父视图模型中的命令,您可以使用 ElementName
获取 ItemsControl
<ItemsControl ItemsSource="{Binding Data}" x:Name="root">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="Do stuff!"
Command="{Binding DataContext.FakeCommand, ElementName=root}"
Cursor="Hand"
Background="Red"
Foreground="White"
BorderThickness="0"
Padding="10 0 10 0" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
如果您不想使用名称,另一种方法是使用 RelativeSource
访问项目控件:
Command="{Binding DataContext.FakeCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
请注意,在这两种情况下,数据上下文都是ItemsControl
,因此您需要做DataContext.FakeCommand
,DataContext
在这里指的是ItemsControl
的数据上下文
您可能还需要为其调用命令的项目,因为它可以为源集合中的任何项目调用。为此,您可以添加一个 CommandParameter={Binding}
,传递给命令的参数将是项目(您的实现不会将参数传递给委托,但它可以)
更新
我尝试使用的按钮位于 <DataTemplate>
内,这显然导致了问题。一旦我在 <ItemsControl>
区域外的按钮上尝试了代码,它就可以工作。谁能告诉我,为什么它在像 <ItemsControl>
和 <DataTemplate>
这样的重复按钮中不起作用?
我正在尝试根据 TutorialsPoints.com 中的一篇文章实现 MVVM 通信模式。我稍微修改了代码,但总的来说它仍然与文章中的代码非常相似。我想要做的是,单击按钮后在控制台中写一行。
通过我的实现(见下面的代码),当我点击按钮时没有任何反应。我也试过在OnClick()
函数中加一个断点,看看是不是运行,这个不是。但是 MyICommand()
的构造函数中的断点显示 class 实际上已初始化。那我做错了什么?
按钮
<Button Content="Do stuff!"
Command="{Binding FakeCommand}"
Cursor="Hand"
Background="Red"
Foreground="White"
BorderThickness="0"
Padding="10 0 10 0" />
视图模型
public class AgreementViewModel : INotifyPropertyChanged
{
public MyICommand FakeCommand { get; set; }
public AgreementViewModel ()
{
LoadAgreements();
FakeCommand = new MyICommand(OnClick, CanClick);
FakeCommand.RaiseCanExecuteChanged();
}
private void OnClick()
{
Console.WriteLine("Something was clicked...");
}
private bool CanClick()
{
return true;
}
}
ICommand的实现
public class MyICommand : ICommand
{
Action _TargetExecuteMethod;
Func<bool> _TargetCanExecuteMethod;
public event EventHandler CanExecuteChanged = delegate {};
public MyICommand(Action executeMethod)
{
_TargetExecuteMethod = executeMethod;
}
public MyICommand(Action executeMethod, Func<bool> canExecuteMethod)
{
_TargetExecuteMethod = executeMethod;
_TargetCanExecuteMethod = canExecuteMethod;
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged(this, EventArgs.Empty);
}
bool ICommand.CanExecute(object parameter)
{
if (_TargetCanExecuteMethod != null)
{
return _TargetCanExecuteMethod();
}
if (_TargetExecuteMethod != null)
{
return true;
}
return false;
}
void ICommand.Execute(object parameter)
{
_TargetExecuteMethod?.Invoke();
}
}
从 CanExecute 和 Execute 方法中删除 ICommand.
。
如果您有一个 ItemsControl
(正如您在更新版本中提到的那样),那么 DataTemplate
的每个实例化的 DataContext
将在ItemsSource
。要绑定到父视图模型中的命令,您可以使用 ElementName
获取 ItemsControl
<ItemsControl ItemsSource="{Binding Data}" x:Name="root">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="Do stuff!"
Command="{Binding DataContext.FakeCommand, ElementName=root}"
Cursor="Hand"
Background="Red"
Foreground="White"
BorderThickness="0"
Padding="10 0 10 0" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
如果您不想使用名称,另一种方法是使用 RelativeSource
访问项目控件:
Command="{Binding DataContext.FakeCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
请注意,在这两种情况下,数据上下文都是ItemsControl
,因此您需要做DataContext.FakeCommand
,DataContext
在这里指的是ItemsControl
的数据上下文
您可能还需要为其调用命令的项目,因为它可以为源集合中的任何项目调用。为此,您可以添加一个 CommandParameter={Binding}
,传递给命令的参数将是项目(您的实现不会将参数传递给委托,但它可以)