我需要在彩色网格中表示大量的实时数据(大约100〜150左右,将来可能会增长)。在不使用第三方库或控件的情况下,在WPF中实现此目的的最佳实践是什么?数据每秒更新几次,因此显示需要合理响应。
我以前通过在面板中覆盖OnPaint来使用WinForm。但是({3}}这样做(我认为是)WPF等价物,Canvas中的OnRender。所以我想知道最佳实践是什么,对于我的特定用例。
答案 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.Left
和Canvas.Top
属性绑定到模型的X和Y属性。项目模板只是一个1x1边框,其背景绑定到模型的Value属性(您需要一个值转换器,根据您的要求将整数值转换为'Color'。)
使用1x1边框意味着除非SnapToDevicePixels
,否则您将遇到问题。你当然可以使边框大于1x1像素 - 你需要在'Canvas.Left'和'Canvas.Top'绑定上使用一个值转换器,它将绑定值乘以你需要的“square”的大小。
(这段代码主要是从内存中转储的,所以不能保证它可以开箱即用,但应该让你上路!)
我很想知道它如何处理性能。我们的应用程序是一个“软实时”系统,每100毫秒更新数百个控件,但距离你正在处理的数字还有很长的路要走!
答案 2 :(得分:0)
我想您可能想要使用Canvas输出必要的显示。鉴于您的刷新率非常高,可能还需要使用CompositionTarget.Rendering事件进行一些帧同步。