简体   繁体   中英

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

The following code draws rectangles in 2D grid. Everything is working fine except it is really slow when it has to draw more than 50,000 squares. I know it sounds like a lot of squares but I have the same program written in C++/Qt and it's a lot faster, it draws the 50,000 almost instantaneously wheres in C#/WPF it takes 50 seconds.

Is there a better/faster way to draw rectangles on the screen in 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

在此处输入图片说明

By not using MVVM due to the slow performance for this use case. Rectangles are FrameworkElement s which contain layout, a feature that does not scale This is discussed many times in SO.

You might want to consider

  • using DrawingVisual and ContainerVisual for lower level rendering with no layout overhead
  • no MVVM
  • create render visuals ahead of time rather than per render

在此处输入图片说明

Image courtesy Microsoft, used without permission.

MSDN has this to say on 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.

See also

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM