Как вызвать свойства Измененные события в свойстве зависимости? - PullRequest
59 голосов
/ 19 марта 2010

ОК, поэтому у меня есть этот элемент управления с двумя свойствами. Один из них - DependencyProperty, другой - псевдоним первого. То, что мне нужно сделать, это вызвать событие PropertyChanged для второго (псевдоним), когда изменяется первое.

ПРИМЕЧАНИЕ : я использую DependencyObjects, а не INotifyPropertyChanged (пробовал это, не работал, потому что мой элемент управления - это подклассифицированный ListView)

как то так .....

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.

Ответы [ 5 ]

51 голосов
/ 22 июня 2011

Я столкнулся с аналогичной проблемой, когда у меня есть свойство зависимости, которое я хотел, чтобы класс прослушивал события изменения, чтобы получить связанные данные из службы.

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");
}
38 голосов
/ 20 марта 2010
  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"));
   }
}
10 голосов
/ 08 октября 2013

Я думаю, что ОП задает не тот вопрос. Код ниже покажет, что нет необходимости вручную вызывать PropertyChanged EVENT из свойства зависимостей для достижения желаемого результата. Способ сделать это - обработать PropertyChanged CALLBACK для свойства зависимостей и установить значения для других свойств зависимостей. Ниже приведен рабочий пример. В приведенном ниже коде MyControl имеет два свойства зависимостей - ActiveTabInt и ActiveTabString. Когда пользователь нажимает кнопку на хосте (MainWindow), ActiveTabString изменяется. PropertyChanged CALLBACK для свойства зависимостей устанавливает значение 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>

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>
3 голосов
/ 24 апреля 2015

Я согласен с Сэмом и Ксасером и, на самом деле, понял это немного дальше. Я не думаю, что вы должны реализовывать интерфейс INotifyPropertyChanged в UserControl вообще ... элемент управления уже является DependencyObject и, следовательно, уже поставляется с уведомлениями. Добавление INotifyPropertyChanged к DependencyObject является излишним и «пахнет» неправильно для меня.

Что я сделал, так это реализовал оба свойства как DependencyProperties, как предлагает Сэм, но затем просто получил PropertyChangedCallback из «первого» свойства зависимостей, чтобы изменить значение «второго» свойства зависимостей. Поскольку оба являются свойствами зависимостей, оба автоматически отправляют уведомления об изменениях всем заинтересованным подписчикам (например, привязка данных и т. Д.)

В этом случае свойство зависимости 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));

Примечание. Я не включаю здесь сигнатуру или конструктор UserControl, потому что в них нет ничего особенного; им вообще не нужно создавать подклассы из INotifyPropertyChanged.

0 голосов
/ 20 марта 2010

Я подвергаю сомнению логику вызова события PropertyChanged во втором свойстве, когда меняется первое свойство. Если значение второго свойства изменится, тогда может возникнуть событие PropertyChanged.

В любом случае, ответ на ваш вопрос заключается в том, что вы должны внедрить INotifyPropertyChange. Этот интерфейс содержит событие PropertyChanged. Реализация INotifyPropertyChanged позволяет другому коду знать, что класс имеет событие PropertyChanged, так что код может подключить обработчик. После реализации INotifyPropertyChange код, который входит в оператор if вашего OnPropertyChanged, выглядит так:

if (PropertyChanged != null)
    PropertyChanged(new PropertyChangedEventArgs("MySecondProperty"));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...