如何在依赖属性上提升属性更改事件?

时间:2010-03-19 20:22:24

标签: wpf binding

好的,所以我有两个属性的控件。其中一个是DependencyProperty,另一个是第一个的“别名”。我需要做的是在第一个更改时为第二个(别名)引发PropertyChanged事件。

注意:我使用的是DependencyObjects,而不是INotifyPropertyChanged(试过,因为我的控件是一个子类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");
        }
    }
}

我可以从一个setter在多个属性上引发PropertyChanged事件。我需要能够做同样的事情,只使用DependencyProperties。

5 个答案:

答案 0 :(得分:49)

我遇到了一个类似的问题,我有一个依赖属性,我希望该类监听更改事件以从服务中获取相关数据。

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 :(得分:38)

  1. 在班级中实施INotifyPropertyChanged

  2. 注册依赖项属性时,在属性元数据中指定回调。

  3. 在回调中,引发PropertyChanged事件。

  4. 添加回调:

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

答案 2 :(得分:10)

我认为OP正在提出错误的问题。下面的代码将显示没有必要从依赖项属性手动引发PropertyChanged EVENT以实现所需的结果。执行此操作的方法是处理依赖项属性上的PropertyChanged CALLBACK,并为其他依赖项属性设置值。以下是一个工作示例。 在下面的代码中,MyControl有两个依赖项属性--ActiveTabInt和ActiveTabString。当用户单击主机(MainWindow)上的按钮时,将修改ActiveTabString。依赖项属性上的PropertyChanged CALLBACK设置ActiveTabInt的值。 MyControl不会手动引发PropertyChanged EVENT。

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>

答案 3 :(得分:3)

我同意Sam和Xaser,并且实际上已经采取了这一点。我认为你根本不应该在UserControl中实现INotifyPropertyChanged接口......控件已经是DependencyObject,因此已经附带了通知。将INotifyPropertyChanged添加到DependencyObject是多余的,并且“气味”对我来说是错误的。

我所做的是将这两个属性实现为DependencyProperties,正如Sam建议的那样,但是后来只是让“first”依赖属性中的PropertyChangedCallback改变了“second”依赖属性的值。由于两者都是依赖属性,因此两者都会自动向任何感兴趣的订阅者发送更改通知(例如数据绑定等)

在这种情况下,依赖项属性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继承。

答案 4 :(得分:0)

我质疑在第二个属性上引发PropertyChanged事件的逻辑,因为它是第一个正在改变的属性。如果第二个属性值发生更改,那么可以在那里引发PropertyChanged事件。

无论如何,您的问题的答案是您应该实现INotifyPropertyChange。此接口包含PropertyChanged事件。实现INotifyPropertyChanged允许其他代码知道该类具有PropertyChanged事件,因此代码可以挂钩处理程序。实现INotifyPropertyChange后,OnPropertyChanged的if语句中的代码为:

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