慢速平移并放大WPF

我在WPF的几幅canvas上画了大量的文字和文字。 我在WPF中使用了最轻量级的元素: DrawingVisual

我在不同的canvas上绘制了线条,我将它们的thickness限制在缩放系数的倒数上,这样我就可以在缩放时获得均匀的线条粗细。 这意味着当我缩放时,我只是用线条重绘canvas。 带有文本的canvas仅在程序开始时创建。

现在我遇到了一个相当奇怪的问题。 当我缩放时,我的表现很慢。 当我使用虚线样式时性能变差。 像在一个单词中加载一个大文本文件时一样慢,滚动它时遇到问题!

我的第一个想法是,线条的创建可能花费太多。 由于我在每次变焦时创建它们(在鼠标滚轮上),所以我使用了一个适度的秒表来测量滚动鼠标滚轮直到线条创建结束之间的时间。 令我惊讶的是它只需要1毫秒! 因此,线条的创建不是问题。

经过进一步的检查,我发现在平移时我的表现相当缓慢! 当你试图在窗户中拍摄非常大的图像时,就像慢一样!

那可能是什么问题呢? 我知道你会想要一些代码,但由于代码很长,我只会展示Mousewheel事件中发生的事情:

 private void OnMouseWheel(object sender, MouseWheelEventArgs e) { var sw = new Stopwatch(); sw.Start(); var st = GetScaleTransform(Window); var tt = GetTranslateTransform(Window); var absoluteX = MousePos.Current.X * st.ScaleX + tt.X; var absoluteY = MousePos.Current.Y * st.ScaleY + tt.Y; const double zoomfactorforwheel = 1.3; if (e.Delta > 0) { st.ScaleX = Math.Min(st.ScaleX * zoomfactorforwheel, Scalemax); st.ScaleY = Math.Min(st.ScaleY * zoomfactorforwheel, Scalemax); } else { st.ScaleX = Math.Max(st.ScaleX / zoomfactorforwheel, Scalemin); st.ScaleY = Math.Max(st.ScaleY / zoomfactorforwheel, Scalemin); } tt.X = absoluteX - MousePos.Current.X * st.ScaleX; tt.Y = absoluteY - MousePos.Current.Y * st.ScaleY; Scale = st.ScaleX; // Inside this function I'm drawing the lines on drawingvisual // Then I'm adding the drawingvisual to a canvas DrawZeroWidthDrawing(Scale); sw.Stop(); Console.WriteLine(sw.ElapsedMilliseconds); } 

我开始认为也许它必须对WPF中渲染事物的方式做些什么,或者可能在WPF中处理鼠标按钮的方式? 似乎某些事情干扰了鼠标事件频率 。 任何建议都非常感谢。

更新:我试图实现GameAlchemist在我的代码中所说的让我惊讶它仍然很慢。

 private MouseWheelEventArgs e; private bool zoomNeeded; CompositionTarget.Rendering += CompositionTargetOnRendering; private void CompositionTargetOnRendering(object sender, EventArgs eventArgs) { if (zoomNeeded) { zoomNeeded = false; DoZoom(); DrawZeroWidthDrawing(); } } private void OnMouseWheel(object sender, MouseWheelEventArgs e) { this.e = e; zoomNeeded = true; } private void DoZoom() { var st = GetScaleTransform(Window); var tt = GetTranslateTransform(Window); var absoluteX = MousePos.Current.X * st.ScaleX + tt.X; var absoluteY = MousePos.Current.Y * st.ScaleY + tt.Y; const double zoomfactorforwheel = 1.3; if (e.Delta > 0) { st.ScaleX = Math.Min(st.ScaleX * zoomfactorforwheel, Scalemax); st.ScaleY = Math.Min(st.ScaleY * zoomfactorforwheel, Scalemax); } else { st.ScaleX = Math.Max(st.ScaleX / zoomfactorforwheel, Scalemin); st.ScaleY = Math.Max(st.ScaleY / zoomfactorforwheel, Scalemin); } tt.X = absoluteX - MousePos.Current.X * st.ScaleX; tt.Y = absoluteY - MousePos.Current.Y * st.ScaleY; Scale = st.ScaleX; } private static TranslateTransform GetTranslateTransform(UIElement element) { return (TranslateTransform)((TransformGroup)element.RenderTransform) .Children.First(tr => tr is TranslateTransform); } private static ScaleTransform GetScaleTransform(UIElement element) { return (ScaleTransform)((TransformGroup)element.RenderTransform) .Children.First(tr => tr is ScaleTransform); } 

我想出可能重新绘制线条是罪魁祸首但似乎没有! 我删除了对实际使用新厚度重绘它们的函数的调用,只留下了缩放部分。 结果没有改变! 我仍然缺乏表现。

 private void CompositionTargetOnRendering(object sender, EventArgs eventArgs) { if (zoomNeeded) { zoomNeeded = false; DoZoom(); } } 

在此处输入图像描述

我尝试使用WPF性能工具测量FPS,它在缩放期间回落到16~24!

好的,这是我的建议:在你的所有事件处理程序中,不要绘制或修改与渲染相关的任何内容。 因为在屏幕刷新期间事件可能会多次触发(对于60Hz屏幕= 16ms),所以最终可能会多次重绘几次。
因此处理一些标志:在计算机图形学中,我们经常将它们称为“脏”标志,但可以根据需要调用它们。 在这里你可以使用两个可能是私有属性的linesNeedsRedraw ,让我们称之为linesNeedsRedrawrenderTransformNeedsUpdate (例如)。
现在在mouseWheelHandler中,只需将您的调用替换为DrawZeroWidthDrawing(Scale); by lineNeedsRedraw = true; 。 在鼠标移动处理程序中,用renderTransformNeedsUpdate=true;替换RenderTransform更改renderTransformNeedsUpdate=true;

第二件事:在CompositionTarget.Rendering事件上挂钩一个方法。 这个方法很简单:

 void repaintIfRequired() { if (linesNeedRedraw) { DrawZeroWidthDrawing(Scale); linesNeedRedraw = false; } if (renderTransformNeedsUpdate) { // ... do your transform change renderTransformNeedsUpdate = false; } } 

这样,每帧最多只有一次更新。

我将以一个问题结束:为什么不使用renderTransform进行缩放?

祝好运。