我有一个复杂的WPF控件,它在OnRender中绘制了很多基元(它有点像地图)。当它的一小部分发生变化时,我只想为受影响的元素重新发出渲染命令,而不是运行整个OnRender。虽然我对OnRender函数在调整大小或其他方面的性能很好,但对于基于鼠标悬停的基元突出显示来说还不够快。
目前,我知道如何强制进行屏幕更新的唯一方法是调用InvalidateVisual()。无法发送脏区域以使其无效。
WPF屏幕组成的最低粒度是UI元素吗?我是否需要将原始渲染渲染到中间目标中,然后使用InvalidateVisual()更新到屏幕?
答案 0 :(得分:3)
如果要编写WPF自定义/复合控件,应尽量避免覆盖OnRender,尤其是在计划使其部分无效时。使用AddVisualChild +覆盖VisualChildrenCount +覆盖GetVisualChild +覆盖Measure&更容易像这样安排(带2个孩子的伪代码):
private void BuildMyControls()
{
AddVisualChild(subControl1);
AddVisualChild(subControl2);
}
protected override int VisualChildrenCount
{
get
{
return 2;
}
}
protected override Visual GetVisualChild(int index)
{
if (index == 0) return subControl1;
if (index == 1) return subControl2;
return null; // should never be called in fact...
}
protected override Size MeasureCore(Size availableSize)
{
base.Measure...
BuildMyControls();
.. measure them, probably call subControlX.Measure(...);
}
protected override void ArrangeCore(Rect finalRect)
{
base.ArrangeCore(finalRect);
... arrange them, probably call subControlX.Arrange
}
使用这种代码,你可以使用subControlX.InvalidateXXX();
之类的东西使一部分无效。答案 1 :(得分:0)
WPF不能正常工作,因此您无法使区域无效。但是,可以进行一些优化。有一个Measure,Arrange,然后是Render pass。如果控件移动但实际呈现的内容不会改变,那么您可以告诉WPF仅执行安排传递。您可以使用FrameworkPropertyMetadata和FrameworkPropertyMetadataOptions(http://msdn.microsoft.com/en-us/library/system.windows.frameworkpropertymetadataoptions.aspx)从依赖项属性值更改中触发这些失效。
答案 2 :(得分:0)
除非您的控件尺寸发生变化,否则您不应该使用InvalidateVisual()
,因为它会导致相对昂贵的UI重新布局。
WPF是保留的绘图系统。这意味着OnRender()
最好称为AccumulateDrawingObjects()
。它实际上正在累积一个实时绘图对象树,每个布局只需要发生一次。然后,它会使用这些对象在需要时绘制UI。要在不重新布局的情况下更改UI的一部分外观,可以在OnRender()
之后随时更新某些对象(如DrawingGroup,RenderTargetBitmap和WriteableBitmap)。
要稍后更新部分UI,请将这些命令包装在DrawingGroup
中,并将该对象放入DrawingContext
。然后你可以Open()
随时更新它,WPF会自动重新绘制UI的那一部分。
这就是它的样子:
DrawingGroup backingStore = new DrawingGroup();
protected override void OnRender(DrawingContext drawingContext) {
base.OnRender(drawingContext);
Render(); // put content into our backingStore
drawingContext.DrawDrawing(backingStore);
}
// I can call this anytime, and it'll update my visual drawing
// without ever triggering layout or OnRender()
private void Render() {
var drawingContext = backingStore.Open();
Render(drawingContext);
drawingContext.Close();
}