2014-02-17 99 views
0

我一直在這個問題上停留了幾個小時。我正在嘗試在WPF中實現MVVM樣式的Word加載項。我沒有使用MVVM工具包。我有一個停靠在WinForm中的WPF用戶控件。雖然我能夠在獲勝窗體中看到WPF用戶控件並與其交互,但當我單擊該按鈕時,綁定到WPF按鈕的通用RelayCommand將不會執行。 RelayCommand位於ViewModel.cs中,視圖的DataContext通過代碼隱藏進行設置。我確定我在做一些愚蠢的事情,但無法弄清楚它是什麼,因此不確定爲什麼RelayCommand屬性的get {}不會被執行。請參閱下面的代碼。先謝謝您的幫助!RelayCommand不會在按鈕上執行單擊

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

您的代碼沒有錯..檢查Datacontext應用正確..添加另一個文本屬性,並將其綁定到文本塊,並檢查顯示? – Sankarann

+0

使用Snoop(http://snoopwpf.codeplex.com/)在運行時檢查您的datacontext – blindmeis

+0

在我看來,您不發送命令參數,並且在您的RelayCommand實現中,出於某種原因,只有在參數isn 't null。 –

回答

2

爲了讓您更詳細的開始到MVVM和RelayCommands:

您不必decl是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; 
     } 
    } 
} 

在您的視圖模型的RelayCommands應像這樣定義(我實現了INotifyPropertyChanged以及WPF Xaml Property處理示例):

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

然後一切都應該正常工作。

+0

感謝您的建議。我基本上開始了逐漸增加的測試。 – RudyD

1

我通過講述在XAML,而不是cs文件數據上下文模型解決了這個問題。

第一:告訴模型中,你放在您的視圖模型的命名空間,我的是象下面這樣:

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

二:在XAML資源加入您的視圖模型,如:

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"

希望它會幫助你

+0

謝謝所有回覆的人。這個特殊的解決方案爲我工作。 – RudyD

+0

@ user3317801如果此解決方案適合您,請將其標記爲幫助他人注意的答案。 –

+0

我想我說得太快了。我在下午的早些時候嘗試瞭解決方案,並認爲它是成功的,因爲在加載項啓動時,SubmitCommentCommand被調用,但是今晚當我嘗試重新啓動應用程序時,我注意到該命令除了在發射。猜猜它總是這樣做,我從來沒有注意到。無論如何,不​​知道這裏發生了什麼。我會嘗試下載snoop,因爲另一個評論這個問題的人建議並看看會發生什麼。 – RudyD