22

I want to add a set of rectangles to the main window of my mvvm application. In my viewModel I've got a collection of objects which I convert to System.Windows.Shapes.Rectangle classes with a converter (code below):

ViewModel:

RecognizedValueViewModel 
{
    public ObservableCollection<BarcodeElement> BarcodeElements
    {
        get { return _BarcodeElements; }
        set { _BarcodeElements = value; }
    }

    public RecognizedValueViewModel()
    {
        BarcodeElements = InitializeBarcodeElements();
    }
}

Converter:

public BarcodeElementToRectangleConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        Rectangle barcodeRectangle = GetRectangleFromBarcodeElement(value as BarcodeElement);

        return barcodeRectangle;
    }
}

The rectangles should be shown in a canvas in my MainWindow:

<Canvas x:Name="Canvas_Image_Main">
    <!-- Show rectangles here -->
</Canvas>

I would add Rectangles to canvas in code but I don't now how many rectangles are there at runtime. Is there a way how I can achieve this? Tank you.

Philipp Eger
  • 2,235
  • 4
  • 23
  • 34

2 Answers2

74

In a proper MVVM approach you would have a view model with an abstract representation of a list of rectangles, e.g. like this:

public class RectItem
{
    public double X { get; set; }
    public double Y { get; set; }
    public double Width { get; set; }
    public double Height { get; set; }
}

public class ViewModel
{
    public ObservableCollection<RectItem> RectItems { get; set; }
}

Then you would have a view that uses an ItemsControl to visualize a collection of such Rect items. The ItemsControl would have a Canvas as its ItemsPanel and an appropriate ItemContainerStyle and ItemTemplate which each bind to the appropriate view model properties. It might look like this:

<ItemsControl ItemsSource="{Binding RectItems}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemContainerStyle>
        <Style TargetType="ContentPresenter">
            <Setter Property="Canvas.Left" Value="{Binding X}"/>
            <Setter Property="Canvas.Top" Value="{Binding Y}"/>
        </Style>
    </ItemsControl.ItemContainerStyle>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Rectangle Width="{Binding Width}" Height="{Binding Height}" Fill="Black"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

An alternative without Bindings in Style Setters (which don't work in UWP) might look like this:

<ItemsControl ItemsSource="{Binding RectItems}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Rectangle Width="{Binding Width}" Height="{Binding Height}" Fill="Black">
                <Rectangle.RenderTransform>
                    <TranslateTransform X="{Binding X}" Y="{Binding Y}"/>
                </Rectangle.RenderTransform>
            </Rectangle>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>
Clemens
  • 123,504
  • 12
  • 155
  • 268
  • I can't seem to get this working in UWP, any ideas on how to do this? Since you cannot bnd to the setter properties anymore. – Nick N. Oct 04 '17 at 14:32
  • @NickN. See here: https://stackoverflow.com/q/40069749. Although marked as duplicate, I'd recommend the RenderTransform approach from the answer. – Clemens Oct 04 '17 at 14:38
7

You can bind the collection of rectangles to an ItemControl and set its height, width and margin:

<ItemsControl  ItemsSource="{Binding Path=RectangleCollection,Mode=TwoWay}">
    <ItemsControl.ItemTemplate>
        <DataTemplate >
            <Canvas>
                <Rectangle Stroke="Black" Heigth={some converter} Width={some converter} Margin={Some Converter}>
            </Canvas>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemControl>

Just an idea to get you started...

Jose
  • 1,857
  • 1
  • 16
  • 34
Abhinav Sharma
  • 443
  • 1
  • 4
  • 15