简体   繁体   English

RelayCommand 不会在按钮单击时执行

[英]RelayCommand Won't Execute On Button Click

I've been stuck on this problem for a few hours.我已经被这个问题困住了几个小时。 I am attempting to implement an MVVM-style Word Add-In in WPF.我正在尝试在 WPF 中实现一个 MVVM 风格的 Word 插件。 I am not using an MVVM toolkit.我没有使用 MVVM 工具包。 I have a WPF user control that is docked within a WinForm.我有一个停靠在 WinForm 中的 WPF 用户控件。 While I am able to see the WPF user control within the win form and interact with it, my generic RelayCommand<T> that is bound to a WPF button won't execute when I click the button.虽然我能够在 win 窗体中看到 WPF 用户控件并与之交互,但当我单击按钮时,绑定到 WPF 按钮的通用RelayCommand<T>不会执行。 The RelayCommand lives in ViewModel.cs and the DataContext for the view is set through the code-behind. RelayCommand位于 ViewModel.cs 中,视图的DataContext是通过代码隐藏设置的。 I'm sure I'm doing something silly, but can't figure out what it is and therefore not sure why RelayCommand property's get{} won't get executed.我确定我在做一些愚蠢的事情,但无法弄清楚它是什么,因此不确定为什么RelayCommand属性的get{}不会被执行。 Please see the code below.请看下面的代码。 Thanks in advance for the help!在此先感谢您的帮助!

RelayCommand.cs (code snippet excludes namespace and includes statements) 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 (only the pertinent snippet. excludes some code) SubmissionUserControl.xaml (仅相关代码段。不包括一些代码)

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

SubmissionUserControl.xaml.cs (contains snippet where I reference the ViewModel) SubmissionUserControl.xaml.cs (包含我引用 ViewModel 的片段)

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

ViewModel.cs (excludes some code. only shows the pertinent RelayCommand) 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()));
    }
}

To give you a more detailed start into MVVM and RelayCommands:为了让您更详细地了解 MVVM 和 RelayCommands:

You do not have to declare your ViewModel in Xaml, this is mostly done programmatically on application root level, maybe with some DI.您不必在 Xaml 中声明您的 ViewModel,这主要是在应用程序根级别以编程方式完成的,也许还有一些 DI。

When sticking to this MSDN Article your RelayCommand should look like this:坚持阅读这篇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
}

Additionally you can define a generic RelayCommand to handle Commandparameters like this:此外,您可以定义一个通用的 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;
        }
    }
}

In your ViewModel the RelayCommands should be definied like this (I implemented INotifyPropertyChanged as well for further WPF Xaml Property handling example):在您的 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));
    }
}

I put your Button Example into a fresh WPF Application like this:我将您的按钮示例放入一个新的 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>

And set the DataContext like this for simplicity reasons (As stated before, this is normally done through some kind of DI at root level):并出于简单的原因像这样设置 DataContext(如前所述,这通常是通过根级别的某种 DI 完成的):

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

Then everything should work fine.然后一切正常。

I solved this issue by telling the Model about data context in XAML instead of .cs file.我通过告诉模型有关 XAML 中的数据上下文而不是 .cs 文件来解决这个问题。

First: Tell Model the namespace in which you placed your view model, mine was like below:首先:告诉 Model 你放置视图模型的namespace ,我的如下所示:

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

Second: Add your ViewModel in XAML Resources like:第二:在 XAML Resources添加您的 ViewModel,例如:

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

Third: Add DataContext to the parent container,in my case that is Grid .第三:DataContext添加到父容器,在我的例子中是Grid

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

Fourth: In your button code add the data context like:第四:在您的按钮代码中添加数据上下文,如:

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

Hope it will help you希望它会帮助你

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

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