2013-06-06 30 views
1

有沒有辦法爲自定義控件創建「Instance only」ICommand實現,而沒有靜態類「後面」?C#WPF非靜態命令的實現可能嗎?

我想更新以前創建的自定義控件。

其中一個目標是確保多實例功能。

如果在同一個應用程序中使用了同一個自定義控件的兩個或多個實例,則會有(如預期的)來自後面使用的任何靜態類的干擾。

我想出瞭如何擺脫最多,但與ICommand麻煩。

給定自定義控件上的GUI項目必須僅在用戶控件實例中有效 - 而不是現在該命令干擾所有實例(例如,CanExecute使GUI項目在UserControl實例上處於活動狀態,其中「本地「條件不符合)。

+1

我認爲這些問題將類似於依賴屬性:http://stackoverflow.com/questions/3660696/non-static-dependency-properties –

+1

我認爲你不正確理解命令。沒有什麼能夠使命令與靜態類相關。 – Euphoric

+0

@Euphoric:與命令相關的MSDN文檔非常糟糕,因爲它充滿了這種樣本(當命令處於靜態類時)。與此同時,作爲WPF主流的MVVM方法假定,該命令是視圖模型的一部分。我明白,爲什麼這個問題出現了。 – Dennis

回答

2

您可以創建你的命令,並將其作爲您的視圖模型的屬性,然後給它綁定在你的控制:

在您的視圖模型:

public ICommand MyCommand {get;set;} // construct your command and set it here 

在你的控制:

<Button Command="{Binding MyCommand}"/> 

如果你沒有使用MVVM模式,那麼你應該在DataContext(可能在你的控制代碼後面)創建相同的字段

您也可以使用Dependency屬性來定義您的命令,如果您在創建用戶控件後更改命令,則應該使用它。

一般:

爲了在WPF/C#寫我建議你閱讀有關MVVM模式,依賴屬性,DataContext的和綁定時,知道你的選擇 - 你可能知道一些這方面了。

+0

我會說「暴露它作爲財產」而不是「保存在一個領域」,因爲數據綁定不適用於字段。這可能令人困惑。 – Dennis

+0

感謝您的洞察力,英語不是我的母語,您的評論非常感謝 - 我已經改變了它。 –

1

我想你可能會被CanExecute和Execute方法沒有一個將它們鏈接到它們應該在其上執行操作的對象的事實所困惑。

但請記住,ICommand接口必須由類實現,並且該類的對象可以並且應該有字段,通常在構造函數中初始化。例如,如果您遵循MVVM模式(正如Ron.B.I.所述),該命令通常具有對視圖模型的引用。或者你可以使用類似於RelayCommand的東西,並將視圖模型捕獲到委託或lambda閉包對象中。

0

非常感謝您的答覆和澄清!

你給了我全部的決定權,所以我想通了。我在purpouse上添加了我的完整示例。

遵從您的建議(Ron B我&丹尼斯)我首先想了解更多關於ViewModel。

http://msdn.microsoft.com/en-ca/magazine/dd419663.aspx下面有非靜態類的例子。因此,解決辦法是簡單地在我的用戶控件添加新的類(正好如下所示提到的網站上 - 圖3 - 一些名稱變更 - 版權屬於約什 - 史密斯joshsmithonwpf.wordpress.com):

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Windows.Input; 

namespace WpfCommandControl 
{ 
class CommandImplementation : ICommand 
{ 
    #region Fields 

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

    #endregion // Fields 

    #region Constructors 

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

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

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

    #region ICommand Members 

    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 

} 
} 

然後在用戶控制 「窗口」

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Data; 
using System.Windows.Documents; 
using System.Windows.Input; 
using System.Windows.Media; 
using System.Windows.Media.Imaging; 
using System.Windows.Navigation; 
using System.Windows.Shapes; 
using System.ComponentModel; 

namespace WpfCommandControl 
{ 
    public partial class CommandControl : UserControl, INotifyPropertyChanged 
    { 
    #region [ Private Members ] 
    private bool _canActivated = false; 
    private int _counter = 0; 
    CommandImplementation _activateCommand; 

    #endregion 

    #region [ Properties ] 
    public int CommandCounter 
    { 
     get 
     { 
      return _counter; 
     } 

     set 
     { 
      _counter = value; 
      OnNotifyPropertyChanged("CommandCounter"); 
     } 

    } 

    public bool CanActivated 
    { 
     get 
     { 
      return _canActivated; 
     } 

     set 
     { 
      _canActivated = value; 
      OnNotifyPropertyChanged("CanActivated"); 
     }   
    } 
    #endregion 

