WPF / MVVM Light中最efficient/fastest在屏幕上绘制矩形的方法是什么

What is the most efficient/fastest way to draw rectangles on the screen in WPF / MVVM Light

以下代码在二维网格中绘制矩形。一切工作正常,只是当它必须绘制超过 50,000 个正方形时它真的很慢。我知道这听起来像很多方块,但我用 C++/Qt 编写了相同的程序,而且速度快得多,它几乎可以立即绘制 50,000,而在 C#/WPF 中需要 50 秒。

有没有 better/faster 在 WPF 屏幕上绘制矩形的方法?

XAML

<Window x:Class="DrawingRectanglesWithMvvmLight.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:ignore="http://www.galasoft.ch/ignore"
        mc:Ignorable="d ignore"
        Height="319"
        Width="453.333"
        Title="MVVM Light Application"
        DataContext="{Binding Main, Source={StaticResource Locator}}">

    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Skins/MainSkin.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>

    <Grid x:Name="LayoutRoot" Margin="0,0,2,0" Height="194" VerticalAlignment="Top" Background="#FF3E7AAC">

        <ItemsControl ItemsSource="{Binding PartsGrid}" Height="200" Margin="5,0,10,-6">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas Background="#FFF1F0F0" Margin="10" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Rectangle 
                        Width="{Binding Width}" 
                       Height="{Binding Height}" 
                       Margin="{Binding Margin}"
                       Fill="{Binding Fill}" />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

        <Grid Margin="5,236,6,-69">
            <Button Content="Draw" Command="{Binding DrawCommand}" Margin="328,0,10,0" />
            <Button Content="Reset" Command="{Binding ResetCommand}" Margin="263,0,109,0" />
            <TextBox Text="{Binding Width}" RenderTransformOrigin="1.049,2.023" Margin="124,0,200,0"/>
            <TextBox Text="{Binding Height}" Margin="0,0,334,0"/>
        </Grid>
    </Grid>
</Window>

Class:

namespace MvvmLightTEST.Model
{
    public class FSRectangle
    {
        public double Width { get; set; }
        public double Height { get; set; }
        public Thickness Margin { get; set; }
        public Brush Fill { get; set; }
    }
}

ViewModel:

namespace DrawingRectanglesWithMvvmLight.ViewModel
{
    public class MainViewModel : ViewModelBase
    {
        public ObservableCollection<FSRectangle> PartsGrid { get; } = new ObservableCollection<FSRectangle>();

        public RelayCommand DrawCommand { get; }
        public RelayCommand ResetCommand { get; }

        public double Width { get; set; }
        public double Height { get; set; }

        public MainViewModel(IDataService dataService)
        {
            DrawCommand = new RelayCommand(Draw);
            ResetCommand = new RelayCommand(Clear);
        }

        private void Draw()
        {
            Clear();

            int xParts = 250;
            int yParts = 200;
            for (int i = 0; i < xParts; i++) {
                for (int j = 0; j < yParts; j++) {
                    FSRectangle part = new FSRectangle();
                    part.Width = Width;
                    part.Height = Height;
                    part.Margin = new Thickness((part.Width + 1) * i, (part.Height + 1) * j, 0, 0);
                    part.Fill = new SolidColorBrush(Color.FromArgb(170, 51, 51, 255));
                    PartsGrid.Add(part);
                }
            }
        }

        private void Clear()
        {
            PartsGrid.Clear();
        }
    }
}

UI

带有 RectangleGeometry 的路径是一个选项: https://docs.microsoft.com/en-us/dotnet/framework/wpf/graphics-multimedia/how-to-define-a-rectangle-using-a-rectanglegeometry

由于此用例的性能低下,未使用 MVVM。矩形是包含布局的 FrameworkElements,这是一种不缩放的功能 这在 SO 中讨论了很多次。

您可能需要考虑

  • 使用 DrawingVisualContainerVisual 进行较低级别的渲染,没有布局开销
  • 没有 MVVM
  • 提前创建渲染视觉效果而不是每次渲染

图片courtesy微软,未经许可使用。

MSDN 在 DrawingVisual

上有此说法

The DrawingVisual is a lightweight drawing class that is used to render shapes, images, or text. This class is considered lightweight because it does not provide layout or event handling, which improves its runtime performance. For this reason, drawings are ideal for backgrounds and clip art. The DrawingVisual can be used to create a custom visual object.

另见