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