C# C WPF非静态命令是否可以实现?

C# C WPF非静态命令是否可以实现?,c#,wpf,C#,Wpf,是否有一种方法可以为自定义控件创建仅实例的ICommand实现,而不需要后面的静态类 我正在尝试更新以前创建的自定义控件 目标之一是确保多实例功能 如果在同一应用程序中使用了同一自定义控件的两个或多个实例,则后面使用的任何静态类都会产生预期的干扰 我想出了如何摆脱大多数,但与ICommand有麻烦 自定义控件上的给定GUI项具有必须仅在用户控件实例内有效的命令-现在,该命令会干扰所有实例,因为example CanExecute会使GUI项在不满足本地条件的用户控件实例上处于活动状态。您可以创建

是否有一种方法可以为自定义控件创建仅实例的ICommand实现,而不需要后面的静态类

我正在尝试更新以前创建的自定义控件

目标之一是确保多实例功能

如果在同一应用程序中使用了同一自定义控件的两个或多个实例,则后面使用的任何静态类都会产生预期的干扰

我想出了如何摆脱大多数,但与ICommand有麻烦


自定义控件上的给定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方法假定命令是视图模型的一部分。我理解,为什么会出现这个问题。我会说将它作为属性公开,而不是保存在字段中,因为数据绑定不适用于字段。这可能会让人困惑。感谢您的见解,英语不是我的母语,非常感谢您的评论-我已经更改了它。我建议将它作为属性公开,而不是保存在字段中,因为数据绑定不适用于字段。这可能会让人困惑。谢谢你的洞察力,英语不是我的母语,非常感谢你的评论——我已经改变了它。