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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 21:35:50  来源:igfitidea点击:

How To Raise Property Changed events on a Dependency Property?

wpfbinding

提问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 PropertyChangedevent 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 ListViesub-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 PropertyChangedevents 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

  1. Implement INotifyPropertyChangedin your class.

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

  3. In the callback, raise the PropertyChangedevent.

  1. INotifyPropertyChanged在你的课堂上实施。

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

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

Adding the callback:

添加回调:

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

Raising PropertyChangedin 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 PropertyChangedEVENT from a dependency property to achieve the desired result. The way to do it is handle the PropertyChangedCALLBACK on the dependency property and set values for other dependency properties there. The following is a working example. In the code below, MyControlhas two dependency properties - ActiveTabIntand ActiveTabString. When the user clicks the button on the host (MainWindow), ActiveTabStringis modified. The PropertyChangedCALLBACK on the dependency property sets the value of ActiveTabInt. The PropertyChangedEVENT is not manually raised by MyControl.

我认为 OP 问错了问题。下面的代码将表明没有必要PropertyChanged从依赖属性手动引发EVENT 来实现所需的结果。这样做的方法是处理PropertyChanged依赖属性上的CALLBACK 并在那里设置其他依赖属性的值。下面是一个工作示例。在下面的代码中,MyControl有两个依赖属性 -ActiveTabIntActiveTabString. 当用户单击主机上的按钮 ( 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 INotifyPropertyChangedinterface in a UserControlat all...the control is already a DependencyObjectand therefore already comes with notifications. Adding INotifyPropertyChangedto a DependencyObjectis 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 PropertyChangedCallbackfrom 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 Visibilityproperty 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 UserControlsignature or constructor here because there is nothing special about them; they don't need to subclass from INotifyPropertyChangedat all.

注意我没有在UserControl这里包含签名或构造函数,因为它们没有什么特别之处;他们根本不需要从子类化INotifyPropertyChanged

回答by Scott J

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

PropertyChanged当第一个属性发生变化时,我质疑在第二个属性上引发事件的逻辑。如果第二个属性值发生变化,则PropertyChanged事件可能会在那里引发。

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

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

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