Storyboard doesn't animate custom FrameworkElement property - c#

I have class that is derived from FrameworkElement, and I want WPF to update its Location property by using DoubleAnimation. I register the property as DependendencyProperty:
public class TimeCursor : FrameworkElement
{
public static readonly DependencyProperty LocationProperty;
public double Location
{
get { return (double)GetValue(LocationProperty); }
set
{
SetValue(LocationProperty, value);
}
}
static TimeCursor()
{
LocationProperty = DependencyProperty.Register("Location", typeof(double), typeof(TimeCursor));
}
}
Following code sets up the storyboard.
TimeCursor timeCursor;
private void SetCursorAnimation()
{
timeCursor = new TimeCursor();
NameScope.SetNameScope(this, new NameScope());
RegisterName("TimeCursor", timeCursor);
storyboard.Children.Clear();
DoubleAnimation animation = new DoubleAnimation(LeftOffset, LeftOffset + (VerticalLineCount - 1) * HorizontalGap + VerticalLineThickness,
new Duration(TimeSpan.FromMilliseconds(musicDuration)), FillBehavior.HoldEnd);
Storyboard.SetTargetName(animation, "TimeCursor");
Storyboard.SetTargetProperty(animation, new PropertyPath(TimeCursor.LocationProperty));
storyboard.Children.Add(animation);
}
Then I call storyboard.Begin(this) from another method of the object which contains the above SetCursorAnimation() method and this object is derived from Canvas. However the Location property is never updated(set accessor of Location is never called) and no exception is thrown. What am I doing wrong?

When a dependency property is animated (or set in XAML, or set by a Style Setter, etc.), WPF does not call the CLR wrapper, but instead directly accesses the underlying DependencyObject and DependencyProperty objects. See the Implementing the "Wrapper" section in Checklist for Defining a Dependency Property and also Implications for Custom Dependency Properties.
In order to get notified about property changes, you have to register a PropertyChangedCallback:
public class TimeCursor : FrameworkElement
{
public static readonly DependencyProperty LocationProperty =
DependencyProperty.Register(
"Location", typeof(double), typeof(TimeCursor),
new FrameworkPropertyMetadata(LocationPropertyChanged)); // register callback here
public double Location
{
get { return (double)GetValue(LocationProperty); }
set { SetValue(LocationProperty, value); }
}
private static void LocationPropertyChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var timeCursor = obj as TimeCursor;
// handle Location property changes here
...
}
}
And note also that animating a dependency property does not necessarily require a Storyboard. You could simply call the BeginAnimation method on your TimeCursor instance:
var animation = new DoubleAnimation(LeftOffset,
LeftOffset + (VerticalLineCount - 1) * HorizontalGap + VerticalLineThickness,
new Duration(TimeSpan.FromMilliseconds(musicDuration)),
FillBehavior.HoldEnd);
timeCursor.BeginAnimation(TimeCursor.LocationProperty, animation);

Related

WPF – Change Property Value in Setter of Another Property

In the following example of my custom control, why I can't change a TransparentColor property in a setter of a SelectedColor property? A solution with callback method works fine, what's the difference between them in the case of change another property?
public class MyColorPicker : Control
{
static MyColorPicker()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyColorPicker), new FrameworkPropertyMetadata(typeof(MyColorPicker)));
}
public static readonly DependencyProperty SelectedColorProperty =
DependencyProperty.Register("SelectedColor", typeof(Color), typeof(MyColorPicker), new PropertyMetadata(Color.FromRgb(0, 201, 201), OnSelectedColorChange));
private static void OnSelectedColorChange(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var colorPicker = (MyColorPicker)sender;
var selectedColor = colorPicker.SelectedColor;
colorPicker.TransparentColor = Color.FromArgb(0, selectedColor.R, selectedColor.G, selectedColor.B);
}
public Color SelectedColor
{
get { return (Color)GetValue(SelectedColorProperty); }
set
{
// Why this not working? BorderStopColor = Color.FromArgb(0, value.R, value.G, value.B);
SetValue(SelectedColorProperty, value);
}
}
private static readonly DependencyPropertyKey TransparentColorPropertyKey =
DependencyProperty.RegisterReadOnly("TransparentColor", typeof(Color), typeof(MyColorPicker), new PropertyMetadata(Color.FromArgb(0, 0, 201, 201)));
public static readonly DependencyProperty TransparentColorProperty = TransparentColorPropertyKey.DependencyProperty;
public Color TransparentColor
{
get { return (Color)GetValue(TransparentColorProperty); }
protected set { SetValue(TransparentColorPropertyKey, value); }
}
}
The setter of a CLR wrapper for a dependency property should only call the SetValue method to set the actual value of the dependency property.
Any other logic should be implemented in the callback.
Also note that the CLR property wrappers are bypassed at runtime when setting dependency properties in XAML (but the callbacks are not):
Why are .NET property wrappers bypassed at runtime when setting dependency properties in XAML?

