3
votes

I have created a simple attached property that enables dragging an item around the screen.

1/ Here's how you would implement it on your element:

<Rectangle Fill="Green" local:MyExtension.CanMove="True" />

2/ This works like a charm. So does this:

// in resources
<x:Boolean x:Key="MyCanMove">true</x:Boolean>
<Rectangle Fill="Blue" local:MyExtension.CanMove="{StaticResource MyCanMove}" />

3/ But one syntax does not work. This fails:

<Rectangle Fill="Red" local:MyExtension.CanMove="{Binding Path=CanMove}" />

What's different? The only thing different is that it is binding the value into the attached property instead of setting it explicitly or through a static resource.

I'm missing something. But what is it?

Here's the full XAML:

<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">

    <Grid.DataContext>
        <local:ViewModel/>
    </Grid.DataContext>

    <ToggleSwitch Header="Enable Dragging" 
                  HorizontalAlignment="Center" 
                  IsOn="{Binding CanMove, Mode=TwoWay}">
        <ToggleSwitch.RenderTransform>
            <TranslateTransform Y="-100" />
        </ToggleSwitch.RenderTransform>
    </ToggleSwitch>

    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
        <StackPanel.Resources>
            <Style TargetType="Rectangle">
                <Setter Property="Height" Value="100" />
                <Setter Property="Width" Value="100" />
            </Style>
            <x:Boolean x:Key="MyCanMove">true</x:Boolean>
        </StackPanel.Resources>
        <Rectangle Fill="Green" local:MyExtension.CanMove="True" />
        <Rectangle Fill="Blue" local:MyExtension.CanMove="{StaticResource MyCanMove}" />
        <Rectangle Fill="Red" local:MyExtension.CanMove="{Binding Path=CanMove}" />
    </StackPanel>

</Grid>

And here's the full code-behind:

public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    bool m_CanMove = true;
    public bool CanMove
    {
        get { return m_CanMove; }
        set
        {
            m_CanMove = value;
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("CanMove"));
        }
    }
}

public class MyExtension
{
    // canmove aproperty
    public static bool GetCanMove(DependencyObject obj)
    {
        return (bool)obj.GetValue(CanMoveProperty);
    }
    public static void SetCanMove(DependencyObject obj, bool value)
    {
        System.Diagnostics.Debug.WriteLine("SetCanMove");
        obj.SetValue(CanMoveProperty, value);

        var rectangle = obj as FrameworkElement;
        rectangle.ManipulationMode = ManipulationModes.TranslateX | ManipulationModes.TranslateY;
        rectangle.ManipulationDelta -= rectangle_ManipulationDelta;
        if (value)
            rectangle.ManipulationDelta += rectangle_ManipulationDelta;
    }
    public static readonly DependencyProperty CanMoveProperty =
        DependencyProperty.RegisterAttached("CanMove", typeof(bool), typeof(MyExtension), new PropertyMetadata(false));

    // implementation
    static void rectangle_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
    {
        var rectangle = sender as FrameworkElement;
        var canMove = System.Convert.ToBoolean(rectangle.GetValue(MyExtension.CanMoveProperty));
        if (canMove)
        {
            var transform = rectangle.RenderTransform as CompositeTransform;
            if (transform == null)
                rectangle.RenderTransform = (transform = new CompositeTransform());
            transform.TranslateX += e.Delta.Translation.X;
            transform.TranslateY += e.Delta.Translation.Y;
        }
    }
}

I'll remind you that this attached property works fine in the first two syntaxes. As a result, I can't imagine the error is in the attached property. And, I read on a few forums where path= is necessary to bind to an attached property, so I included that (though it didn't make a difference). Changing the Mode (OneWay, TwoWay) doesn't make a difference. Binding with an ElementName didn't make a difference. I am wondering if this simply isn't enabled in Windows 8.0 WinRT. Can anyone else get this to work?

EDIT: Solution

The problem was that without a changed event handler setup, the binding doesn't raise a changed event. Here's the updated MyExtension code:

public class MyExtension
{
    // canmove aproperty
    public static bool GetCanMove(DependencyObject obj) { return (bool)obj.GetValue(CanMoveProperty); }
    public static void SetCanMove(DependencyObject obj, bool value) { obj.SetValue(CanMoveProperty, value); }
    public static readonly DependencyProperty CanMoveProperty =
        DependencyProperty.RegisterAttached("CanMove", typeof(bool), typeof(MyExtension), new PropertyMetadata(false, OnCanMoveChanged));

    // respond to change
    private static void OnCanMoveChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var rectangle = d as FrameworkElement;
        rectangle.ManipulationMode = ManipulationModes.TranslateX | ManipulationModes.TranslateY;
        rectangle.ManipulationDelta -= rectangle_ManipulationDelta;
        if ((bool)e.NewValue)
            rectangle.ManipulationDelta += rectangle_ManipulationDelta;
    }

    // implementation
    static void rectangle_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
    {
        var rectangle = sender as FrameworkElement;
        var canMove = System.Convert.ToBoolean(rectangle.GetValue(MyExtension.CanMoveProperty));
        if (canMove)
        {
            var transform = rectangle.RenderTransform as CompositeTransform;
            if (transform == null)
                rectangle.RenderTransform = (transform = new CompositeTransform());
            transform.TranslateX += e.Delta.Translation.X;
            transform.TranslateY += e.Delta.Translation.Y;
        }
    }
}
1

1 Answers

4
votes

This is just speculation as I don't have a compiler in front of me, but I'm wondering if the binding infrastructure doesn't use the exposed methods you created GetCanMove etc.

Try registering a property changed method in the PropertyMetadata

new PropertyMetadata(false, OnCanMoveChanged)

and have the setup and teardown code in there

private void OnCanMoveChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)