Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Attached.Property="{Binding}" does not work

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;
        }
    }
}
like image 701
Jerry Nixon Avatar asked Jul 15 '26 13:07

Jerry Nixon


1 Answers

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)
like image 139
Nigel Sampson Avatar answered Jul 20 '26 16:07

Nigel Sampson



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!