wpf 如何在依赖项属性上引发属性更改事件?

1zmg4dgp  于 2023-03-13  发布在  其他
关注(0)|答案(6)|浏览(222)

我有一个有两个属性的控件。一个是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

zzzyeukh

zzzyeukh1#

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

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

blmhpbnm2#

1.在类中实现INotifyPropertyChanged
1.注册依赖项属性时,在属性元数据中指定回调。
1.在回调中,引发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"));
   }
}
ulydmbyx

ulydmbyx3#

我认为OP问错了问题。下面的代码将显示,没有必要手动从依赖属性引发PropertyChanged EVENT来获得所需的结果。方法是处理依赖属性上的PropertyChanged CALLBACK,并在那里设置其他依赖属性的值。下面是一个工作示例。在下面的代码中,MyControl具有两个相关性属性-ActiveTabIntActiveTabString。当用户单击主机上的按钮(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>
hkmswyz6

hkmswyz64#

我同意Sam和Xaser的观点,并且实际上做得更深入一些。我认为您根本不应该在UserControl中实现INotifyPropertyChanged接口...该控件已经是DependencyObject,因此已经带有通知。将INotifyPropertyChanged添加到DependencyObject是多余的,而且在我看来“闻起来”不对劲。
我所做的是按照Sam的建议将这两个属性都实现为DependencyProperties,然后简单地让“first”依赖属性中的PropertyChangedCallback更改“second”依赖属性的值。由于这两个属性都是依赖属性,因此它们都会自动向任何感兴趣的订阅者发出更改通知(例如,数据绑定等)。
在本例中,依赖项属性A是字符串InviteText,它触发依赖项属性B(名为ShowInviteVisibility属性)的更改。如果您希望能够通过数据绑定将某些文本完全隐藏在控件中,则这是一种常见的使用情况。

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继承子类。

b5lpy0ml

b5lpy0ml5#

我怀疑当第一个属性发生变化时,在第二个属性上引发PropertyChanged事件的逻辑。如果第二个属性值发生变化,那么PropertyChanged事件可能会在那里引发。
无论如何,你的问题的答案是你应该实现INotifyPropertyChange。这个接口包含PropertyChanged事件。实现INotifyPropertyChanged可以让其他代码知道这个类有PropertyChanged事件,这样代码就可以挂接一个处理程序。实现INotifyPropertyChange之后,进入OnPropertyChanged的if语句的代码是:

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

2ic8powd6#

基于前面已被接受的答案,对我来说,没有强制转换就无法访问非静态PropertyChanged
1.在类中实现INotifyPropertyChanged,例如UserControl CustomView
1.注册依赖项属性时,在属性元数据中指定回调。
1.在回调中,强制转换并引发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)
{
   var control = (CustomView)sender;
   PropertyChangedEventHandler h = control.PropertyChanged;
   if (h != null)
   {
      h(sender, new PropertyChangedEventArgs("Second"));
   }
}

相关问题