    #region [ Property_Changed_Utilities ] 
    public event PropertyChangedEventHandler PropertyChanged; 

    private void OnNotifyPropertyChanged(String info) 
    { 
     // Note: Do not forget to add interface "INotifyPropertyChanged" to your class. 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(info)); 
     } 
    } 
    #endregion 

    # region [ Commands ] 
    public ICommand ActivateCommand 
    { 
     get 
     { 
      return _activateCommand; 
     } 
    } 
    #endregion 

    #region [ Constructor ] 
    public CommandControl() 
    { 
     InitializeComponent(); 
     _activateCommand = new CommandImplementation(param => this.Activate(), param => this.CanActivated); 
    } 
    #endregion 

    #region [ Methods ] 
    void Activate() 
    { 
     CommandCounter++; 
    } 
    #endregion 


} 
} 

最重要的部分:

命令被執行財產:

public ICommand ActivateCommand 
    { 
     get 
     { 
      return _activateCommand; 
     } 
    } 

所以它可以確保它會返回相關命令的實際情況,這是實例化用戶控件的構造λ-表達:

public CommandControl() 
    { 
     InitializeComponent(); 
     _activateCommand = new CommandImplementation(param => this.Activate(), param => this.CanActivated); 
    } 

拉姆達 - 表達使得該邏輯begind連接:

param => this.Activate() 

對於激活()函數至極會命令來執行被激發

void Activate() 
    { 
     CommandCounter++; 
    } 

而且

param => this.CanActivated 

用於傳遞ICommand CanExecute屬性的本地邏輯,從而使您可以控制何時可以執行該命令。

在我來說,我使用的是可以綁定到複選框的財產,但你也可以做的另一種方式......

public bool CanActivated 
    { 
     get 
     { 
      return _canActivated; 
     } 

     set 
     { 
      _canActivated = value; 
      OnNotifyPropertyChanged("CanActivated"); 
     }   
    } 

同樣是從約什 - 史密斯joshsmithonwpf.wordpress.com所示 - 我只是將其更改爲在構造函數中實例化,而不是檢查私有成員是否爲null,並在Command Property的GET部分需要時提供新實例。

其餘代碼只是執行所需的屬性和OnNotifyPropertyChanged,如MSDN上所示。

XAML很簡單 - 僅用於概念驗證。

<UserControl x:Class="WpfCommandControl.CommandControl" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:local="clr-namespace:WpfCommandControl" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     d:DesignHeight="300" 
     d:DesignWidth="300" 
     mc:Ignorable="d"> 
<Grid> 
    <StackPanel> 
     <CheckBox Content="Activate" IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, Path=CanActivated}" /> 
     <Button Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, 
                   AncestorType=UserControl}, 
            Path=ActivateCommand}" 
       Content="Click me" 
       IsEnabled="{Binding RelativeSource={RelativeSource Mode=FindAncestor, 
                    AncestorType=UserControl}, 
            Path=CanActivated}" /> 
     <Label Content="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, Path=CommandCounter}" IsEnabled="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, Path=CanActivated}" /> 
    </StackPanel> 
</Grid> 

正如你所看到的只有一個複選框 - 綁定將提供啓動按鈕的開/禁用。 點擊按鈕會觸發簡單增加計數器的命令 - 再次顯示在標籤上的綁定。

把所有在一起:

只是一個簡單的XAML格式有四個用戶控件:

<Window x:Class="CommandsTest.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="clr-namespace:CommandsTest" 
    xmlns:uctrl="clr-namespace:WpfCommandControl;assembly=WpfCommandControl" 
    Title="MainWindow" 
    Width="525" 
    Height="350"> 

<Grid> 
    <Grid.RowDefinitions> 
     <RowDefinition /> 
     <RowDefinition /> 
    </Grid.RowDefinitions> 

    <Grid.ColumnDefinitions> 
     <ColumnDefinition /> 
     <ColumnDefinition /> 
    </Grid.ColumnDefinitions> 

    <uctrl:CommandControl Grid.Row="0" Grid.Column="0" /> 

    <uctrl:CommandControl Grid.Row="0" Grid.Column="1" /> 

    <uctrl:CommandControl Grid.Row="1" Grid.Column="0" /> 

    <uctrl:CommandControl Grid.Row="1" Grid.Column="1" /> 

</Grid> 

上的每個控制觸發的命令是完全一樣的所需的元素中。

全部解決在WPF的方式 - 使用命令和綁定沒有與GUI元素的任何直接的互動,從而GUI可以不需要在後面的代碼更新的交換。

再次感謝你讓我說,也有另一種(例如安全)的方式來實現在WPF自定義命令。