web-dev-qa-db-ja.com

依存関係プロパティのプロパティ変更イベントを発生させる方法は?

OK、2つのプロパティを持つこのコントロールがあります。これらの1つはDependencyPropertyであり、もう1つは最初の1つに対する「エイリアス」です。私ができる必要があるのは、最初のイベントが変更されたときに、2番目のイベント(エイリアス)のPropertyChangedイベントを発生させることです。

[〜#〜] note [〜#〜]:INotifyPropertyChangedではなく、DependencyObjectsを使用しています(コントロールがサブクラスのListViewであるため動作しませんでした)

このようなもの.....

protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
    base.OnPropertyChanged(e);
    if (e.Property == MyFirstProperty)
    {
        RaiseAnEvent( MySecondProperty ); /// what is the code that would go here?
    }    
}

INotifyを使用している場合、このようにすることができます...

public string SecondProperty
{
    get
    {
        return this.m_IconPath;
    }
}

public string IconPath
{
    get
    {
        return this.m_IconPath;
    }
    set
    {
        if (this.m_IconPath != value)
        {
            this.m_IconPath = value;
        this.SendPropertyChanged("IconPath");
        this.SendPropertyChanged("SecondProperty");
        }
    }
}

1つのセッターから複数のプロパティでPropertyChangedイベントを発生させることができます。 DependencyPropertiesのみを使用して、同じことを実行できるようにする必要があります。

60
Muad'Dib
  1. クラスにINotifyPropertyChangedを実装します。

  2. 依存関係プロパティを登録するときに、プロパティメタデータでコールバックを指定します。

  3. コールバックで、PropertyChangedイベントを発生させます。

コールバックの追加:

public static DependencyProperty FirstProperty = DependencyProperty.Register(
  "First", 
  typeof(string), 
  typeof(MyType),
  new FrameworkPropertyMetadata(
     false, 
     new PropertyChangedCallback(OnFirstPropertyChanged)));

コールバックでPropertyChangedを上げる:

private static void OnFirstPropertyChanged(
   DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
   PropertyChangedEventHandler h = PropertyChanged;
   if (h != null)
   {
      h(sender, new PropertyChangedEventArgs("Second"));
   }
}
38
Robert Rossney

サービスから関連データを取得するためにクラスが変更イベントをリッスンするようにしたい依存関係プロパティがある、同様の問題に遭遇しました。

public static readonly DependencyProperty CustomerProperty = 
    DependencyProperty.Register("Customer", typeof(Customer),
        typeof(CustomerDetailView),
        new PropertyMetadata(OnCustomerChangedCallBack));

public Customer Customer {
    get { return (Customer)GetValue(CustomerProperty); }
    set { SetValue(CustomerProperty, value); }
}

private static void OnCustomerChangedCallBack(
        DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
    CustomerDetailView c = sender as CustomerDetailView;
    if (c != null) {
        c.OnCustomerChanged();
    }
}

protected virtual void OnCustomerChanged() {
    // Grab related data.
    // Raises INotifyPropertyChanged.PropertyChanged
    OnPropertyChanged("Customer");
}
54
Brett Ryan

OPは間違った質問をしていると思います。以下のコードは、必要な結果を得るために、依存関係プロパティからPropertyChanged EVENTを手動で発生させる必要がないことを示します。それを行う方法は、依存関係プロパティでPropertyChanged CALLBACKを処理し、そこで他の依存関係プロパティの値を設定することです。以下は実際の例です。以下のコードでは、MyControlにActiveTabIntとActiveTabStringの2つの依存関係プロパティがあります。ユーザーがホスト(メインウィンドウ)のボタンをクリックすると、ActiveTabStringが変更されます。依存関係プロパティのPropertyChanged CALLBACKは、ActiveTabIntの値を設定します。 PropertyChanged EVENTは、MyControlによって手動で発生しません。

MainWindow.xaml.cs

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
        ActiveTabString = "zero";
    }

    private string _ActiveTabString;
    public string ActiveTabString
    {
        get { return _ActiveTabString; }
        set
        {
            if (_ActiveTabString != value)
            {
                _ActiveTabString = value;
                RaisePropertyChanged("ActiveTabString");
            }
        }
    }

    private int _ActiveTabInt;
    public int ActiveTabInt
    {
        get { return _ActiveTabInt; }
        set
        {
            if (_ActiveTabInt != value)
            {
                _ActiveTabInt = value;
                RaisePropertyChanged("ActiveTabInt");
            }
        }
    }

    #region INotifyPropertyChanged implementation
    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        ActiveTabString = (ActiveTabString == "zero") ? "one" : "zero";
    }

}

public class MyControl : Control
{
    public static List<string> Indexmap = new List<string>(new string[] { "zero", "one" });


    public string ActiveTabString
    {
        get { return (string)GetValue(ActiveTabStringProperty); }
        set { SetValue(ActiveTabStringProperty, value); }
    }

