【问题标题】:Refresh after sending a WM_SETREDRAW message to the panel向面板发送 WM_SETREDRAW 消息后刷新
【发布时间】:2012-07-16 12:14:27
【问题描述】:
ControlHelper.SuspendDrawing(panel);
panel.Controls.Clear();
AddItemIdLabel();
AddLastEditedLabel();
AddDeleteButton();
AddSaveButton();
ControlHelper.ResumeDrawing(panel);

public static class ControlHelper
{
    [DllImport("user32.dll")]
    private static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);

    private const int WM_SETREDRAW = 0xB;

    public static void SuspendDrawing(Control target)
    {
        SendMessage(target.Handle, WM_SETREDRAW, 0, 0);
    }

    public static void ResumeDrawing(Control target)
    {
        SendMessage(target.Handle, WM_SETREDRAW, 1, 0);
        target.Refresh();
    }
} 

如果我使用上面的代码进行测试,面板的某些部分不会被刷新。 您可以在没有添加新控件的地方看到 Clear() 之前的旧控件。

如果我将panel.Controls.Clear(); 放在ControlHelper.SuspendDrawing(panel); 之前,一切都会按预期工作但是有些闪烁是可见的,这是我试图避免的。

那么这里发生了什么?取决于我是否在暂停之前或之后清除控件集合会产生什么影响?

【问题讨论】:

    标签: c# refresh panel redraw


    【解决方案1】:

    您的target.Refresh() 调用并未使旧控件被删除的区域完全失效。由于Control.Refresh() 是一种“虚拟”方法,因此它可能会被覆盖并可能产生类似这样的副作用。

    为保证完全覆盖,您应该在ResumeDrawing() 中使用Invalidate(true)Update() 方法。 Invalidate(true) 方法会将控件的整个区域和所有子控件设置为无效,Update() 方法将在最后重绘所有内容。

    此外,您应该考虑将这些方法实现为 .NET 扩展。 这将自动将 SuspendDrawing()ResumeDrawing() 方法添加到每个 System.Windows.Forms.Control 中,其中您有一个 USING 用于声明扩展的命名空间:

    [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(this Control Target)
    {
        SendMessage(Target.Handle, WM_SETREDRAW, false, 0);
    }
    
    public static void ResumeDrawing(this Control Target)
    {
        SendMessage(Target.Handle, WM_SETREDRAW, true, 0);
        Target.Invalidate(true);
        Target.Update();
    }
    

    【讨论】:

    • Control.Refresh() 正是Invalidate(true) + Update() 的组合。如果它被子控件覆盖,这可能是有充分理由的。
    【解决方案2】:

    注意,提到的 PInvoke 不正确。应该是这样的:

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, IntPtr lParam);
    private const int WM_SETREDRAW = 11;
    
    public static void SuspendDrawing(this Control Target)
    {
        SendMessage(Target.Handle, WM_SETREDRAW, 0, IntPtr.Zero);
    }
    
    public static void ResumeDrawing(this Control Target)
    {
        SendMessage(Target.Handle, WM_SETREDRAW, 1, IntPtr.Zero);
        Target.Invalidate(true);
        Target.Update();
    }
    

    特别是返回类型是 IntPtr 而不是 int(64 位上 8 个字节,而不是 4 个字节)可能会随机使您的运行时崩溃。

    来源:http://www.pinvoke.net/default.aspx/user32.sendmessage

    【讨论】:

      猜你喜欢
      • 2012-06-25
      • 1970-01-01
      • 2018-07-25
      • 1970-01-01
      • 1970-01-01
      • 2013-07-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多