我在谷歌或Stack Overflow上找不到任何有用的东西,或者根本没有答案(或者我只是不知道要搜索什么) - 我能得到的最接近的问题是:{{3 }}
但是我想在这个简单的程序中找到这个滞后的底部,也许我只是没有做正确的事情。
我在UI元素的OnRender()中使用它们之间的线条渲染大约2000个点,实质上是创建折线图。没关系,但我想用MouseMove平移图表。这工作正常,但问题是LAG。无论何时用鼠标拖动我都希望平滑更新,我认为用它们之间的线重绘2000点就可以在公园里走i5 CPU了。但即使在家里的笔记本电脑上分辨率很低,它也非常慢。所以我检查了Performance Profiler。 OnRender()函数几乎不使用任何CPU。
The reason behind slow performance in WPF
事实证明,布局正在改变并使用如此多的CPU。
“布局”花费最多的时间来完成
现在,我听说过Visual Tree这个术语,但在这个简单的项目中几乎没有任何视觉效果。只是主窗口上的UI元素。它使用绘图上下文,我认为绘图上下文就像一个位图,或者是用自己的事件/命中框等绘制UI元素?因为我想要的只是UIElement像一个图像,但也处理鼠标事件,所以我可以拖动整个事物(或用鼠标滚轮缩放)。
所以问题:
以下是来源:
.cs文件
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows;
using System.Windows.Media;
namespace SlowChart
{
public class SlowChartClass : UIElement
{
List<Point> points = new List<Point>();
double XAxis_Width = 2000;
double XAxis_LeftMost = 0;
double YAxis_Height = 300;
double YAxis_Lowest = -150;
Point mousePoint;
double XAxis_LeftMostPan = 0;
double YAxis_LowestPan = 0;
public SlowChartClass()
{
for (int i = 0; i < 2000; i++)
{
double cos = (float)Math.Cos(((double)i / 100) * Math.PI * 2);
cos *= 100;
points.Add(new Point(i, cos));
}
MouseDown += SlowChartClass_MouseDown;
MouseUp += SlowChartClass_MouseUp;
MouseMove += SlowChartClass_MouseMove;
}
private void SlowChartClass_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
if (IsMouseCaptured)
{
XAxis_LeftMost = XAxis_LeftMostPan - (e.GetPosition(this).X - mousePoint.X);
YAxis_Lowest = YAxis_LowestPan + (e.GetPosition(this).Y - mousePoint.Y);
InvalidateVisual();
}
}
private void SlowChartClass_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
ReleaseMouseCapture();
}
private void SlowChartClass_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
mousePoint = e.GetPosition(this);
XAxis_LeftMostPan = XAxis_LeftMost;
YAxis_LowestPan = YAxis_Lowest;
CaptureMouse();
}
double translateYToScreen(double Y)
{
double y = RenderSize.Height - (RenderSize.Height * ((Y - YAxis_Lowest) / YAxis_Height));
return y;
}
double translateXToScreen(double X)
{
double x = (RenderSize.Width * ((X - XAxis_LeftMost) / XAxis_Width));
return x;
}
protected override void OnRender(DrawingContext drawingContext)
{
bool lastPointValid = false;
Point lastPoint = new Point();
Rect window = new Rect(RenderSize);
Pen pen = new Pen(Brushes.Black, 1);
// fill background
drawingContext.DrawRectangle(Brushes.White, null, window);
foreach (Point p in points)
{
Point screenPoint = new Point(translateXToScreen(p.X), translateYToScreen(p.Y));
if (lastPointValid)
{
// draw from last to this one
drawingContext.DrawLine(pen, lastPoint, screenPoint);
}
lastPoint = screenPoint;
lastPointValid = true;
}
// draw axis
drawingContext.DrawText(new FormattedText(XAxis_LeftMost.ToString("0.0") + "," + YAxis_Lowest.ToString("0.0"),CultureInfo.InvariantCulture,FlowDirection.LeftToRight,new Typeface("Arial"),12,Brushes.Black),new Point(0,RenderSize.Height-12));
}
}
}
.XAML文件
<Window x:Class="SlowChart.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:local="clr-namespace:SlowChart"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:SlowChartClass/>
</Grid>
</Window>
答案 0 :(得分:5)
不要为此致电InvalidateVisual()
。它触发了UI的完全重新布局,这非常慢。
WPF良好性能的关键在于理解它是保留的绘图系统。 OnRender()
应该真正命名为AccumulateDrawingObjects()
。它仅在布局过程结束时使用,并且它正在累积的对象实际上是活动对象,您可以在 完成后更新。
执行您要做的事情的有效方法是创建一个DrawingGroup&#34; backingStore&#34;为您的图表。 OnRender()需要做的唯一事情是将backingStore添加到DrawingContext。然后,您可以随时使用backingStore.Open()并仅绘制它来更新它。 WPF将自动更新您的UI。
您会发现StreamGeometry
是绘制到DrawingContext
的最快方式,因为它会优化非动画几何体。
您还可以在笔上使用.Freeze()
来获得额外的性能,因为它不是动画的。虽然我怀疑你只注意2000点时会注意到。
它看起来像这样:
DrawingGroup backingStore = new DrawingGroup();
protected override void OnRender(DrawingContext drawingContext) {
base.OnRender(drawingContext);
Render(); // put content into our backingStore
drawingContext.DrawDrawing(backingStore);
}
// Call render anytime, to update visual
// without triggering layout or OnRender()
public void Render() {
var drawingContext = backingStore.Open();
Render(drawingContext);
drawingContext.Close();
}
private void Render(DrawingContext drawingContext) {
// move the code from your OnRender() here
}
如果您想查看更多示例代码,请查看此处:
但是,如果视觉相对静止,并且您想要做的就是平移和缩放,还有其他选项。您可以创建一个Canvas
,并将Shapes实例化到其中,然后在鼠标移动过程中,您可以使用Canvas变换进行平移和缩放。