Is the a MVVM WPF way to implement DrawingVisual based on an ObservableCollection

464 views Asked by At

My Application displays a lot of lines and polygons/paths on a canvas. My ViewModel holds a series of ObservableCollections that represent different items to be drawn.

The issue I have is the application is very slow to zoom and pan. Zoom and pan is all taken care of using an IvalueConverter and converts from world coordinate system to the canvas coordinate system.

For this to work, I have to NotifyPropertyChange all objects visible on the screen to force them to be redrawn with the latest pan and zoom values. It works very well for a few hundred lines, but for thousands it’s very slow. And if you zoom out so all objects are visible therefore subject to NotifyPropertyChange it’s almost unusable with over 10,000 lines.

I’m not using the polygons built-in features in any way as all handling, selection moving etc is taken care of in the viewmodel. I therefore want to try and use DrawingVisual instead of Shapes as I understand they have much lower overheads but I can’t find any good MVVM examples of how to use them. Examples I have seen show them being built in codebehind which isn’t how I think I should be using them.

Examples as follows:

//new DrawingVisual
DrawingVisual drawingVisual = new DrawingVisual();
DrawingContext drawingContext = drawingVisual.RenderOpen();
Rect rect = new Rect(new System.Windows.Point(160, 100), new System.Windows.Size(320, 80));
drawingContext.DrawRectangle(System.Windows.Media.Brushes.LightBlue, (System.Windows.Media.Pen)null, rect);
drawingContext.Close();

//creating a Host as follows:
public MyVisualHost()
    {
    _children = new VisualCollection(this);
    _children.Add(CreateDrawingVisualRectangle());
    _children.Add(CreateDrawingVisualText());
    _children.Add(CreateDrawingVisualEllipses());

   this.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(MyVisualHost_MouseLeftButtonUp);
    }

Unless there is a WPF way to bind to my observable collection using DrawingVisual I would need to create and delete drawings objects in my models. Every-time a model is updated it would then have to update my drawingVisual. But then I’m creating View items in model which can’t be the correct way. Can anybody advise how I should go about implementing DrawingVisual instead of Shapes in my MVVM application?

Here is an extract of the code I am currently using that uses Shapes

XAML

    <ItemsControl x:Name="Catchments">
        <ItemsControl.Resources>
            <CollectionViewSource x:Key="CatchmentPolygons" Source="{Binding Path=NetworkMain.Catchments}"></CollectionViewSource>
            <DataTemplate DataType="{x:Type cad:Catchment}">
                <Polygon 
                    Stroke="{Binding IsSelected, Mode=OneWay, Converter={StaticResource ObjectColour}, ConverterParameter=Catchment}"
                    StrokeThickness="1" 
                    Visibility="{Binding Visible, Mode=OneWay, TargetNullValue='Hidden'}"
                    Points="{Binding Points, Mode=OneWay, Converter={StaticResource CollectionPointConverter}}">
                    <Polygon.Fill>
                        <SolidColorBrush 
                            Color="{Binding IsSelected, Mode=OneWay, Converter={StaticResource ObjectColour}, ConverterParameter=Catchment}" 
                            Opacity=".25"
                            >
                            
                        </SolidColorBrush>
                    </Polygon.Fill>
                </Polygon>
            </DataTemplate>
        </ItemsControl.Resources>
        <ItemsControl.ItemsSource>
            <CompositeCollection>
                <CollectionContainer Collection="{Binding Source={StaticResource CatchmentPolygons}}"></CollectionContainer>
            </CompositeCollection>
        </ItemsControl.ItemsSource>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas 
                    ClipToBounds="true">
                </Canvas>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>

My Observable Collections in my ViewModel:

    public ObservableCollection<Conduit> Conduits { get; set; } = new();
    public ObservableCollection<Node> Nodes { get; set; } = new();
    public ObservableCollection<Catchment> Catchments { get; set; } = new();

EDIT

Screenshots of the Canvas with a zoomed in and zoomed out view:

Zoomed out:

enter image description here

The images have scaled a little bit but in reality, line thicknesses, node sizes and arrows remain constant as you zoom in and out. Only the projected coordinates of nodes change.

Zoomed in:

enter image description here

1

There are 1 answers

0
mm8 On BEST ANSWER

You could add a dependency property to your visual host and bind this one to the source property of the view model.

Then the visual host can create a DrawingVisual per item in the source collection