如何引发依赖属性上的属性更改事件?


68

我有两个属性的控件。一个是DependencyProperty,另一个是第一个的“别名”。PropertyChanged更改第一个事件时,如何引发第二个事件(别名)的事件。

注意:我使用的DependencyObjects不是,INotifyPropertyChanged(尝试过,因为我的控件是ListVie子类的,所以没有用)

像这样...

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

在哪里可以PropertyChanged从一个设置器引发多个属性的事件?我只需要使用就能做同样的事情DependencyProperties


如果您不使用INotifyPropertyChanged,则不会引发PropertyChanged事件(除非您在谈论自定义事件?)。另外,我不确定“ alias”属性是DP还是普通CLR属性-您能澄清一下吗?谢谢!
itowlson

“别名”不是DP,但我希望它像一个DP。这样,当第一个属性更改时,我的UI会收到通知另一个属性已更改的信息。
Muad'Dib 2010年

Answers:


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

第一个已经是dp,继承自我的基类(ListView),并且在OnPropertyChanged中捕获了属性更改。我将尝试一下,看看会得到什么。
Muad'Dib'3

31
您如何PropertyChanged从静态方法中获取事件的引用?您首先需要投射到INotifyPropertyChanged并访问该事件。
布雷特·瑞安

8
由于您尝试通过静态方法访问非静态成员,因此您的答案不起作用。您应该按照@BrettRyan的状态编辑答案。
kskyriacou

1
如何调用静态方法中的propertychanged?
Bhuvanesh Narayanasamy,

1
这似乎不起作用。看来,如果对象是DependencyObject,则WPF引擎将忽略INotifyPropertyChanged并且不订阅其事件。
whatsisname

62

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

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

2
我知道我晚会晚了,但是这个答案像魅力一样解决了我的疑问,谢谢!
Nahuel Ianni 2014年

1
推迟了几个月-但这就像一个魅力。我已经尝试破解了一段时间了。谢谢!
imdandman 2015年

2
这似乎比公认的答案更合适。
IAbstract

1
这样可以使Label拥有IsSelected属性(例如TreeViewItem行为)来更改背景颜色。完善。
IAbstract 2013年

10

我认为OP提出了错误的问题。下面的代码将显示不必PropertyChanged从依赖项属性中手动提高EVENT即可获得所需的结果。做到这一点的方法是处理PropertyChanged依赖项属性上的CALLBACK并在那里设置其他依赖项属性的值。以下是一个工作示例。在下面的代码中,MyControl具有两个依赖项属性-ActiveTabIntActiveTabString。当用户单击主机上的按钮(MainWindow)时,ActiveTabString被修改。在PropertyChanged对依赖项属性回调设置的值ActiveTabInt。该PropertyChanged事件不手动升起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>

应用程式

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

我对此有一个怪癖,找不到RaisePropertyChanged(但不得不使用RaisePropertyChangedEventImmediately(
Solx 2016年

4

我同意Sam和Xaser的看法,实际上已经走得更远了。我认为您根本不应该在中实现该INotifyPropertyChanged接口UserControl...该控件已经是a DependencyObject,因此已经带有通知。添加INotifyPropertyChanged到aDependencyObject是多余的,对我来说“闻起来”是错误的。

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

在这种情况下,依赖项属性A是字符串InviteText,它会触发对Visibility名为的属性的依赖项属性B的更改ShowInvite。如果您希望通过数据绑定将某些文本完全隐藏在控件中,则这将是一个常见的用例。

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


1
我同意您的看法,使用INPC没有任何意义。但是,您的示例的行为与OP的示例不同。您创建的重复数据可能变得不同步:ShowInvite可以公开更改,但不会更新InviteText。OPSecondProperty是只读的。 ShowInvite应该是只读的DependencyProperty。
Zach Mierzejewski

0

我质疑在PropertyChanged第二个属性发生变化时在第二个属性上引发事件的逻辑。如果第二个属性值更改,则PropertyChanged可以在此处引发事件。

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

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

我从ListView控件派生。我可以添加INotify,实际上,我已经尝试过了。没有骰子,当我尝试引发事件时,什么也没发生。我要“ alais”的属性是依赖项属性。
Muad'Dib'3
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.