Question

I am trying to reflect a position from my view-model expressing the location of an object in my view by binding the Canvas.Left and Canvas.Top properties in the style of the item view to appropriate properties in the view-model. However, the bindings do not seem to work.

For this minimal sample, I have simplified the structure so there only is one control Thing that is styled and templated:

using System;
using System.Windows;
using System.Windows.Controls;

namespace LocationBinding
{
    public class Thing : Control
    {
        static Thing()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(Thing), new FrameworkPropertyMetadata(typeof(Thing)));
        }

        public Point Location {
            get {
                return new Point(70, 70);
            }
        }

        public double VPos {
            get {
                return 100;
            }
        }
    }
}

For the sake of simplicity, I have declared the style in the resource dictionary of the main window - a simple window with a canvas on it (my real project has this in Themes\Generic.xaml). In its style, I am binding to property values of the control:

<Window x:Class="LocationBinding.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:LocationBinding"
    Title="LocationBinding" Height="300" Width="300">
    <Window.Resources>
        <Style TargetType="local:Thing">
            <Setter Property="Panel.ZIndex" Value="542"/>
            <Setter Property="Canvas.Left" Value="{Binding Location.X}"/>
            <Setter Property="Canvas.Top" Value="{Binding VPos}"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="local:Thing">
                        <Ellipse Fill="ForestGreen" Width="30" Height="30"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Canvas Name="cnv">

    </Canvas>
</Window>

The code-behind of the main window simply adds a Thing instance to the canvas:

using System;
using System.Windows;

namespace LocationBinding
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();

            cnv.Children.Add(new Thing());
        }
    }
}

The style is correctly applied, as evidenced by the ZIndex value (according to Snoop) and the correct template-based appearance of the control. However, Canvas.Left and Canvas.Top remain unset (and thus the Thing sticks in the top left corner of the canvas), even though according to threads such as this or this, Property="Canvas.Left" seems to be the correct syntax to refer to the attached property in a style.

I was first trying to bind Canvas.Top to Location.Y and replaced that with the VPos property in case the problem is related to binding to struct properties, but that does not seem to change anything, either.

What am I missing; how can I bind Canvas.Left and Canvas.Top in my style to the coordinates from my Thing.Location property?

Was it helpful?

Solution

By default, binding will search for property in DataContext of control but Location is your control (Thing) property.

So you need to use RelativeSource with Mode set to Self to tell binding engine to search for property in control itself and not in DataContext of control:

<Setter Property="Canvas.Left" Value="{Binding Location.X,
                                      RelativeSource={RelativeSource Self}}"/>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top