RelayCommand不会执行按钮单击

时间:2014-02-17 05:54:39

标签: c# wpf xaml mvvm relaycommand

我已经坚持这个问题几个小时了。我试图在WPF中实现MVVM风格的Word加载项。我没有使用MVVM工具包。我有一个停靠在WinForm中的WPF用户控件。虽然我能够在win窗体中看到WPF用户控件并与之交互,但是当我单击按钮时,绑定到WPF按钮的通用RelayCommand将不会执行。 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()));
        }
    }

2 个答案:

答案 0 :(得分:2)

为了更详细地介绍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来处理这样的Command参数:

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();
    }
}

然后一切都应该正常。

答案 1 :(得分:1)

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

首先:告诉模型您放置视图模型的命名空间,我的如下所示:

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

第二:在xaml资源中添加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"

希望它能帮到你