WPF渲染同步

本文关键字:同步 WPF | 更新日期: 2023-09-27 18:08:51

我有两个自定义WPF控件(带有自定义渲染逻辑)放在彼此的顶部(透明背景和东西)。在某些时候,我想重新绘制这些控件。我的第一个想法是调用:

foreach(var control in Controls)
{
    control.InvalidateVisual();
}

但这并不完全有效。我的意思是,它确实会强制渲染我的控件,但视觉更新不会同时发生。控件一个接一个地更新,这看起来不太好,可能会导致混淆(在一个控件显示更新的视觉效果,而另一个控件仍然显示旧的视觉效果的情况下)。关于InvalidateVisual方法的文档真的很差,但我猜这是一个异步方法,这就是为什么我有这个问题。

所以问题是:是否有一种方法来同步呈现多个控件?除了创建一个包含所有渲染逻辑的大型控件(我想避免这种情况)。

WPF渲染同步

一般不应该使用InvalidateVisual()

要更新保持相同大小的控件,您可以创建DrawingGroup并在OnRender()期间将其放入可视化树中-然后您可以随时使用DrawingGroup.Open()更新绘图组,WPF将更新UI。

对于您的情况,它看起来像:

class Control1 : UIElement {
    DrawingGroup backingStore = new DrawingGroup();
    protected override void OnRender(DrawingContext drawingContext) {      
        base.OnRender(drawingContext);            
        Render(); // put content into our backingStore
        drawingContext.DrawDrawing(backingStore);
    }
    private void Render() {            
        var drawingContext = backingStore.Open();
        Render(drawingContext);
        drawingContext.Close();            
    }
    private void Render(DrawingContext) {
        // put your existing drawing commands here.
    }
}
class Control2 : UIElement {
    DrawingGroup backingStore = new DrawingGroup();
    protected override void OnRender(DrawingContext drawingContext) {      
        base.OnRender(drawingContext);            
        Render(); // put content into our backingStore
        drawingContext.DrawDrawing(backingStore);
    }
    private void Render() {            
        var drawingContext = backingStore.Open();
        Render(drawingContext);
        drawingContext.Close();            
    }
    private void Render(DrawingContext) {
        // put your existing drawing commands here.
    }
}

class SomeOtherClass {
    public void SomeOtherMethod() {
         control1.Render();
         control2.Render();
    }
}    

您需要暂停刷新控制,直到您完成所有它们的无效,一种方法如下:

class DrawingControl
{
    [DllImport("user32.dll")]
    public static extern int SendMessage(IntPtr hWnd, Int32 wMsg, bool wParam, Int32 lParam);
    private const int WM_SETREDRAW = 11; 
    public static void SuspendDrawing( Control parent )
    {
        SendMessage(parent.Handle, WM_SETREDRAW, false, 0);
    }
    public static void ResumeDrawing( Control parent )
    {
        SendMessage(parent.Handle, WM_SETREDRAW, true, 0);
        parent.Refresh();
    }
}
如何暂停控件及其子控件的绘制?

另一种方法是使用数据绑定来触发渲染,如果控件有一些绑定到视图模型的Data属性。

public static readonly DependencyProperty DataProperty 
    = DependencyProperty.Register("Data", typeof(DataType), typeof(MyView), 
       new FrameworkPropertyMetadata(default(DataType), 
           FrameworkPropertyMetadataOptions.AffectsRender));
public DataType Data
{
    get { return (DataType)GetValue(DataProperty); }
    set { SetValue(DataProperty, value); }
}

注意AffectsRender标志。然后,您可以通过同时更新绑定属性来同时重新绘制所有控件:

foreach(var viewModel in ViewModels)
{
    viewModel.Data = ...;
}