views:

1919

answers:

4

Hi Everyone,

I am currently trying to bind a collection of objects to a Canvas in Silverlight 3 using an ItemsControl as below:

<ItemsControl x:Name="ctrl" ItemsSource="{Binding myObjectsCollection}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas></Canvas>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Rectangle Stroke="LightGray" Fill="Black"  StrokeThickness="2" 
                   RadiusX="15" RadiusY="15" Canvas.Left="{Binding XAxis}"
                   Height="25" Width="25">
            </Rectangle>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Unfortunately it seems the binding on the Canvas.Left is being ignored. From what i have learned here it would appear this is due to the items being placed inside a content presenter not the actual canvas i have specified in the items panel.

Is there a way i can use data binding to determine the position of elements on a canvas?

+1  A: 

You are right, a ContentPresenter is inserted between the Canvas and the Rectangle. One workaround would be to set a left margin instead of a Canvas.Left:

<Rectangle Stroke="LightGray" Fill="Black" StrokeThickness="2" 
      RadiusX="15" RadiusY="15" Height="25" Width="25">
    <Rectangle.Margin>
        <Thickness Left="{Binding XAxis}"/>
    </Rectangle.Margin>
</Rectangle>
Mart
Unfortunately the property Left is readonly and cannot be set.
Blounty
I don't know the reason why it is read-only.Anyway I managed to make that work using a value converter:<Rectangle ... Margin="{Binding XAxis, Converter={StaticResource LeftMarginConverter}}"/>but that certainly isn't an elegant solution.
Mart
How does the LeftMarginConverter work and when this is applied is the margin applied to all of the bound items relative to the left edge of the container?
Blounty
The converter is:public class LeftMarginConverter : IValueConverter{ public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return new Thickness((int)value, 0, 0, 0); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); }}This way the left offset is set. If you bind with Margin={Binding} you can imagine any other positioning based on your object's properties.
Mart
+2  A: 

I realize that this already has an answer accepted, but the way to achieve the initial goal without messing with margins is to create a custom ItemsControl and override the PrepareContainerForItemOverride method. In this method, you set the binding in code.

public class CustomItemsCollection
    : ItemsControl
{
    protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
    {

        FrameworkElement contentitem = element as FrameworkElement;
        Binding leftBinding = new Binding("Left"); // "Left" is the property path that you want to bind the value to.
        contentitem.SetBinding(Canvas.LeftProperty, leftBinding);

        base.PrepareContainerForItemOverride(element, item);
    }

}
Tom
A: 

Add the following to your ItemsControl

 <ItemsControl.ItemContainerStyle>
    <Style TargetType="{x:Type ContentPresenter}">
      <Setter Property="Canvas.Left" Value="{Binding XPath=XAxis}"/>
    </Style>
  </ItemsControl.ItemContainerStyle>

No need for any custom controls

Nat
This cannot work, as the ItemsControl does not have a ItemContainerStyle property in Silverlight (v3 or v4). In WPF yes, but that's not the question.
Anthony
+2  A: 

You cannot use ItemsControl.ItemContainerStyle in Silverlight. It doesn't exist. It exists only on a couple of leaf level classes like ListBox itself.

DMC