如何在依赖属性上引发属性更改事件?
Posted
技术标签:
【中文标题】如何在依赖属性上引发属性更改事件?【英文标题】:How To Raise Property Changed events on a Dependency Property? 【发布时间】:2011-01-29 15:17:54 【问题描述】:我有一个具有两个属性的控件。一个是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");
我可以在哪里从一个 setter 引发多个属性的 PropertyChanged
事件?我需要能够做同样的事情,只使用DependencyProperties
。
【问题讨论】:
如果您没有使用 INotifyPropertyChanged,则没有要引发的 PropertyChanged 事件(除非您在谈论自定义事件?)。另外,我不确定“别名”属性是 DP 还是普通 CLR 属性——你能澄清一下吗?谢谢! “别名”不是 DP,但我希望它像一个 DP。这样当第一个属性发生更改时,我的 UI 将收到另一个已更改的通知。 【参考方案1】:我遇到了类似的问题,我有一个依赖属性,我希望类监听更改事件以从服务中获取相关数据。
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");
【讨论】:
我知道我迟到了,但这个答案解决了我的疑惑,谢谢! 晚了几个月 - 但这就像一个魅力。我一直试图破解这个有一段时间了。谢谢! 这似乎比接受的答案更合适。 这允许Label
具有IsSelected
属性(如TreeViewItem
行为)来更改背景颜色。完美。
我知道我来晚了,但我只是在谷歌搜索时发现了这一点,其他人也可能如此。我喜欢这种方法,但您可以像这样内联回调减少开销:new PropertyMetadata((o,e) => (o as Customer)?.OnCustomerChanged()));【参考方案2】:
在你的课堂上实现INotifyPropertyChanged
。
注册依赖属性时在属性元数据中指定回调。
在回调中,引发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"));
【讨论】:
first 已经是一个 dp,继承自我的基类 (ListView),并且我正在捕获 OnPropertyChanged 中的属性更改。我会试试这个,看看我们得到了什么。 如何从静态方法中获取对PropertyChanged
事件的引用?您首先需要投射到 INotifyPropertyChanged
并访问那里的事件。
您的答案不起作用,因为您试图通过静态方法访问非静态成员。您应该按照@BrettRyan 的状态编辑您的答案。
静态方法中如何调用propertychanged?
这似乎不起作用。似乎如果一个对象是 DependencyObject
,WPF 引擎会忽略 INotifyPropertyChanged
并且不订阅它的事件。【参考方案3】:
我认为 OP 提出了错误的问题。下面的代码将表明,无需手动从依赖属性中引发PropertyChanged
EVENT 即可获得所需的结果。这样做的方法是处理依赖属性上的PropertyChanged
CALLBACK 并在那里设置其他依赖属性的值。以下是一个工作示例。
在下面的代码中,MyControl
有两个依赖属性 - ActiveTabInt
和 ActiveTabString
。当用户点击主机(MainWindow
)上的按钮时,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>
【讨论】:
我对此有一个怪癖,我找不到RaisePropertyChanged(
,而是不得不使用RaisePropertyChangedEventImmediately(
【参考方案4】:
我同意 Sam 和 Xaser 的观点,并且实际上已经走得更远了。我认为您根本不应该在UserControl
中实现INotifyPropertyChanged
接口……控件已经是DependencyObject
,因此已经带有通知。将INotifyPropertyChanged
添加到DependencyObject
是多余的,并且对我来说“闻起来”是错误的。
我所做的是将这两个属性都实现为DependencyProperties
,正如 Sam 所建议的那样,但随后只是让“第一个”依赖属性中的 PropertyChangedCallback
改变了“第二个”依赖属性的值。由于两者都是依赖属性,因此两者都会自动向任何感兴趣的订阅者发出更改通知(例如数据绑定等)
在这种情况下,依赖属性 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
继承。
【讨论】:
我同意你的观点,使用 INPC 没有意义。不过,您的示例的行为与 OP 的示例不同。您创建的重复数据可能会变得不同步:ShowInvite
可以公开更改,但不会更新 InviteText
。 OP 的 SecondProperty
是只读的。 ShowInvite
应该是只读的 DependencyProperty。【参考方案5】:
我质疑当第一个属性发生变化时在第二个属性上引发PropertyChanged
事件的逻辑。如果第二个属性值发生变化,则可能会在此处引发 PropertyChanged
事件。
无论如何,您的问题的答案是您应该实施INotifyPropertyChange
。此接口包含PropertyChanged
事件。实现INotifyPropertyChanged
让其他代码知道该类具有PropertyChanged
事件,以便代码可以连接处理程序。实现INotifyPropertyChange
后,OnPropertyChanged
的 if 语句中的代码为:
if (PropertyChanged != null)
PropertyChanged(new PropertyChangedEventArgs("MySecondProperty"));
【讨论】:
我是从 ListView 控件派生的。我可以添加 INotify,事实上,我试过了。没有骰子,当我试图引发事件时,什么也没发生。我想要“alais”的属性是依赖属性。以上是关于如何在依赖属性上引发属性更改事件?的主要内容,如果未能解决你的问题,请参考以下文章
IOC 控制反转Android 事件依赖注入 ( 事件依赖注入具体的操作细节 | 获取 Activity 中的所有方法 | 获取方法上的注解 | 获取注解上的注解 | 通过注解属性获取事件信息 )(代