2

I have a list of objects with X,Y properties. I want to put the collection into ItemsControl and position each item according to its x and y.

I don't know the maximum of X or Y.

I tried to use Canvas as ItemsControl.ItemsPanel, but Canvas doesn't change its size based on its children. Thus ScrollViewer cannot scroll ItemsControl.

I checked How to Visible ScrollBar In Items Panel With Canvas as ItemsPaneltemplate. The first solution is to use Grid and set Margin. However, Thickness.X and Thickness.Y are not dependency properties and not bindable. The second solution is OK but involves code-behind. The last solution should also work but writing a new class is laborious.

How to position items in ItemsControl and enable scroll? What solution do you prefer?

Community
  • 1
  • 1
Gqqnbig
  • 5,845
  • 10
  • 45
  • 86
  • bind the Canvas `Width` and Height` properties to properties in your ViewModel, and have the ViewModel calculate the required values. That's what I did [here](http://stackoverflow.com/a/15821573/643085) and it works like a charm. – Federico Berasategui Sep 08 '13 at 05:30
  • @Shoe that doesn't resolve the size of the canvas issue, what are you talking about? – Federico Berasategui Sep 08 '13 at 05:40
  • 1
    There is a radar screen in the book "WPF Control Development Unleashed: Building Advanced User Experiences" which is built on the basis of a ListBox and a self-developed ItemsPanel. This panel poitions the items according to their coordinate and determines its preferred size from those coordinates. I am sure you find the source code for download somewhere. – oddparity Sep 08 '13 at 06:16

1 Answers1

1

Though Thickness.X and Thickness.Y are not dependency properties, Margin is. You just need to define a ValueConverter that accepts your object containing X and Y values, or a MultiValueConvert that accepts the X and Y values. Both implementations would return a Thickness.

<YourElement.Margin>
    <MultiBinding Converter="{StaticResource myMarginConverter}">
      <Binding Path="X"/>
      <Binding Path="Y"/>
    </MultiBinding>
</YourElement.Margin>

.

public class NameConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        //Validate parameters...

        Thickness margin = new Margin(values[0], values[1], 0d, 0d);

        return margin;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        //Validate parameters...

        double x = ((Thickness)value).Left;
        double y = ((Thickness)value).Top;

        return new object[]{x, y};
    }
}

I haven't tested this code but you get the picture. As far as which approach is better, using a Canvas sounds like more of a control designed for your intended purpose. However, it depends on the details of what you're trying to do. I believe canvas has potential for greater efficiency with virtualization if you're expecting a lot of points. If they don't need to be interactive, you could also just draw them to a bitmap image.

xr280xr
  • 12,621
  • 7
  • 81
  • 125