wpf 如何在依赖属性上引发属性更改事件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2480366/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How To Raise Property Changed events on a Dependency Property?
提问by Muad'Dib
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.
我有一个具有两个属性的控件。一个是 a DependencyProperty
,另一个是第一个的“别名”。PropertyChanged
当第一个事件发生更改时,如何引发第二个事件(别名)的事件。
NOTE: I'm using DependencyObjects
, not INotifyPropertyChanged
(tried that, didn't work because my control is a ListVie
sub-classed)
注意:我正在使用DependencyObjects
, not INotifyPropertyChanged
(试过了,没有用,因为我的控件是一个ListVie
子类)
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...
如果我使用的是 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");
}
}
}
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
.
我在哪里可以PropertyChanged
从一个 setter 中引发多个属性的事件?我需要能够做同样的事情,只使用DependencyProperties
.
采纳答案by Robert Rossney
Implement
INotifyPropertyChanged
in your class.Specify a callback in the property metadata when you register the dependency property.
In the callback, raise the
PropertyChanged
event.
INotifyPropertyChanged
在你的课堂上实施。注册依赖属性时在属性元数据中指定回调。
在回调中,引发
PropertyChanged
事件。
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:
PropertyChanged
在回调中提高:
private static void OnFirstPropertyChanged(
DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
PropertyChangedEventHandler h = PropertyChanged;
if (h != null)
{
h(sender, new PropertyChangedEventArgs("Second"));
}
}
回答by Brett Ryan
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");
}
回答by Sam
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
.
我认为 OP 问错了问题。下面的代码将表明没有必要PropertyChanged
从依赖属性手动引发EVENT 来实现所需的结果。这样做的方法是处理PropertyChanged
依赖属性上的CALLBACK 并在那里设置其他依赖属性的值。下面是一个工作示例。在下面的代码中,MyControl
有两个依赖属性 -ActiveTabInt
和ActiveTabString
. 当用户单击主机上的按钮 ( MainWindow
) 时,ActiveTabString
被修改。在PropertyChanged
对依赖项属性回调设置的值ActiveTabInt
。该PropertyChanged
事件不手动升起MyControl
。
MainWindow.xaml.cs
主窗口.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
主窗口.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
应用程序.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>
回答by karfus
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.
我同意 Sam 和 Xaser 的观点,并且实际上更进一步。我认为您根本不应该INotifyPropertyChanged
在 aUserControl
中实现接口......控件已经是 a DependencyObject
,因此已经带有通知。添加INotifyPropertyChanged
到 aDependencyObject
是多余的,对我来说“闻起来”是错误的。
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.)
我所做的是将两个属性都实现为DependencyProperties
,正如 Sam 所建议的那样,然后只是让PropertyChangedCallback
“第一个”依赖属性更改了“第二个”依赖属性的值。由于两者都是依赖属性,两者都会自动向任何感兴趣的订阅者发出更改通知(例如数据绑定等)
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.
在这种情况下,依赖属性 A 是字符串InviteText
,它会触发依赖属性 B(Visibility
名为的属性)的更改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));
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.
注意我没有在UserControl
这里包含签名或构造函数,因为它们没有什么特别之处;他们根本不需要从子类化INotifyPropertyChanged
。
回答by Scott J
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.
PropertyChanged
当第一个属性发生变化时,我质疑在第二个属性上引发事件的逻辑。如果第二个属性值发生变化,则PropertyChanged
事件可能会在那里引发。
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:
无论如何,您的问题的答案是您应该实施INotifyPropertyChange
. 该接口包含PropertyChanged
事件。实现INotifyPropertyChanged
让其他代码知道该类具有该PropertyChanged
事件,以便代码可以连接一个处理程序。实现之后INotifyPropertyChange
,你的 if 语句中的代码OnPropertyChanged
是:
if (PropertyChanged != null)
PropertyChanged(new PropertyChangedEventArgs("MySecondProperty"));