Dependencey Property with New Object in designer

I am struggling to find information about how to add some extra functionality in Visual Studio for dependency properties.
Basically, I want to create a similar function as the Effect property on most controls. If I press the Effects' new button, a list of possible effect objects are shown.
This is what I am used to for DP
public MyClass MyClass
{
get { return (MyClass)GetValue(MyClassProperty); }
set { SetValue(MyClassProperty, value); }
}
public static readonly DependencyProperty MyClassProperty =
DependencyProperty.Register("MyClass", typeof(MyClass),
typeof(MyView),
new FrameworkPropertyMetadata(new MyClass(), FrameworkPropertyMetadataOptions.AffectsRender, MyClassPropertyChangedCallback));
private static void MyClassPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as MyView;
control.MyClass = (MyClass)e.NewValue;
}

GetValue and SetValue is not called on binding

I wrote a custom control derived from Progressbar which implements animations on valuechange (the value fills up with a doubleanimation until the target is reached).
var duration = new Duration(TimeSpan.FromSeconds(2.0));
var doubleanimation = new DoubleAnimation(value, duration)
{
EasingFunction = new BounceEase()
};
BeginAnimation(ValueProperty, doubleanimation);
A new Property "TargetValue" is used, because the ControlTemplate used for the ProgressBar has to show the new value straight after changing it. For this the ProgressEx contains the following:
public static readonly DependencyProperty TargetValueProperty = DependencyProperty.Register("TargetValue", typeof (int), typeof (ProgressEx), new FrameworkPropertyMetadata(0));
public int TargetValue
{
get { return (int)GetValue(TargetValueProperty); }
set
{
if (value > Maximum)
{
//Tinting background-color
_oldBackground = Background;
Background = FindResource("DarkBackgroundHpOver100") as LinearGradientBrush;
}
else
{
if (_oldBackground != null)
Background = _oldBackground;
}
SetValue(TargetValueProperty, value);
Value = value;
}
}
When the TargetValue exceeds the maximum i will use a different color defined in xaml. This works really good - But. Now i want to use this bar in a listview where it is bind to some data. Problem is, the setter is not called in this case, so no animation is executed, even when the value is changed via TargetValue={Binding ProgressValue}. I know that the framework will always call GetValue and SetValue directly and no logic should be supplied, but is there a way around this?
Thanks in advance.
The CLR style getters and setters of DependencyPropertys are not meant to be called by the Framework... they are there for developers to use in code. If you want to know when a DependencyProperty value has changed, you need to add a handler:
public static readonly DependencyProperty TargetValueProperty =
DependencyProperty.Register("TargetValue", typeof (int), typeof (ProgressEx),
new FrameworkPropertyMetadata(0, OnTargetValueChanged));
private static void OnTargetValueChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e)
{
// Do something with the e.NewValue and/or e.OldValue values here
}

Update dependency property when child property has been updated

