The common question about positioning an element inside a Canvas
is "How to position the center of element (instead of the top left corner)".
Several solutions are presented, but they all have drawbacks.
The easiest solution is to accommodate the element size during while setting the Canvas.Left
and Canvas.Top
properties programmatically. This works, but only once. This solution doesn't support bindings and it will break when the element size is changed. You also cannot set the Canvas.Left
or Canvas.Top
using
Another set of solutions involve translate transformations utilizing either RenderTransform
or Margin
. These solutions require binding some property to the -0.5 * Width
or -0.5 * Height
. Such binding requires creating a custom ValueConverter
and is impossible to create using only XAML.
So, is there a simple way to position an element inside canvas so that its Canvas.Left
and Canvas.Top
correspond to the element's center and both size and position properties can be bound to some other properties?
XAML and bindings seem very powerful, but sometimes there are simple problems that require very complex solutions. In my bindings library creating such binding would be as easy as writing element.Center = position
or element.TopLeft = position - element.Size / 2
, but don't let me get carried away.
I've found a very simple solution which uses only XAML and supports binding both size and position properties of the element. It seems that when the WPF control with alignment set too Stretch
or Center
is placed inside the canvas, the element "gravitates" towards centering as the (Canvas.Left, Canvas.Top)
point (the state that we desire), but is stopped by the "angle plate" placed at the same (Canvas.Left, Canvas.Top)
point. How do I know about this "gravitation"? It's evident when you ease the block by setting the Margin
of the element to a negative value. Setting the negative margin allows the element to move towards its center goal. The element moves until the Margin
reaches (-Height / 2, -Width / 2)
so that the element becomes perfectly centered at the (Canvas.Left, Canvas.Top)
point. Further changes don't cause any movement since the element is already perfectly positioned.
Solution: set Margin="-1000000"
.
So in the following code the ellipses are both centered at the (200, 200) point. The first ellipse has Left
and Top
properties corresponding to the ellipse center allowing to easily bind them with some other objects' properties.
<Canvas>
<Ellipse Width="100" Height="100" Canvas.Left="200" Canvas.Top="200" Opacity="0.5" Fill="Red" Margin="-100000" />
<Ellipse Width="100" Height="100" Canvas.Left="150" Canvas.Top="150" Opacity="0.5" Fill="Blue" />
</Canvas>
The bad thing is this solution only work in WPF. Silverlight and WinRT don't have the described behavior.