Search code examples
c#wpfuser-interfacecanvasmvvm

Add n rectangles to canvas with MVVM in WPF


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.


Solution

  • 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>