- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我没有在 Google 或 Stack Overflow 上找到任何有用的信息,或者根本没有答案(或者我可能只是不知道要搜索什么)——我能找到的最接近的问题是这个:The reason behind slow performance in WPF
但我想深入了解这个简单程序中的延迟,也许我只是做错了什么。
我在 UI 元素的 OnRender() 中渲染大约 2000 个点,它们之间有线,本质上是创建一个折线图。没关系,但我想用 MouseMove 平移图形。这工作正常,但问题是 LAG。每当用鼠标拖动时,我都希望能顺利更新,我认为重新绘制 2000 个点并在它们之间画线对于 i5 CPU 来说就像在公园里散步一样。但它非常慢,即使在我家里的笔记本电脑上使用低分辨率也是如此。所以我检查了性能分析器。 OnRender() 函数几乎不使用任何 CPU。
事实证明是布局发生了变化并占用了如此多的 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>
最佳答案
不要为此调用 InvalidateVisual()
。它会触发您的 UI 的完全重新布局,这非常慢。
在 WPF 中获得良好性能的关键是了解它是一个保留绘图系统。 OnRender()
实际上应该命名为 AccumulateDrawingObjects()
。它仅在布局过程结束时使用,它积累的对象实际上是事件对象,您可以在它完成后更新。
做你想做的事情的有效方法是为你的图表创建一个 DrawingGroup“backingStore”。您的 OnRender() 唯一需要做的就是将 backingStore 添加到 DrawingContext。然后,您可以随时使用 backingStore.Open() 更新它并直接绘制到其中。 WPF 将自动更新您的 UI。
您会发现 StreamGeometry
是绘制到 DrawingContext
的最快方法,因为它针对非动画几何进行了优化。
您还可以通过在 Pen 上使用 .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 转换以进行平移和缩放。
关于c# - WPF MouseMove InvalidateVisual OnRender 更新非常慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44336308/
我有以下代码来截取“目标”部门的屏幕截图。我在一个网站上找到了这个。但我不知道“onrendered:function() ”是如何工作的。我想问一下“onrendered”是如何使用的?如果我不添加
经过几个小时的实验和阅读,我仍然不明白为什么myTemplate.onRendered(function() {...})只调用一次。 根据您阅读的位置,您会获得不同的信息,但显然每次内部 HTML
我有一个自定义控件,用于执行动态数据呈现。此控件包含一组 DataPoint 对象,每个 DataPoint 都根据其位置 在控件中呈现(这些点在 OnRender 方法)。 自定义控件包含许多影响呈
截至最近, meteor 的所有文档都指出 onRendered是一种在模板完成渲染时获取回调的新方法。和 rendered只是为了向后兼容。 但是,这似乎对我不起作用。 onRendered永远不会
我有一个自定义面板,它实现了其子项的良好自定义布局。我想向面板添加一些额外的图形,因此覆盖 OnRender 以添加额外的矩形等。它工作得很好,除了 OnRender 似乎只添加到背景。有没有办法让
我想使用 OnRender 方法在文本框中绘制下划线,但该线绘制在文本框后面。文本框是不透明的,因此看不到下划线。如何在文本框上方绘制内容? protected override void OnRen
这是我的代码, var pdf = function() { console.log(12); var form = $('.container');
我有一个自定义控件 MyControl,它继承自 Canvas,并且在 OnRender-Method 中有自己的逻辑。它应该将一些矩形和文本绘制到绘图上下文中。 public class MyCon
我想在 twitter-bootstrap 的日期时间选择器中禁用过去的日期。例如,用户不能选择当前日期之前的日期。我编写代码 $(function() { var now = new Da
我有一个 iron-router 路由: Router.route('/profiel/bewerken', { subscriptions: function () {
我正在使用 ionic 框架包,并且我有一个侧边栏类。有一个名为 ionSideMenu.snapper 的类,查看源代码,该类是在呈现 ionSideMenuContainer 模板时定义的,因此我
在 Meteor 模板中使用 JQuery 插件时遇到问题。我试过this插件。 // A bunch of items var options = {
我将 PhaserJS 与 MeteorJS 结合起来,我担心 onRendered 回调中会包含大量代码。 为了简单和组织,我想将许多函数拆分到几个不同的文件中。 但是,我需要避免失去范围。如何将一
我想动态地绘制一些东西。以下代码显示了我的 OnRender。我在程序中需要它的地方设置 DrawItem。但是,当我调用 DrawItem =5; 时,我必须调用什么,以便调用 OnRender?
我在 .NET 3.5 中使用 Adorner,我可以通过覆盖 OnRender 进行绘制,但我需要能够重绘装饰器以更改其外观。 本质上,我正在寻找一种方法来清除绘图上下文并再次调用 OnRender
我有一个用于更新特定项目数据的 Iron-router 路由: Router.route('/project/:key/update', { ... }); 每次用户导航到“编辑项目页面”时,我都
父模板onRendered函数在子模板之前调用。如何在子模板渲染后执行父模板函数。 {{#each object}} {{> child}} {{/each}} 现在我需要执行一些
我正在尝试在我的meteor应用程序中使用chartist.js。我将其代码放在 Template.reportPage.onRendered() 中,以便在插入模板 DOM 后生成条形图。我尝试添加
我画了一些椭圆并将它们添加到网格中。 然后我想为每个 Ellipse 添加一些 FormattedText。我可以通过获取椭圆的 RectangleBounds 来做到这一点。 但是下面这个例子: h
我有一个自定义的 TextBox,它覆盖了 OnRender 方法。不幸的是,当我向网格添加超过 143-145 个文本框时,OnRender 无法正常工作。 这是带有 160 个文本框的窗口在 wp
我是一名优秀的程序员,十分优秀!