wpf - data - Cómo aumentar los eventos cambiados de propiedad en una propiedad de dependencia?



wpf binding types (4)

  1. Implemente INotifyPropertyChanged en su clase.

  2. Especifique una devolución de llamada en los metadatos de propiedades cuando registre la propiedad de dependencia.

  3. En la devolución de llamada, levante el evento PropertyChanged.

Agregar la devolución de llamada:

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

Raising PropertyChanged en la devolución de llamada:

private static void OnFirstPropertyChanged(
   DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
   PropertyChangedEventHandler h = PropertyChanged;
   if (h != null)
   {
      h(sender, new PropertyChangedEventArgs("Second"));
   }
}

https://src-bin.com

OK, entonces tengo este control con dos propiedades. Uno de estos es DependencyProperty, el otro es un "alias" del primero. Lo que tengo que hacer es subir el evento PropertyChanged para el segundo (el alias) cuando se cambie el primero.

NOTA : Estoy usando DependencyObjects, no INotifyPropertyChanged (lo intenté, no funcionó porque mi control es un ListView subclasificado)

algo como esto.....

protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
    base.OnPropertyChanged(e);
    if (e.Property == MyFirstProperty)
    {
        RaiseAnEvent( MySecondProperty ); /// what is the code that would go here?
    }    
}

Si estuviera usando un INotify, podría hacer esto ...

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

donde puedo levantar eventos PropertyChanged en propiedades múltiples de un setter. Necesito poder hacer lo mismo, solo usando DependencyProperties.


Answer #1

Creo que el OP está haciendo la pregunta incorrecta. El siguiente código mostrará que no es necesario subir manualmente el EVENTO PropertyChanged desde una propiedad de dependencia para lograr el resultado deseado. La forma de hacerlo es manejar el PropertyChanged CALLBACK en la propiedad de dependencia y establecer valores para otras propiedades de dependencia allí. El siguiente es un ejemplo de trabajo. En el siguiente código, MyControl tiene dos propiedades de dependencia: ActiveTabInt y ActiveTabString. Cuando el usuario hace clic en el botón del host (MainWindow), se modifica ActiveTabString. PropertyChanged CALLBACK en la propiedad de dependencia establece el valor de ActiveTabInt. El EVENTO PropertyChanged no es levantado manualmente por 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>

Answer #2

Estoy de acuerdo con Sam y Xaser y he llevado esto un poco más lejos. No creo que deba implementar la interfaz INotifyPropertyChanged en un UserControl en absoluto ... el control ya es un DependencyObject y, por lo tanto, ya viene con notificaciones. Agregar INotifyPropertyChanged a DependencyObject es redundante y "me huele mal".

Lo que hice fue implementar ambas propiedades como DependencyProperties, como sugiere Sam, pero luego simplemente hice que PropertyChangedCallback de la "primera" propiedad de dependencia alterara el valor de la "segunda" propiedad de dependencia. Dado que ambas son propiedades de dependencia, ambas generarán automáticamente notificaciones de cambio a cualquier suscriptor interesado (por ejemplo, enlace de datos, etc.)

En este caso, la propiedad de dependencia A es la cadena InviteText, que desencadena un cambio en la propiedad de dependencia B, la propiedad Visibility denominada ShowInvite. Este sería un caso de uso común si tiene algún texto que desea ocultar completamente en un control mediante enlace de datos.

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

Tenga en cuenta que no incluyo la firma o el constructor UserControl aquí porque no hay nada especial sobre ellos; no es necesario que subclases de INotifyPropertyChanged en absoluto.


Answer #3

Me encontré con un problema similar en el que tengo una propiedad de dependencia que quería que la clase escuchara para cambiar eventos y obtener datos relacionados de un servicio.

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