C# C WPF非静态命令是否可以实现?
是否有一种方法可以为自定义控件创建仅实例的ICommand实现,而不需要后面的静态类 我正在尝试更新以前创建的自定义控件 目标之一是确保多实例功能 如果在同一应用程序中使用了同一自定义控件的两个或多个实例,则后面使用的任何静态类都会产生预期的干扰 我想出了如何摆脱大多数,但与ICommand有麻烦C# C WPF非静态命令是否可以实现?,c#,wpf,C#,Wpf,是否有一种方法可以为自定义控件创建仅实例的ICommand实现,而不需要后面的静态类 我正在尝试更新以前创建的自定义控件 目标之一是确保多实例功能 如果在同一应用程序中使用了同一自定义控件的两个或多个实例,则后面使用的任何静态类都会产生预期的干扰 我想出了如何摆脱大多数,但与ICommand有麻烦 自定义控件上的给定GUI项具有必须仅在用户控件实例内有效的命令-现在,该命令会干扰所有实例,因为example CanExecute会使GUI项在不满足本地条件的用户控件实例上处于活动状态。您可以创建
自定义控件上的给定GUI项具有必须仅在用户控件实例内有效的命令-现在,该命令会干扰所有实例,因为example CanExecute会使GUI项在不满足本地条件的用户控件实例上处于活动状态。您可以创建命令并将其作为属性,然后在控件中绑定到它:
<Button Command="{Binding MyCommand}"/>
在ViewModel中:
public ICommand MyCommand {get;set;} // construct your command and set it here
在您的控制下:
<Button Command="{Binding MyCommand}"/>
如果您没有使用MVVM模式,那么您应该在DataContext中创建相同的字段,可能是在您的控件代码中
还可以使用依赖项属性来定义命令,如果在创建用户控件后更改命令,则应使用它
一般而言:
为了了解在WPF/C中编写时的选项,我建议您阅读有关MVVM模式、依赖项属性、DataContext和绑定的内容—您可能已经知道其中一些内容。您可以创建命令并将其作为ViewModel的属性公开,然后在控件中绑定到它:
<Button Command="{Binding MyCommand}"/>
在ViewModel中:
public ICommand MyCommand {get;set;} // construct your command and set it here
在您的控制下:
<Button Command="{Binding MyCommand}"/>
如果您没有使用MVVM模式,那么您应该在DataContext中创建相同的字段,可能是在您的控件代码中
还可以使用依赖项属性来定义命令,如果在创建用户控件后更改命令,则应使用它
一般而言:
为了了解在WPF/C中编写时的选项,我建议阅读有关MVVM模式、依赖项属性、,DataContext和Binding—您可能已经知道了一些。CanExecute和Execute方法没有一个参数将它们链接到它们应该操作的对象,这一事实可能会让您感到困惑 但请记住,ICommand接口必须由一个类实现,该类的对象可以也应该有字段,通常在构造函数中初始化
例如,如果您遵循Ron.B.I.已经提到的MVVM模式,那么该命令通常会引用viewmodel。或者您可以使用RelayCommand之类的工具,在委托或lambda闭包对象中捕获viewmodel。我想您可能会被以下事实弄糊涂:CanExecute和Execute方法没有一个参数将它们链接到它们应该操作的对象 但请记住,ICommand接口必须由一个类实现,该类的对象可以也应该有字段,通常在构造函数中初始化
例如,如果您遵循Ron.B.I.已经提到的MVVM模式,那么该命令通常会引用viewmodel。或者您可以使用RelayCommand之类的工具,在委托或lambda闭包对象中捕获viewmodel。非常感谢您的回答和澄清 你给了我决定性的一脚,所以我想出来了。我在purpouse上添加了完整的示例 根据您的建议,Ron B I&Dennis我首先想了解更多关于ViewModel的信息 下面是后面有非静态类的示例。因此,解决方案只是在我的用户控件中添加一个新类,正如上面提到的站点所示-图3-更改了一些名称-版权属于Josh Smith JoshonWPF.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
}
}
最重要的部分:
命令作为属性实现:
public ICommand ActivateCommand
{
get
{
return _activateCommand;
}
}
因此,它确保返回实际的与实例相关的命令,该命令在用户控件的构造函数中使用Lambda表达式实例化:
public CommandControl()
{
InitializeComponent();
_activateCommand = new CommandImplementation(param => this.Activate(), param => this.CanActivated);
}
Lambda-表达式将连接到逻辑begind:
param => this.Activate()
对于激活功能,将在启动命令时执行
void Activate()
{
CommandCounter++;
}
及
用于传递ICommand CanExecute属性的本地逻辑,从而使您能够控制何时可以执行命令
在我的例子中,我使用了可以绑定到复选框的属性,但是你也可以用另一种方式来做
public bool CanActivated
{
get
{
return _canActivated;
}
set
{
_canActivated = value;
OnNotifyPropertyChanged("CanActivated");
}
}
同样,如Josh Smith joshsonwpf.wordpress.com所示-我只是将其更改为在构造函数中实例化,而不是检查私有成员是否为null,并在命令属性的GET部分交付新实例(如果需要)
代码的其余部分只是所需属性和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>
正如你所看到的那样
只有复选框绑定将提供启用/禁用按钮。
点击按钮触发命令,只需通过绑定再次增加标签上显示的计数器
综合起来:
只有一个简单的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中实现自定义命令的实例安全方法。非常感谢您的回答和澄清 你给了我决定性的一脚,所以我想出来了。我在purpouse上添加了完整的示例 根据您的建议,Ron B I&Dennis我首先想了解更多关于ViewModel的信息 下面是后面有非静态类的示例。因此,解决方案只是在我的用户控件中添加一个新类,正如上面提到的站点所示-图3-更改了一些名称-版权属于Josh Smith JoshonWPF.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
}
}
最重要的部分:
命令作为属性实现:
public ICommand ActivateCommand
{
get
{
return _activateCommand;
}
}
因此,它确保返回实际的与实例相关的命令,该命令在用户控件的构造函数中使用Lambda表达式实例化:
public CommandControl()
{
InitializeComponent();
_activateCommand = new CommandImplementation(param => this.Activate(), param => this.CanActivated);
}
Lambda-表达式将连接到逻辑begind:
param => this.Activate()
对于激活功能,将在启动命令时执行
void Activate()
{
CommandCounter++;
}
及
用于传递ICommand CanExecute属性的本地逻辑,从而使您能够控制何时可以执行命令
在我的例子中,我使用了可以绑定到复选框的属性,但是你也可以用另一种方式来做
public bool CanActivated
{
get
{
return _canActivated;
}
set
{
_canActivated = value;
OnNotifyPropertyChanged("CanActivated");
}
}
同样,如Josh Smith joshsonwpf.wordpress.com所示-我只是将其更改为在构造函数中实例化,而不是检查私有成员是否为null,并在命令属性的GET部分交付新实例(如果需要)
代码的其余部分只是所需属性和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>
正如您所看到的,只有一个复选框-绑定将提供启用/禁用按钮。
点击按钮触发命令,只需通过绑定再次增加标签上显示的计数器
综合起来:
只有一个简单的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中实现自定义命令的实例安全方法。我认为问题与依赖属性类似:我认为您没有正确理解命令。没有任何东西可以使命令与静态类相关。@Ephoric:MSDN文档与命令相关,非常糟糕,因为当命令在静态类中时,它充满了此类示例。同时,WPF的主流MVVM方法假定命令是视图模型的一部分。我理解,为什么会出现这个问题。我认为问题将类似于依赖属性:我认为您没有正确理解命令。没有任何东西可以使命令与静态类相关。@Ephoric:MSDN文档与命令相关,非常糟糕,因为当命令在静态类中时,它充满了此类示例。同时,WPF的主流MVVM方法假定命令是视图模型的一部分。我理解,为什么会出现这个问题。我会说将它作为属性公开,而不是保存在字段中,因为数据绑定不适用于字段。这可能会让人困惑。感谢您的见解,英语不是我的母语,非常感谢您的评论-我已经更改了它。我建议将它作为属性公开,而不是保存在字段中,因为数据绑定不适用于字段。这可能会让人困惑。谢谢你的洞察力,英语不是我的母语,非常感谢你的评论——我已经改变了它。