我已经在这个问题上停留了几个小时。我正在尝试在WPF中实现MVVM样式的Word加载项。我没有使用MVVM工具包。我有一个停在WinForm中的WPF用户控件。虽然我可以在Win表单中看到WPF用户控件并与其进行交互,但是单击该按钮时,将不会执行绑定(bind)到WPF按钮的通用RelayCommand<T>RelayCommand位于ViewModel.cs中,而 View 的DataContext通过后面的代码设置。我确定我在做些愚蠢的事情,但无法弄清楚它是什么,因此不确定为什么RelayCommand属性的get{}无法执行。请参见下面的代码。在此先感谢您的帮助!

RelayCommand.cs (代码段排除 namespace 并包含语句)

/// <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 Article时,您的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));
    }
}

我将您的Button示例放入新的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();
    }
}

然后,一切应该正常。

关于c# - RelayCommand不会在按钮上执行,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/21821762/

10-13 08:31