Подтвердить что ты не робот

Как повысить свойства Измененные события в зависимости от свойства?

ОК, поэтому у меня есть этот элемент управления с двумя свойствами. Один из них - 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.

4b9b3361

Ответ 1

  • Внесите 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"));
   }
}

Ответ 2

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

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

Ответ 3

Я думаю, что ОП задает неправильный вопрос. В приведенном ниже коде будет показано, что для достижения желаемого результата вручную не нужно вручную создавать свойство 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>

Ответ 4

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

То, что я сделал, - это реализовать оба свойства как DependencyProperties, как предлагает Сэм, но тогда просто PropertyChangedCallback из свойства "first" изменил значение свойства "второй" зависимости. Поскольку оба являются зависимыми свойствами, оба автоматически повышают уведомления об изменениях любым заинтересованным абонентам (например, привязка данных и т.д.).

В этом случае свойство зависимостей A является строкой InviteText, которая вызывает изменение свойства зависимости B, свойство Видимость с именем 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.

Ответ 5

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

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

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