    public static readonly DependencyProperty ActiveTabStringProperty = DependencyProperty.Register(
        "ActiveTabString",
        typeof(string),
        typeof(MyControl), new FrameworkPropertyMetadata(
            null,
            FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
            ActiveTabStringChanged));


    public int ActiveTabInt
    {
        get { return (int)GetValue(ActiveTabIntProperty); }
        set { SetValue(ActiveTabIntProperty, value); }
    }
    public static readonly DependencyProperty ActiveTabIntProperty = DependencyProperty.Register(
        "ActiveTabInt",
        typeof(Int32),
        typeof(MyControl), new FrameworkPropertyMetadata(
            new Int32(),
            FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));


    static MyControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(MyControl), new FrameworkPropertyMetadata(typeof(MyControl)));

    }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
    }


    private static void ActiveTabStringChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        MyControl thiscontrol = sender as MyControl;

        if (Indexmap[thiscontrol.ActiveTabInt] != thiscontrol.ActiveTabString)
            thiscontrol.ActiveTabInt = Indexmap.IndexOf(e.NewValue.ToString());

    }
}

MainWindow.xaml

    <StackPanel Orientation="Vertical">
    <Button Content="Change Tab Index" Click="Button_Click" Width="110" Height="30"></Button>
    <local:MyControl x:Name="myControl" ActiveTabInt="{Binding ActiveTabInt, Mode=TwoWay}" ActiveTabString="{Binding ActiveTabString}"></local:MyControl>
</StackPanel>

App.xaml

<Style TargetType="local:MyControl">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="local:MyControl">
                    <TabControl SelectedIndex="{Binding ActiveTabInt, Mode=TwoWay}">
                        <TabItem Header="Tab Zero">
                            <TextBlock Text="{Binding ActiveTabInt}"></TextBlock>
                        </TabItem>
                        <TabItem Header="Tab One">
                            <TextBlock Text="{Binding ActiveTabInt}"></TextBlock>
                        </TabItem>
                    </TabControl>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
10
Sam

私はサムとザザーに同意し、実際にこれをもう少し進めました。 UserControlにINotifyPropertyChangedインターフェイスを実装する必要はないと思います...コントロールは既にDependencyObjectであるため、既に通知が付属しています。 INotifyPropertyChangedをDependencyObjectに追加することは冗長であり、「臭い」が間違っています。

私がしたことは、Samが示唆するように、両方のプロパティをDependencyPropertiesとして実装しますが、「最初の」依存関係プロパティのPropertyChangedCallbackで「2番目の」依存関係プロパティの値を変更するだけです。両方が依存関係プロパティであるため、両方とも関心のあるサブスクライバーに変更通知を自動的に発生させます(データバインディングなど)

この場合、依存関係プロパティAは文字列InviteTextであり、依存関係プロパティB、つまりShowInviteというVisibilityプロパティの変更をトリガーします。これは、データバインディングを介してコントロール内で完全に非表示にしたいテキストがある場合の一般的な使用例です。

    public string InviteText  
    {
        get { return (string)GetValue(InviteTextProperty); }
        set { SetValue(InviteTextProperty, value); }
    }

    public static readonly DependencyProperty InviteTextProperty =
        DependencyProperty.Register("InviteText", typeof(string), typeof(InvitePrompt), new UIPropertyMetadata(String.Empty, OnInviteTextChanged));

    private static void OnInviteTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        InvitePrompt Prompt = d as InvitePrompt;
        if (Prompt != null)
        {
            string text = e.NewValue as String;
            Prompt.ShowInvite = String.IsNullOrWhiteSpace(text) ? Visibility.Collapsed : Visibility.Visible;
        }
    }

    public Visibility ShowInvite
    {
        get { return (Visibility)GetValue(ShowInviteProperty); }
        set { SetValue(ShowInviteProperty, value); }
    }

    public static readonly DependencyProperty ShowInviteProperty =
        DependencyProperty.Register("ShowInvite", typeof(Visibility), typeof(InvitePrompt), new PropertyMetadata(Visibility.Collapsed));

注ここでは、UserControlの署名またはコンストラクターは特別なものがないため、ここには含めていません。 INotifyPropertyChangedからサブクラス化する必要はまったくありません。

3
karfus

変更されている最初のプロパティである場合、2番目のプロパティでPropertyChangedイベントを発生させるロジックに疑問があります。 2番目のプロパティ値が変更されると、そこでPropertyChangedイベントが発生する可能性があります。

とにかく、あなたの質問に対する答えは、INotifyPropertyChangeを実装することです。このインターフェイスには、PropertyChangedイベントが含まれます。 INotifyPropertyChangedを実装すると、クラスにPropertyChangedイベントがあることを他のコードが認識できるため、コードでハンドラーをフックできます。 INotifyPropertyChangeを実装した後、OnPropertyChangedのifステートメントに含まれるコードは次のとおりです。

if (PropertyChanged != null)
    PropertyChanged(new PropertyChangedEventArgs("MySecondProperty"));
0
Scott J