繁体   English   中英

RelayCommand 不会在按钮单击时执行

[英]RelayCommand Won't Execute On Button Click

我已经被这个问题困住了几个小时。 我正在尝试在 WPF 中实现一个 MVVM 风格的 Word 插件。 我没有使用 MVVM 工具包。 我有一个停靠在 WinForm 中的 WPF 用户控件。 虽然我能够在 win 窗体中看到 WPF 用户控件并与之交互,但当我单击按钮时,绑定到 WPF 按钮的通用RelayCommand<T>不会执行。 RelayCommand位于 ViewModel.cs 中,视图的DataContext是通过代码隐藏设置的。 我确定我在做一些愚蠢的事情,但无法弄清楚它是什么,因此不确定为什么RelayCommand属性的get{}不会被执行。 请看下面的代码。 在此先感谢您的帮助!

RelayCommand.cs (代码片段不包括命名空间并包括语句)

/// <summary>
/// RelayCommand
/// </summary>
/// <typeparam name="T">Generic Parameter</typeparam>
public class RelayCommand<T> : ICommand where T : class
{
    #region Constructors

    /// <summary>
    /// RelayCommand constructor
    /// </summary>
    /// <param name="exec">Delegate that encapsulates a method that takes in a single parameter and returns void</param>
    /// <param name="canExec">Delegate that encapsulates a method that defines a set of criteria and returns a true if criteria is met; else false</param>
    public RelayCommand(Action<T> execute, Predicate<T> canExecute = null)
    {
        if (execute == null)
            throw new ArgumentNullException("execute is null");

        _canExecute = canExecute;
        _execute    = execute;
    }

    #endregion
    #region Members

    /// <summary>
    /// Execute method
    /// </summary>
    /// <param name="param">Parameter</param>
    public void Execute(object param)
    {
        T obj = param as T;

        if(obj != null)
        {
            _execute(obj);
        }
    }

    /// <summary>
    /// CanExec is a method that shows whether or not execution can happen
    /// </summary>
    /// <param name="param">Parameter</param>
    /// <returns>true if can execute; else false</returns>
    public bool CanExecute(object param)
    {
        if (_canExecute == null)
            return true;

        T obj = param as T;
        return obj == null || _canExecute(obj);
    }

    /// <summary>
    /// CanExec event changed
    /// </summary>
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    #endregion

    #region Fields

    private readonly Predicate<T> _canExecute;
    private readonly Action<T> _execute;

    #endregion
}

SubmissionUserControl.xaml (仅相关代码段。不包括一些代码)

<Button Grid.Column="2" x:Name="SubmitButton" Command="{Binding Path=SubmitCommentCommand}"
                        Content="Submit" HorizontalAlignment="Right" Margin="5"/>

SubmissionUserControl.xaml.cs (包含我引用 ViewModel 的片段)

ViewModel viewModel;
public SubmissionUserControl()
{
    InitializeComponent();
    viewModel = new ViewModel();
    DataContext = viewModel;
}

ViewModel.cs (不包括一些代码。只显示相关的RelayCommand)

/// <summary>
/// SubmitCommentCommand responsible for interacting with UI to submit a comment.
/// </summary>
/// <returns>Returns a RelayCommand that executes a method to Save comments from the comment box</returns>
public ICommand SubmitCommentCommand
{
    get
    {
        return _submitCommentCommand ?? (_submitCommentCommand = new RelayCommand<object>(param => this.SaveComment()));
    }
}

为了让您更详细地了解 MVVM 和 RelayCommands:

您不必在 Xaml 中声明您的 ViewModel,这主要是在应用程序根级别以编程方式完成的,也许还有一些 DI。

坚持阅读这篇MSDN 文章时,您的 RelayCommand 应如下所示:

public class RelayCommand : ICommand
{
    #region Fields

    readonly Action<object> _execute;
    readonly Predicate<object> _canExecute;

    #endregion // Fields

    #region Constructors

    public RelayCommand(Action<object> execute)
        : this(execute, null)
    {
    }

    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

