在不使用第三方库的情况下,在WPF中绘制大型彩色网格的最快方法?

时间:2018-01-05 12:22:35

标签: c# wpf graphics

我需要在彩色网格中表示大量的实时数据(大约100〜150左右,将来可能会增长)。在不使用第三方库或控件的情况下,在WPF中实现此目的的最佳实践是什么?数据每秒更新几次,因此显示需要合理响应。

我以前通过在面板中覆盖OnPaint来使用WinForm。但是({3}}这样做(我认为是)WPF等价物,Canvas中的OnRender。所以我想知道最佳实践是什么,对于我的特定用例。

3 个答案:

答案 0 :(得分:1)

为了在Canvas上绘图,我创建了从FrameworkElement派生的自定义控件。如果控件内部有较小的东西(如您的情况下的单元格),那么FrameworkElement中可能有多个Visual对象。

这是一个(未经测试的)类实例,它实现了这样的东西。它不会覆盖OnRender,而只是填充内部的Visual。由您决定如何定义以及何时更新内部Visual(意味着Populate函数的实现)。

public class CanvasDrawingObject : FrameworkElement
{
    VisualCollection m_children;
    DrawingVisual m_drawingVisual;

    public CanvasDrawingObject( )
        : base( )
    {
        m_children = new VisualCollection(this);
        m_drawingVisual = new DrawingVisual();
        m_children.Add(m_drawingVisual);
    }

    public void PopulateMyContent( )
    {
        // place it somewhere
        Canvas.SetLeft(this, 7);
        Canvas.SetTop(this, 42);

        // draw
        DrawingContext context = this.DrawingVisual.RenderOpen();
        if (context != null)
        {
            // do your drawing
            //context.DrawLine(... );
            context.Close();
        }
    }

    protected override int VisualChildrenCount
    {
        get { return m_children.Count; }
    }

    protected override Visual GetVisualChild(int index)
    {
        if (index < 0 || index >= m_children.Count)
            throw new ArgumentOutOfRangeException();

        return m_children[index];
    }
}

也就是说,我使用类似构造的应用程序没有真正的实时要求,但它用于绘制大量的矢量图形和文本。

答案 1 :(得分:1)

除了性能问题之外,我认为这是一个相当优雅的基于MVVM的绑定解决方案,尽管您可能会发现它的简单性有助于保持性能良好。

我假设你有一个代表每个数据点的类,例如:

public class DataPoint : INotifyPropertyChanged
{
    private int _value;

    public int X {get;set;}

    public int Y {get;set;}

    public int Value
    {
        get { return _value; }
        set
        {
            if (_value!= value)
            {
               _value= value;
               OnPropertyChanged("Value");
            }
        }
    }

    // INotifyPropertyChanged implementation ommitted for brevity
}

X和Y属性是网格中点的位置,因此将被指定为[0,0],[0,1] ...... [99,149]。

接下来,通过VM公开这些对象的集合:

public List<DataPoint> DataPoints {get; private set;}

(您可能需要使用`ObservableCollection,具体取决于您实例化和填充集合的方式/位置。)

在XAML中,将ItemsControl绑定到此集合,并使用Canvas作为其ItemsPanelTemplate:

<ItemsControl ItemsSource="{Binding DataPoints}"
              ItemContainerStyle="{StaticResource DataPointStyle}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas Width="100" Height="150" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

(我通过将画布大小设置为100x150来保持此示例简单,因此每个“数据点”将为1x1像素)。

这是项目容器样式:

<Style x:Key="DataPointStyle" TargetType="ContentPresenter">
    <Setter Property="Canvas.Left" Value="{Binding X}" />
    <Setter Property="Canvas.Top" Value="{Binding Y}" />
    <Setter Property="ContentTemplate">
        <Setter.Value>
            <DataTemplate DataType="{x:Type DataPoint}">
                <Border Width="1" Height="1" 
                        Background="{Binding Value, Converter={...}" />
            </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>

这会将列表项的Canvas.LeftCanvas.Top属性绑定到模型的X和Y属性。项目模板只是一个1x1边框,其背景绑定到模型的Value属性(您需要一个值转换器,根据您的要求将整数值转换为'Color'。)

使用1x1边框意味着除非SnapToDevicePixels,否则您将遇到问题。你当然可以使边框大于1x1像素 - 你需要在'Canvas.Left'和'Canvas.Top'绑定上使用一个值转换器,它将绑定值乘以你需要的“square”的大小。

(这段代码主要是从内存中转储的,所以不能保证它可以开箱即用,但应该让你上路!)

我很想知道它如何处理性能。我们的应用程序是一个“软实时”系统,每100毫秒更新数百个控件,但距离你正在处理的数字还有很长的路要走!

答案 2 :(得分:0)

我想您可能想要使用Canvas输出必要的显示。鉴于您的刷新率非常高,可能还需要使用CompositionTarget.Rendering事件进行一些帧同步。