C# – WPF PropertyChange does not cascade – animating one custom class dependencyproperty changes other properties – PropertyChange only fires for first

animationbindingcinotifypropertychangedwpf

Very simplified, this is my problem: (solution in the bottom)

I have a custom class, inheriting Animatable and INotifyPropertyChange, containing one DependencyProperty (which I animate, say from 1 to 10) and one extra property (which I want to use from the XAML with a binding):

MyClass : Animatable, INotifyPropertyChanged {

    public static DependencyProperty AnimateValueProperty = DependencyProperty.Register(
      "AnimateValue",
      typeof(double),
      typeof(MyClass)
     );

    public double AnimateValue {
        get { return (double)GetValue(AnimateValueProperty); }
        set {
                SetValue(AnimateValueProperty, value);
                OnPropertyChanged(new PropertyChangedEventArgs("AnimateValue"));
                OnPropertyChanged(new PropertyChangedEventArgs("GuiValue")); //Added in edit
            }
        }
    }

    public double GuiValue {
        get { 
            return AnimateValue + 10; 
            //More fancy stuff happening in the real class...
        }
    }


    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) {
        if (PropertyChanged != null) {
            PropertyChanged(this, e);
        }
    }

    protected override Freezable CreateInstanceCore() {
        return new MyClass();
    }
}

When I bind the GUI element to the property I actually animate (AnimateValue) everything works as expected – when I bind the GUI element to the related property (GuiValue) nothing happens. I guess that the changed event doesn't reach that property, but I really don't know how to solve the problem. Any pointers?

Any help is greatly appreciated!

Edit: Thanks for the two replies from Adam Sills and Kai Wang, stupid of me to leave out the second OnPropertyChange call, however I have tried that before and it didnt work. To debug, I put a writeline in the AnimateValue.set function and behold – it does not get executed at all!

I also tried changing the getters to always return 20 – no change! 😮 It seems like the animation completely skips the property get and set and animates and reads the underlying dependencyproperty… Or have I misunderstood? Animation code I use is:

DoubleAnimation da = new DoubleAnimation (1, 10, new Duration(TimeSpan.FromSeconds(3)));
myClassInstance.BeginAnimation(MyClass.AnimateValueProperty, da);

Hmm… After writing that code here it seems very reasonable that the setter is not used (although I would really like it to be!) – but still, the basic problem stands and I still dont get why the getter is not used!

Edit2: Solution!

As suggested below I couldnt use the INotifyPropertyChange for the properties connecting to the DependencyProperty – the animation and XAML use the DepencencyProperty internally and does not use the getters and setters at all.

Hence – PropertyChangedCallback to notify others on change, CoerceValueCallback to "fix" data. It is also possible to validate data, all of this is done in the DependencyProperty constructor. Thanks all!

Thanks!

/Victor

Best Answer

here is a property changed notifaction pattern for use with dependency properties

  public Boolean PasswordBoxVisible {
     get { return (Boolean)GetValue(PasswordBoxVisibleProperty); }
     set { SetValue(PasswordBoxVisibleProperty, value); }
  }

  public static readonly DependencyProperty PasswordBoxVisibleProperty =
      DependencyProperty.Register("PasswordBoxVisible", typeof(Boolean), typeof(UserControl), new UIPropertyMetadata(false, new PropertyChangedCallback(PropChanged)));

  //this method is called when the dp is changed
  static void PropChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) {
       UserControl userControl = o as UserControl;
       //now you can use this to call some methods, raise other value changed etc, in this case OtherProperty.
       userControl.NotifyPropertyChanged("OtherProperty");
  }

I have a feeling, but am not entirely sure, that what you might need to do is coerce the other values. have a look at the coerce value callback section in this article

Related Topic