views:

504

answers:

4

Hi all! 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! :-o 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

+1  A: 

I am not sure if I understand what you mean. But see if this helps.

    public double AnimateValue {
    get { return (double)GetValue(AnimateValueProperty); }
    set {
            SetValue(AnimateValueProperty, value);
            OnPropertyChanged(new PropertyChangedEventArgs("AnimateValue"));
            OnPropertyChanged(new PropertyChangedEventArgs("GuiValue "));
        }
    }
}
Kai Wang
Thanks for the pointer, that OnPropertyChanged should of course also be there. I have tried that before though, se edit for details above.
Victor
+2  A: 

By setting the value of AnimateValue, you are telling WPF that it has changed. But you are never telling WPF that GuiValue has changed. How is the WPF data binding framework supposed to know that GuiValue has changed when you don't tell it?

When you update AnimateValue and raise a notification of the change (which itself isn't necessary because it is a Dependency Property), you need to also raise a notification about GuiValue because it is dependent on AnimateValue but is calculated manually inside the property.

Basically just add

OnPropertyChanged(new PropertyChangedEventArgs("GuiValue"));

to the AnimateValue set block.

UPDATE:

Based on your new edits, if doing your property setting via XAML, the getter/setter of a Dependency Property is never called, it's all done through GetValue/SetValue directly.

If you need to response locally to changes, override OnPropertyChanged or provide a change callback inside DependencyProperty.Register

Adam Sills
Thanks for the pointer, should of course be there! I have tried that before though without any luck, se edit for details above.
Victor
+1  A: 

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
       userContol.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

Aran Mulholland
Thanks a lot for the reply and the msdn link! I added a line about the solution in the original question.
Victor
Btw - what does the "readonly" do? I tried both with and without and both work fine...
Victor
dont know what readonly does.. will look it up but. when i generate dependency properties i use the "propdp" snippet in visual studio and that is what i get, try this.. type propdp then hit tab, it gives you the outline of a dp you just have to fill in the blanks. snippets rock!
Aran Mulholland
from the help...The readonly keyword is a modifier that you can use on fields. When a field declaration includes a readonly modifier, assignments to the fields introduced by the declaration can only occur as part of the declaration or in a constructor in the same class.
Aran Mulholland
Thanks... Still strange that it still works to animate the field :)Oh well...
Victor
the dependency property is readonly, you cant change it after it is defined, however you can change the value that it stores. a dependency property wraps a value.
Aran Mulholland
+1  A: 

If your object is derived from Dependency Object then you can not use INotifyPropertyChanged because the Binding architecture will only use Dependency Property Resgistration in order to use binding.

For this you need Read Only Dependency Property which I have explained as below.

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

 // This is for the Readonly GuiValueKey property
 public static DependencyPropertyKey GuiValueKey = 
     DependencyProperty.RegisterReadOnly (
     "GuiValue",
      typeof(double),
      typeof(MyClass),
      new UIPropertyMetadata(0.0)
);

public double AnimateValue {
    get { return (double)GetValue(AnimateValueProperty); }
    set {
            SetValue(AnimateValueProperty, value);
            // Following statement update GuiValue wherever it
            // is binded
            this.SetValue(GuiValueKey,value + 10);
        }
    }
}

// you dont even need following line...
// but its good to have it..
public double GuiValue {
    get { 
        return (double)this.GetValue(GuiValueKey); 
    }
}
Akash Kava