3
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);一切都按预期工作,我试图避免一些闪烁是可见的。

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

4

2 回答 2

1

注意,提到的 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

于 2021-02-02T14:31:23.587 回答
1

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

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

此外,您应该考虑将这些方法实现为 .NET 扩展。这将自动将SuspendDrawing()andResumeDrawing()方法添加到每个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();
}
于 2015-12-19T14:10:13.803 回答