开发者

How To Raise Property Changed events on a Dependency Property?

I have a control with two properties. One is a DependencyProperty, the other is an "alias" to the first one. How do I raise the PropertyChanged event for the second one (the alias) when the first one is changed.

NOTE: I'm using DependencyObjects, not INotifyPropertyChange开发者_Go百科d (tried that, didn't work because my control is a ListVie sub-classed)

Something like this.....

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

If I were using an INotify I could do like this...

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

Where can I raise PropertyChanged events on multiple properties from one setter? I need to be able to do the same thing, only using DependencyProperties.


I ran into a similar problem where I have a dependency property that I wanted the class to listen to change events to grab related data from a service.

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


  1. Implement INotifyPropertyChanged in your class.

  2. Specify a callback in the property metadata when you register the dependency property.

  3. In the callback, raise the PropertyChanged event.

Adding the callback:

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

Raising PropertyChanged in the callback:

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


I think the OP is asking the wrong question. The code below will show that it not necessary to manually raise the PropertyChanged EVENT from a dependency property to achieve the desired result. The way to do it is handle the PropertyChanged CALLBACK on the dependency property and set values for other dependency properties there. The following is a working example. In the code below, MyControl has two dependency properties - ActiveTabInt and ActiveTabString. When the user clicks the button on the host (MainWindow), ActiveTabString is modified. The PropertyChanged CALLBACK on the dependency property sets the value of ActiveTabInt. The PropertyChanged EVENT is not manually raised by 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>


I agree with Sam and Xaser and have actually taken this a bit farther. I don't think you should be implementing the INotifyPropertyChanged interface in a UserControl at all...the control is already a DependencyObject and therefore already comes with notifications. Adding INotifyPropertyChanged to a DependencyObject is redundant and "smells" wrong to me.

What I did is implement both properties as DependencyProperties, as Sam suggests, but then simply had the PropertyChangedCallback from the "first" dependency property alter the value of the "second" dependency property. Since both are dependency properties, both will automatically raise change notifications to any interested subscribers (e.g. data binding etc.)

In this case, dependency property A is the string InviteText, which triggers a change in dependency property B, the Visibility property named ShowInvite. This would be a common use case if you have some text that you want to be able to hide completely in a control via data binding.

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

Note I'm not including the UserControl signature or constructor here because there is nothing special about them; they don't need to subclass from INotifyPropertyChanged at all.


I question the logic of raising a PropertyChanged event on the second property when it's the first property that's changing. If the second properties value changes then the PropertyChanged event could be raised there.

At any rate, the answer to your question is you should implement INotifyPropertyChange. This interface contains the PropertyChanged event. Implementing INotifyPropertyChanged lets other code know that the class has the PropertyChanged event, so that code can hook up a handler. After implementing INotifyPropertyChange, the code that goes in the if statement of your OnPropertyChanged is:

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