2

「背後」の静的クラスなしで、カスタム コントロールの「インスタンスのみ」の ICommand 実装を作成する方法はありますか?

以前に作成したカスタム コントロールを更新しようとしています。

目標の 1 つは、マルチインスタンス機能を確保することです。

同じカスタム コントロールの 2 つ以上のインスタンスが同じアプリケーションで使用された場合、背後で使用されている静的クラスからの干渉が (予想どおり) 発生します。

ほとんどを取り除く方法を見つけましたが、ICommand に問題がありました。

カスタム コントロールの特定の GUI アイテムには、ユーザー コントロール インスタンス内でのみ有効でなければならないコマンドがあります。代わりに、コマンドがすべてのインスタンスに干渉しています (例として、CanExecute は、「ローカル」条件が存在しないユーザー コントロール インスタンスで GUI アイテムをアクティブにします。会った)。

4

3 に答える 3

2

コマンドを作成し、それを ViewModel のプロパティとして公開してから、コントロールでバインドできます。

あなたのViewModelで:

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

あなたのコントロールで:

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

MVVM パターンを使用していない場合は、同じフィールドを作成する必要がありますDataContext(おそらくコントロール コード ビハインド)。

コマンドを定義するために Dependency プロパティを使用することもできます。ユーザー コントロールの作成後にコマンドを変更する場合は、それを使用する必要があります。

一般に:

WPF / C# で記述する際のオプションを知るために、MVVM パターン、依存関係プロパティ、DataContext、Binding について読むことをお勧めします。これらのいくつかは既に知っているかもしれません。

于 2013-06-06T08:44:31.650 に答える
1

回答と説明をありがとうございました!

あなたは私にすべての決定的なキックを与えたので、私はそれを理解しました。意図的に完全な例を追加しました。

あなたのアドバイス (Ron BI と Dennis) に従って、まず ViewModel についてもっと読みたいと思いました。

http://msdn.microsoft.com/en-ca/magazine/dd419663.aspxの下には、非静的クラスの背後にある例があります。したがって、解決策は、ユーザー コントロールに新しいクラスを追加するだけでした (前述のサイトに示されているとおりです - 図 3 - 一部の名前が変更されました - 著作権は Josh Smith 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;
        }
    }

そのため、ユーザー コントロールのコンストラクターで Lambda-Expression を使用してインスタンス化された実際のインスタンス関連のコマンドが返されるようにします。

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

ラムダ - 式は、ロジックへの接続を開始します。

param => this.Activate()

コマンドが起動されると実行される Activate() 関数の場合

    void Activate()
    {
        CommandCounter++;
    }

param => this.CanActivated

ICommand CanExecute プロパティの背後にあるローカル ロジックを渡し、コマンドをいつ実行できるかを制御できるようにします。

私の場合、CheckBoxにバインドできるプロパティを使用しましたが、別の方法で行うこともできます...

    public bool CanActivated
    {
        get
        {
            return _canActivated;
        }

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

これも Josh Smith joshsmithonwpf.wordpress.com に示されているとおりです。コマンド プロパティの GET 部分で必要に応じて、プライベート メンバーが null であるかどうかを確認し、新しいインスタンスを配信するかどうかを確認する代わりに、コンストラクターでインスタンス化するように変更しました。

コードの残りの部分は、MSDN に示されているように、必要なプロパティと OnNotifyPropertyChanged の実装です。

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>

ご覧のとおり、CheckBox しかありません。バインディングは、ボタンの有効化/無効化を提供します。ボタンをクリックすると、単純にカウンターをインクリメントするコマンドが起動されます。これは、再びラベルに表示され、バインディングを介して表示されます。

すべてをまとめる:

4 つのユーザー コントロールを含む 1 つの単純な 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 でカスタム コマンドを実装する別の (インスタンス セーフな) 方法もあると教えていただき、ありがとうございます。

于 2013-06-07T08:17:49.003 に答える
1

CanExecute および Execute メソッドには、それらが作用するはずのオブジェクトにリンクするパラメーターがないという事実に混乱するかもしれません。

ただし、ICommand インターフェイスはクラスによって実装する必要があり、そのクラスのオブジェクトは、通常コンストラクターで初期化されるフィールドを持つことができ、また持つ必要があることに注意してください。

たとえば、MVVM パターンに従う場合 (Ron.BI で既に説明されているように)、通常、コマンドにはビューモデルへの参照があります。または、RelayCommand のようなものを使用して、ビューモデルをデリゲートまたはラムダ クロージャー オブジェクトにキャプチャすることもできます。

于 2013-06-06T08:53:46.157 に答える