Silverlight 2 XAML Binding and Custom / Core Dependency Properties
Lately, I have been working in Silverlight 2 quite a bit. I
have been infused with inspiration from the Chicago
PhizzPop Design Challenge. I'm waiting for the pictures and vids
from a couple people to be served. I will be linking them into the blog.
For the time being, I have been hacking away at some of my own interests.
I ran into some interesting issues with the SL 2 binding. I
don't think these are new. There have been several posts about them in
the past, but I wanted to dig into them for myself, and see what reflector had
to say about it.
Update: I was completely wrong... "I will take SWORDS for 500 Alex"
Thankfully, a very friendly and knowledgeable Justin-Josef Angel helped me out with the issue and learned me 'bout some silverlight. Thank you very much Justin! Ok, so what I was trying to do was a little boneheaded (we all know pride tastes really bad)... Really, the way around the issue, as Justin pointed out, is to handle the PropertyChangedCallback on the dependency property rather than trying to place logic in the setter of the CLR Property which fronts the DependencyProperty. I am providing below the updated GlassButton class with the changes suggested.
We can all be pretty damn dumb from time to time. All we can hope is to try to learn from our mistakes, so we can be just that little bit better the next day. Anything you read below with a line struck through it, is null and void (aka dumbness). I hope this helps everyone out :).
public class GlassButton : Button
{
public static readonly DependencyProperty _radiusProperty = DependencyProperty.Register("Radius",
typeof (double),
typeof (GlassButton),
new PropertyMetadata(
new PropertyChangedCallback
(RadiusCallback)));
public double Radius
{
get { return (double) this.GetValue(_radiusProperty); }
set { this.SetValue(_radiusProperty, value); }
}
private static void RadiusCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var value = (double) e.NewValue;
if (value > 0)
{
((FrameworkElement) d).RenderTransform = new ScaleTransform {ScaleX = value/26, ScaleY = value/26};
}
}
}
The basic scenario is as follows: I have done quite a bit of
WPF programming in the past (since back when it was WinFx). I feel pretty damn good
about the bindings in WPF, and I feel like I have wrapped my head around
binding and dependency properties pretty well. So, I thought when I decided
to write Silverlight 2 apps rather than WPF apps, it would
be a very similar feeling. The feeling was abruptly interrupted by the
evilness of the ElementName Binding param. For those of you who have
spent time writing WPF apps, you will know the joy of binding element
properties to each other and having no problems. It's easy to take such
things for granted.
I understand you have to trim down the size of the download package for
SL, and I am perfectly groovy with that fact. The issue I have with it,
is that there is no recourse for a developer to step outside of those bounds.
Meaning, I would like to have my CustomDependencyProperties be able to
take part in XAML binding, the same way CoreDependencyProperties take part in
binding. Below, I'm going to outline some code to illustrate my issue.
Mind you this code is not at all cool. It just is there to elucidate
my point. If you want cool code, you can look to my boy Brownie, or
some other person in a better mood.
XAML:
<UserControl x:Class="Test.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Test="clr-namespace:Test"
Width="400" Height="300">
<Canvas>
<Canvas.Resources>
<Test:BindingHelper x:Key="bindingHelper"/>
</Canvas.Resources>
<Test:GlassButton x:Name="glass" Content="{Binding Value, Source={StaticResource bindingHelper}}"
Radius="{Binding Value, Source={StaticResource bindingHelper}}" Canvas.Top="10"/>
<Slider Value="{Binding Value, Source={StaticResource bindingHelper}, Mode=TwoWay}"
Width="100" Maximum="100" Minimum="0" Height="20"/>
</Canvas>
</UserControl>
BindingHelper:
#region
using System.ComponentModel;
#endregion
namespace Test
{
public class BindingHelper : INotifyPropertyChanged
{
private double value;
public double Value
{
get { return this.value; }
set
{
this.value = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("Value"));
}
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
}
GlassButton:
#region
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
#endregion
namespace Test
{
public class GlassButton : Button
{
public static readonly DependencyProperty _radiusProperty = DependencyProperty.Register("Radius", typeof (double),
typeof (GlassButton), null);
public double Radius
{
get { return (double)GetValue(_radiusProperty); }
set
{
if (value > 0)
{
this.RenderTransform = new ScaleTransform {ScaleX = value/26, ScaleY = value/26};
this.SetValue(_radiusProperty, value);
}
}
}
}
}
Check out the code if you will. It's in a nice little sln zipped up, with a bow on top. Cheers! CODE ZIPPED UP
Twitter: @DavidJustice
