Skip Navigation LinksHome > View Post

Using Binding to position a Collection of elements

UPDATE - here's a much better way to solve this: Using Binding to position a Collection of elements - Take 2 I was recently looking at a WPF query with my new colleague Simon Ince (check out his first MSDN blog post on CTE in SQL 2005).

The question was along the lines of "How can I position a collection of elements on a canvas using databinding? The number of elements is unknown at design time".

Wherever we have an unknown number of items in a collection your attention is normally going to turn to using an ItemsControl. In this case we'd probably want to swap the ItemsPanel for a Canvas and then we can databind away, right?

Here's an example in pure Xaml. Note that we create an array of System.Drawing.Points in Xaml to bind to.

<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:draw="clr-namespace:System.Drawing;assembly=System.Drawing">

    <Page.Resources>
        <x:Array x:Key="myArray" Type="{x:Type draw:Point}">
            <draw:Point X="10" Y="20"/>
            <draw:Point X="20" Y="10"/>
            <draw:Point X="30" Y="50"/>
        </x:Array>
    </Page.Resources>

    <ItemsControl ItemsSource="{Binding Source={StaticResource myArray}, Path=.}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Rectangle Canvas.Top="{Binding Y}" Canvas.Left="{Binding X}" Fill="Red" Width="10" Height="10"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas IsItemsHost="True" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>

</Page>

However, as you can see from the render in XamlPad, this isn't working. The visual tree shows why...

xamlpad render

The ItemsControl wraps each item in a ContentPresenter and, as you know, Canvas's attached Top and Left properties only work on direct children of the canvas. Time for another approach.

Let's use a transform, a translate transform to be specific.

<Rectangle Fill="Red" Width="10" Height="10">
    <Rectangle.RenderTransform>
        <TranslateTransform X="{Binding X}" Y="{Binding Y}" />
    </Rectangle.RenderTransform>
</Rectangle>

working xamlpad render

That's better.

Another option would be to modify the Margin of the rectangle something like this:

<Rectangle Fill="Red" Width="10" Height="10">
    <Rectangle.Margin>
        <Thickness Left="{Binding X}" Top="{Binding Y}" />
    </Rectangle.Margin>
</Rectangle>

Of course this won't work because the Thickness's Left and Top properties aren't DependencyProperties and therefore can't be the target of a binding. That doesn't rule this out as an option, you'd just have to write an IMultiValueConverter and a MultiBinding as discussed at the end of Reason 2. Databinding.

Tags: WPF

 
Josh Post By Josh Twist
5:39 AM
31 Oct 2007

» Next Post: Reason 6. Layout
« Previous Post: Binding to Attached Properties

Comments are closed for this post.

Posted by Peter @ 08 Sep 2008 2:54 PM
Do you know of any code that illustrates a bar graph that uses adorners to adjust the bars by dragging? Additionally, I would like the polys bound so as they adjust, their positions can be used via convertors to determine their X/Y values?

Thanks!

© 2005 - 2014 Josh Twist - All Rights Reserved.