我在WPF的几幅画布上画了大量的文字和文字。我在WPF中使用了最轻量级的元素:DrawingVisual
我在不同的画布上绘制了线条,我将它们thickness
绑定到缩放系数的倒数,这样我就可以在缩放时获得均匀的线条粗细。这意味着当我缩放时,我只是用线条重绘画布。带有文本的画布仅在程序开始时创建。
现在我遇到了一个相当奇怪的问题。当我缩放时,我的表现很慢。当我使用虚线样式时性能变差。像在一个单词中加载一个大文本文件时一样慢,滚动它时遇到问题!
我的第一个想法是,线条的创造可能花费太多。因为我在每次变焦时创建它们(在鼠标滚轮上)所以我使用了一个适度的秒表来测量滚动鼠标滚轮直到线条创建结束之间的时间。令我惊讶的是它只需要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!
答案 0 :(得分:3)
好的,这是我的建议:在你的所有事件处理程序中,不要绘制或修改与渲染相关的任何内容。因为在屏幕刷新期间事件可能会多次触发(对于60Hz屏幕= 16ms),所以最终可能会多次重绘几次。
因此处理一些标志:在计算机图形学中,我们经常称它们为“脏”。标记,但可以根据需要调用它们。在这里,您可以使用两个可能属于私有属性的bool,让我们称之为linesNeedsRedraw
和renderTransformNeedsUpdate
(例如)。
现在,在mouseWheelHandler中,只需将DrawZeroWidthDrawing(Scale);
的调用替换为lineNeedsRedraw = true;
即可。在鼠标移动处理程序中,将RenderTransform更改替换为renderTransformNeedsUpdate=true;
。
第二件事:在CompositionTarget.Rendering
事件上挂钩一个方法。这个方法很简单:
void repaintIfRequired() {
if (linesNeedRedraw) {
DrawZeroWidthDrawing(Scale);
linesNeedRedraw = false;
}
if (renderTransformNeedsUpdate) {
// ... do your transform change
renderTransformNeedsUpdate = false;
}
}
这样,每帧最多只有一次更新。
我将以一个问题结束:为什么不使用renderTransform进行缩放?
祝你好运。