views:

308

answers:

2

I have a class like this:

public class Stretcher : Panel {

    public static readonly DependencyProperty StretchAmountProp = DependencyProperty.RegisterAttached("StretchAmount", typeof(double), typeof(Stretcher), null);

    public static void SetStretchAmount(DependencyObject obj, double amount)
    {
        FrameworkElement elem = obj as FrameworkElement;
        elem.Width *= amount;

        obj.SetValue(StretchAmountProp, amount);
    }
}

I can set the stretch amount property in XAML using the attribute syntax:

<UserControl x:Class="ManagedAttachedProps.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:map="clr-namespace:ManagedAttachedProps"
    Width="400" Height="300">

    <Rectangle Fill="Aqua" Width="100" Height="100" map:Stretch.StretchAmount="100" />

</UserControl>

and my rectangle is stretched, but I can't use property element syntax like this:

<UserControl x:Class="ManagedAttachedProps.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:map="clr-namespace:ManagedAttachedProps"
    Width="400" Height="300">

    <Rectangle Fill="Aqua" Width="100" Height="100">
        <map:Stretcher.StretchAmount>100</map:Stretcher.StretchAmount>
    </Rectangle>
</UserControl>

with the property element syntax my set block seems to be totally ignored (I can even put invalid double values in there), and the SetStretchAmount method is never called.

I know it's possible to do something like this, because VisualStateManager does it. I've tried using types other than double and nothing seems to work.

+1  A: 

I think I figured this out, although I'm not totally sure I understand the reason why it works.

In order to get your example to work I had to create a custom type called Stretch with a property called StretchAmount. Once I did that and put that inside the property element tags it worked. Otherwise it wasn't called.

public class Stretch
{
    public double StretchAmount { get; set; }
}

And the property changed to..

public static readonly DependencyProperty StretchAmountProp = DependencyProperty.RegisterAttached("StretchAmount", typeof(Stretch), typeof(Stretcher), null);

public static void SetStretchAmount(DependencyObject obj, Stretch amount) 
{ 
    FrameworkElement elem = obj as FrameworkElement; 
    elem.Width *= amount.StretchAmount; 
    obj.SetValue(StretchAmountProp, amount); 
}

To get this to work in the scenario where you aren't using a property element you would need to create a custom type converter to allow this to work.

Hope this helps, even though it doesn't explain the why which I'm still trying to understand.

BTW - for a real brain teaser, take a look at the VisualStateManager in reflector. The dependency property and the setter for VisualStateGroups are both internal.

Bryant
Very interesting, and somewhat strange. I'll give this a try tomorrow when I have access to a windows machine and make sure to give you the answer if it works. Thanks!
Jacksonh
A: 

So Bryant's solution works, it does require a slight modification to the XAML:

<Rectangle Fill="Aqua" Width="100" Height="100" x:Name="the_rect">
        <map:Stretcher.StretchAmount>
            <map:Stretch StretchAmount="100" />
        </map:Stretcher.StretchAmount>
</Rectangle>
Jacksonh