        _execute = execute;
        _canExecute = canExecute;
    }
    #endregion // Constructors

    #region ICommand Members

    [DebuggerStepThrough]
    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
}

此外,您可以定义一个通用的 RelayCommand 来处理这样的 Commandparameters:

public class GenericRelayCommand<T> : ICommand
{
    private readonly Action<T> _execute;
    public Predicate<T> CanExecuteFunc { get; private set; }

    public GenericRelayCommand(Action<T> execute) : this(execute, p => true)
    {}

    public GenericRelayCommand(Action<T> execute, Predicate<T> canExecuteFunc)
    {
        _execute = execute;
        CanExecuteFunc = canExecuteFunc;
    }

    public bool CanExecute(object parameter)
    {
        var canExecute = CanExecuteFunc((T)parameter);
        return canExecute;
    }

    public void Execute(object parameter)
    {
        _execute((T)parameter);
    }

    public event EventHandler CanExecuteChanged
    {
        add
        {
            CommandManager.RequerySuggested += value;
        }
        remove
        {
            CommandManager.RequerySuggested -= value;
        }
    }
}

在您的 ViewModel 中,RelayCommands 应该像这样定义(我还实现了 INotifyPropertyChanged 以及进一步的 WPF Xaml 属性处理示例):

public class ViewModel : INotifyPropertyChanged
{
    private string _comment;
    public string Comment
    {
        get { return _comment; }
        set { _comment = value; OnPropertyChanged("Comment"); }
    }

    public GenericRelayCommand<string> SubmitComment1Command { get; set; }
    public RelayCommand SubmitComment2Command { get; set; }

    public ViewModel()
    {
        Comment = "Hello World!";
        SubmitComment1Command = new GenericRelayCommand<string>(OnSubmitComment1);
        SubmitComment2Command = new RelayCommand(OnSubmitComment2);
    }

    private void OnSubmitComment1(string obj)
    {
        //Save Comment Mock with CommandParameter
        MessageBox.Show(obj);    
    }

    private void OnSubmitComment2(object obj)
    {
        //Save Comment Mock with Property
        MessageBox.Show(Comment);
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

我将您的按钮示例放入一个新的 WPF 应用程序中,如下所示:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Width="525"
        Height="350">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <StackPanel Grid.Column="0"
                    Orientation="Horizontal">
            <TextBox Name="textBox"
                     Width="200"
                     Text="{Binding Comment,
                                    Mode=TwoWay,
                                    UpdateSourceTrigger=PropertyChanged}" />
            <Button x:Name="SubmitButton1"
                    Grid.Column="0"
                    Margin="5"
                    HorizontalAlignment="Right"
                    Command="{Binding Path=SubmitComment1Command}"
                    CommandParameter="{Binding ElementName=textBox,
                                               Path=Text}"
                    Content="Submit1" />
        </StackPanel>


        <Button x:Name="SubmitButton2"
                Grid.Column="1"
                Margin="5"
                HorizontalAlignment="Right"
                Command="{Binding Path=SubmitComment2Command}"
                Content="Submit2" />
    </Grid>
</Window>

并出于简单的原因像这样设置 DataContext(如前所述,这通常是通过根级别的某种 DI 完成的):

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new ViewModel();
    }
}

然后一切正常。

我通过告诉模型有关 XAML 中的数据上下文而不是 .cs 文件来解决这个问题。

首先:告诉 Model 你放置视图模型的namespace ,我的如下所示:

xmlns:Local="clr-namespace:MKPL.Views.A01.S020"

第二:在 XAML Resources添加您的 ViewModel,例如:

<UserControl.Resources>  
        <Local:ViewModel x:Key="dvm"/>   
</UserControl.Resources>

第三:DataContext添加到父容器,在我的例子中是Grid

<Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource dvm}}">

第四:在您的按钮代码中添加数据上下文,如:

<Button Grid.Column="2" x:Name="SubmitButton" Command="{Binding Path=SubmitCommentCommand, Source={StaticResource dvm}}"
                        Content="Submit" HorizontalAlignment="Right" Margin="5"/>

希望它会帮助你

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM