C# 10000';s+;UI元素,绑定还是绘制?
我正在为时间轴控件绘制标题。 看起来是这样的: 每行0.01毫秒,因此在10分钟的时间线内,我将绘制60000行+6000个标签。 这需要一段时间,大约10秒。 我想从UI线程中卸载这个。 我的代码当前为:C# 10000';s+;UI元素,绑定还是绘制?,c#,wpf,data-binding,C#,Wpf,Data Binding,我正在为时间轴控件绘制标题。 看起来是这样的: 每行0.01毫秒,因此在10分钟的时间线内,我将绘制60000行+6000个标签。 这需要一段时间,大约10秒。 我想从UI线程中卸载这个。 我的代码当前为: private void drawHeader() { Header.Children.Clear(); switch (viewLevel) { case ViewLevel.MilliSeconds100: double hWidth = Heade
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;
}
}
private void drawHeader()
{
Header.Children.Clear();
开关(视图级别)
{
案例ViewLevel.毫秒100:
双宽度=页眉宽度;
这是一条绘制头线(新的时间跨度(0,0,0,0,10),100,5,hWidth);
//正在查看后台工作人员以卸载UI
//backgroundWorker=新的backgroundWorker();
//backgroundWorker.DoWork+=委托(对象发送方,DoWorkEventArgs参数)
// {
//这是一条绘制头线(新的时间跨度(0,0,0,0,10),100,5,hWidth);
// };
//backgroundWorker.RunWorkerAsync();
打破
}
}
专用空心绘制头线(TimeSpan timeStep、int Majore Veryxline、int Distance Between Lines、double headerWidth)
{
var currentTime=新的时间跨度(0,0,0,0,0);
const int everyXLine100=10;
双电流x=0;
var-currentLine=0;
while(当前x
我已经研究了BackgroundWorker,除了不能在非UI线程上创建UI元素
是否可以在非UI线程中执行drawHeaderLines
我可以用数据绑定来画线吗?
这会有助于用户界面响应吗
我可以想象我可以使用数据绑定,但是样式可能超出了我当前的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);
}
});
}
private void按钮1\u单击(对象发送者,事件参数e)
{
TaskScheduler uiContext=TaskScheduler.FromCurrentSynchronizationContext();
Task.Run(异步()=>
{
对于(int i=0;i<1000;i++)
{
等待任务。工厂。开始新建(()=>
{
Add(新标签(){Text=i.ToString()});
},CancellationToken.None,TaskCreationOptions.None,uiContext);
}
});
}
首先,我们在UI线程中获取UI上下文,然后在后台启动一个新线程,负责启动所有小任务。这里是循环的开始。(您可能希望将其提取到另一个方法中,因为您的方法并不那么简单。)然后,立即在循环内部启动一个新任务,并在UI上下文中启动该任务。在该任务中,您可以将整个循环中的内容放在其中。通过等待
完成该任务,您可以确保每个任务都是上一个任务的延续,因此它们都按顺序运行。如果订单不重要(这不太可能,但可能),那么您根本不需要等待
如果你需要一个C#4.0版本,你可以让一个
任务
退出循环,在每次迭代中连接一个新任务作为前一个任务的延续,然后将“自身”设置为该任务。不过会更混乱。这只是一个概念证明,但我认为你可以
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListView Grid.Row="0" Grid.Column="0" ItemsSource="{Binding Path=Numbers}" Width="100" Height="500">
<ListView.RenderTransform>
<RotateTransform Angle="90" CenterX="150" CenterY="150"/>
</ListView.RenderTransform>
</ListView>
</Grid>
public List<string> Numbers
{
get
{
List<string> numbers = new List<string>();
for (UInt16 i=1; i < 60000; i++)
{
numbers.Add(i.ToString());
}
return numbers;
}
}
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); }
}
<Canvas x:Name="Header" Background="#FF2D2D30" Grid.Row="0">
<ContentPresenter Content="{Binding HeaderDrawingVisual}" />
</Canvas>
Header.Width = 50000;
VisualHost = new MyVisualHost(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