10000的+ UI元素,绑定还是绘制?

我正在绘制时间轴控件的标题。 它看起来像这样: 在此处输入图像描述

我每行走0.01毫秒,所以对于10分钟的时间线我正在寻找60000行+ 6000个标签。 这需要一段时间,约10秒。 我想从UI线程中卸载它。 我的代码目前是:

private void drawHeader() { Header.Children.Clear(); switch (viewLevel) { case ViewLevel.MilliSeconds100: double hWidth = Header.Width; this.drawHeaderLines(new TimeSpan(0, 0, 0, 0, 10), 100, 5, hWidth); //Was looking into background worker to off load UI //backgroundWorker = new BackgroundWorker(); //backgroundWorker.DoWork += delegate(object sender, DoWorkEventArgs args) // { // this.drawHeaderLines(new TimeSpan(0, 0, 0, 0, 10), 100, 5, hWidth); // }; //backgroundWorker.RunWorkerAsync(); break; } } private void drawHeaderLines(TimeSpan timeStep, int majorEveryXLine, int distanceBetweenLines, double headerWidth) { var currentTime = new TimeSpan(0, 0, 0, 0, 0); const int everyXLine100 = 10; double currentX = 0; var currentLine = 0; while (currentX < headerWidth) { var l = new Line { ToolTip = currentTime.ToString(@"hh\:mm\:ss\.fff"), StrokeThickness = 1, X1 = 0, X2 = 0, Y1 = 30, Y2 = 25 }; if (((currentLine % majorEveryXLine) == 0) && currentLine != 0) { l.StrokeThickness = 2; l.Y2 = 15; var textBlock = new TextBlock { Text = l.ToolTip.ToString(), FontSize = 8, FontFamily = new FontFamily("Tahoma"), Foreground = new SolidColorBrush(Color.FromRgb(255, 255, 255)) }; Canvas.SetLeft(textBlock, (currentX - 22)); Canvas.SetTop(textBlock, 0); Header.Children.Add(textBlock); } if ((((currentLine % everyXLine100) == 0) && currentLine != 0) && (currentLine % majorEveryXLine) != 0) { l.Y2 = 20; var textBlock = new TextBlock { Text = string.Format(".{0}", TimeSpan.Parse(l.ToolTip.ToString()).Milliseconds), FontSize = 8, FontFamily = new FontFamily("Tahoma"), Foreground = new SolidColorBrush(Color.FromRgb(192, 192, 192)) }; Canvas.SetLeft(textBlock, (currentX - 8)); Canvas.SetTop(textBlock, 8); Header.Children.Add(textBlock); } l.Stroke = new SolidColorBrush(Color.FromRgb(255, 255, 255)); Header.Children.Add(l); Canvas.SetLeft(l, currentX); currentX += distanceBetweenLines; currentLine++; currentTime += timeStep; } } 

我查看了BackgroundWorker,除了你不能在非UI线程上创建UI元素。

是否有可能在非UI线程中执行drawHeaderLines?

我可以使用数据绑定来绘制线条吗? 这有助于UI响应吗?

我想我可以使用数据绑定,但是Styling可能超出了我目前的WPF能力(来自winforms并试图了解所有这些样式对象是什么并绑定它们)。

是否有人能够提供诱惑这一点的起点? 或谷歌一个教程,让我开始?

所以,正如你所说的,所有这些工作都需要在UI线程中完成; 你不能只在后台线程中做到这一点。

但是,运行一个非常长的循环在UI线程中进行大量的UI修改会阻塞UI线程,所以很明显我们不能这样做。

他们关键在于你需要将你正在做的事情分解成许多较小的工作单元,然后在UI线程中完成所有这些小工作单元。 UI线程正在做与以前一样多的工作(可能甚至更多,由于管理所有这些的开销)但它允许其他事情(例如鼠标移动/点击事件,按键等)发生介于这些任务之间。

这是一个简单的例子:

 private void button1_Click(object sender, EventArgs e) { TaskScheduler uiContext = TaskScheduler.FromCurrentSynchronizationContext(); Task.Run(async () => { for (int i = 0; i < 1000; i++) { await Task.Factory.StartNew(() => { Controls.Add(new Label() { Text = i.ToString() }); }, CancellationToken.None, TaskCreationOptions.None, uiContext); } }); } 

首先,我们在UI线程中获取UI上下文,然后我们在后台启动一个新线程,负责启动所有的小任务。 在这里,您将拥有循环的开始。 (您可能希望将其解除为另一种方法,因为您的方法并不那么简单。)然后,在循环内部立即启动一个新任务,并在UI的上下文中启动该任务。 在该任务的内部,您可以放置​​循环中的整个主体。 通过await该任务,您可以确保每个任务都被安排为前一个任务的延续,因此它们都按顺序运行。 如果订单无关紧要(这不太可能,但可能),那么您根本不需要等待。

如果你需要一个C#4.0版本,你可以将一个Task放在循环之外,并在每次迭代中连接一个新任务作为前一个的继续,然后将“自己”设置为该任务。 但它会更加混乱。

这只是一个概念certificate,但我认为您可以使用ListView虚拟化。 而不是文本只有一个图像集合(每10行一个图像)。 通过虚拟化,它只呈现显示内容。

              public List Numbers { get { List numbers = new List(); for (UInt16 i=1; i < 60000; i++) { numbers.Add(i.ToString()); } return numbers; } } 

好吧,我把它放在后面燃烧了一点但是。

但我想我已经提出了一个解决方案。

使用DrawingVisual和DrawingContext。

这篇博文对我有所帮助: 简单的WPF 2D图形:DrawingVisual

首先我们需要获取我们的Visual类(注意这个代码已被打包在一起,清理是一个好主意):

 public class MyVisualHost : FrameworkElement { private readonly VisualCollection children; public MyVisualHost(int width) { children = new VisualCollection(this); var visual = new DrawingVisual(); children.Add(visual); var currentTime = new TimeSpan(0, 0, 0, 0, 0); const int everyXLine100 = 10; double currentX = 0; var currentLine = 0; double distanceBetweenLines = 5; TimeSpan timeStep = new TimeSpan(0, 0, 0, 0, 10); int majorEveryXLine = 100; var grayBrush = new SolidColorBrush(Color.FromRgb(192, 192, 192)); grayBrush.Freeze(); var grayPen = new Pen(grayBrush, 2); var whitePen = new Pen(Brushes.White, 2); grayPen.Freeze(); whitePen.Freeze(); using (var dc = visual.RenderOpen()) { while (currentX < width) { if (((currentLine % majorEveryXLine) == 0) && currentLine != 0) { dc.DrawLine(whitePen, new Point(currentX, 30), new Point(currentX, 15)); var text = new FormattedText( currentTime.ToString(@"hh\:mm\:ss\.fff"), CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("Tahoma"), 8, grayBrush); dc.DrawText(text, new Point((currentX - 22), 0)); } else if ((((currentLine % everyXLine100) == 0) && currentLine != 0) && (currentLine % majorEveryXLine) != 0) { dc.DrawLine(grayPen, new Point(currentX, 30), new Point(currentX, 20)); var text = new FormattedText( string.Format(".{0}", currentTime.Milliseconds), CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("Tahoma"), 8, grayBrush); dc.DrawText(text, new Point((currentX - 8), 8)); } else { dc.DrawLine(grayPen, new Point(currentX, 30), new Point(currentX, 25)); } currentX += distanceBetweenLines; currentLine++; currentTime += timeStep; } } } // Provide a required override for the VisualChildrenCount property. protected override int VisualChildrenCount { get { return children.Count; } } // Provide a required override for the GetVisualChild method. protected override Visual GetVisualChild(int index) { if (index < 0 || index >= children.Count) { throw new ArgumentOutOfRangeException(); } return children[index]; } } 

接下来我们需要绑定:

 public static readonly DependencyProperty HeaderDrawingVisualProperty = DependencyProperty.Register("HeaderDrawingVisual", typeof(MyVisualHost), typeof(MainWindow)); public MyVisualHost VisualHost { get { return (MyVisualHost)GetValue(HeaderDrawingVisualProperty); } set { SetValue(HeaderDrawingVisualProperty, value); } } 

XAML:

    

然后只需在代码中设置:

 Header.Width = 50000; VisualHost = new MyVisualHost(50000); 

我的测试表明,使用这种新方法的宽度为50000,我看到了很大的增长!

 Old way Total Milliseconds: 277.061 New way Total Milliseconds: 13.9982 Old way Total Milliseconds: 518.4632 New way Total Milliseconds: 12.9423 Old way Total Milliseconds: 479.1846 New way Total Milliseconds: 23.4987 Old way Total Milliseconds: 477.1366 New way Total Milliseconds: 12.6469 Old way Total Milliseconds: 481.3118 New way Total Milliseconds: 12.9678 

时间少于第一次,因为重建时必须清除项目。