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?
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}}"/>