I have a class inheriting canvas with the following dependency property
public class StorageCanvas : Canvas
{
public readonly static DependencyProperty StorageProperty = DependencyProperty.Register(
"Storage",
typeof(Polygon),
typeof(StorageCanvas));
public Polygon Storage
{
get { return (Polygon) GetValue(StorageProperty); }
set { SetValue(StorageProperty, value); }
}
}
Can I somehow make the dependency property "update" when the Storage polygon Points has been altered/updated, instead of requiring the polygon to be replaced with a new instance?
Well Polygon.Points is a PointCollection, so you could just subscribe to the Changed event of it and then call InvalidateVisual() as suggested by #dowhilefor
public class StorageCanvas : Canvas {
public static readonly DependencyProperty StorageProperty = DependencyProperty.Register(
"Storage",
typeof(Polygon),
typeof(StorageCanvas),
new FrameworkPropertyMetadata(null, PropertyChangedCallback));
public Polygon Storage {
get {
return (Polygon)GetValue(StorageProperty);
}
set {
SetValue(StorageProperty, value);
}
}
private static void PropertyChangedCallback(
DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args) {
var currentStorageCanvas = dependencyObject as StorageCanvas;
if (currentStorageCanvas == null)
return;
var oldPolygon = args.OldValue as Polygon;
if (oldPolygon != null)
oldPolygon.Points.Changed -= currentStorageCanvas.PointsOnChanged;
var newPolygon = args.NewValue as Polygon;
if (newPolygon == null)
return;
newPolygon.Points.Changed += currentStorageCanvas.PointsOnChanged;
// Just adding the following to test if updates are fine.
currentStorageCanvas.Children.Clear();
currentStorageCanvas.Children.Add(newPolygon);
}
private void PointsOnChanged(object sender, EventArgs eventArgs) {
InvalidateVisual();
}
}
So now if any individual Point in Storage changed, without actually recreating the entire object, InvalidateVisual() will be fired.
The concept is just about subscribing to the Changed event of PointsCollection. Whether it's the right thing to do for you is a question you need to address yourself based on your requirements and logic.
Register the dependency property with Affects render meta data option.
http://msdn.microsoft.com/en-us/library/system.windows.frameworkpropertymetadata.affectsrender.aspx

Silverlight detect when Dependency property has a Xaml binding

Is it possible to detect when a Xaml binding is set on a DependencyProperty in Silverlight?
E.g. if I had a custom user-control with single dependency property and a binding declared like this:
public class MyControl : UserControl
{
public static readonly DependencyProperty TestProperty =
DependencyProperty.Register("Test",
typeof(object), typeof(MyControl),
new PropertyMetadata(null));
public object Test
{
get { return GetValue(TestProperty); }
set { SetValue(TestProperty, value); }
}
}
<MyControl Test="{Binding APropInViewModel}>
</MyControl>
Can I in the MyControl code to something like this?
// Ctor
public MyControl()
{
TestProperty.BindingChanged += new EventHandler(...)
}
e.g. can I get a notification of binding?
NOTE:
This is to solve a tricky order of precedence problem, described here so just checking for new values in the DependencyPropertyChanged handler won't work - because the property changed handler doesn't fire!!
It is possible for value changes in this binding. You can detect changes using propertychanged callback method which is static for Dependency properties.
public static readonly DependencyProperty TestProperty =
DependencyProperty.Register("Test",
typeof(object), typeof(MyControl),
new PropertyMetadata(null, TestChangedCallbackHandler));
private static void TestChangedCallbackHandler(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
MyControl obj = sender as MyControl;
Test = args.NewValue;
}
However, this might cause following event listening cases. If you want to listen changes on this dependency property is explained in this link :
Listen DepencenyProperty value changes
public void RegisterForNotification(string propertyName, FrameworkElement element, PropertyChangedCallback callback)
{
Binding b = new Binding(propertyName) { Source = element };
var prop = System.Windows.DependencyProperty.RegisterAttached(
"ListenAttached" + propertyName,
typeof(object),
typeof(UserControl),
new System.Windows.PropertyMetadata(callback));
element.SetBinding(prop, b);
}
and call like this
this.RegisterForNotification("Test", this, TestChangedCallback);

Categories