C# 窗口窗体:拆分器闪烁



我有以下问题:我在表单中放置了一个拆分器控件(不是拆分容器)并添加了 2 个面板。分离器工作正常,但是当我移动分配器时,它开始闪烁 - 面板没有。

我用拆分容器得到相同的结果。

我试过这个,但没有任何效果

this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.DoubleBuffered = true;
...

class XSplitter : Splitter
{
    public XSplitter() : base()
    {
        this.SetStyle(ControlStyles.UserPaint, true);
        this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
        this.SetStyle(ControlStyles.DoubleBuffer, true);
        this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
        this.DoubleBuffered = true;
    }
}
class XPanel : Panel
{
    public XPanel() : base()
    {
        this.SetStyle(ControlStyles.UserPaint, true);
        this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
        this.SetStyle(ControlStyles.DoubleBuffer, true);
        this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
        this.DoubleBuffered = true;
    }
}

我使用 Windows 8.1 和 VS 2010

谢谢你的帮助!

以下是使用此控件的步骤:

  • 将新类"NonFlickerSplitContainer"添加到 C# 应用程序中。
  • 将自动生成的类代码替换为如下所示的 C# 代码。
  • 在应用程序中使用 NonFlickerSplitContainer 对象而不是 SplitContainer 对象。

    public partial class NonFlickerSplitContainer : SplitContainer
    {
       public NonFlickerSplitContainer()
       {
        this.SetStyle(ControlStyles.AllPaintingInWmPaint |
                      ControlStyles.UserPaint |
                      ControlStyles.OptimizedDoubleBuffer, true);
        MethodInfo objMethodInfo = typeof(Control).GetMethod("SetStyle",BindingFlags.NonPublic|BindingFlags.Instance);
        object[] objArgs = new object[] { ControlStyles.AllPaintingInWmPaint |
                                          ControlStyles.UserPaint |
                                          ControlStyles.OptimizedDoubleBuffer, true };
        objMethodInfo.Invoke(this.Panel1, objArgs);
        objMethodInfo.Invoke(this.Panel2, objArgs);
       }
    }
    

查看拆分器源代码 - http://referencesource.microsoft.com

  1. 拆分器使用来自WM_PAINT外部的 GDI 调用,利用父级(非自己的)设备上下文:

    private void DrawSplitHelper(int splitSize) {
      ...
      IntPtr parentHandle = ParentInternal.Handle;
      IntPtr dc = UnsafeNativeMethods.GetDCEx(new HandleRef(ParentInternal, parentHandle), NativeMethods.NullHandleRef, NativeMethods.DCX_CACHE | NativeMethods.DCX_LOCKWINDOWUPDATE);
      IntPtr halftone = ControlPaint.CreateHalftoneHBRUSH();
      ...
    

    所以样式设置没有任何影响。

  2. 当拆分器移动时,它会从先前的位置删除图像,然后再在新位置绘制:

    private void DrawSplitBar(int mode) {
      if (mode != DRAW_START && lastDrawSplit != -1) {
        DrawSplitHelper(lastDrawSplit);
        lastDrawSplit = -1;
      ...
    

    如果此时屏幕刷新,您会看到闪烁。

  3. Windows 窗体是在那些年开发的,当时大多数人使用 CRT 监视器,半色调画笔(参见上文)看起来很平滑。如今,即使在静态图像上,一些LCD显示器也会闪烁。

解决方案是创建实心画笔而不是半色调画笔:

    var brush = default(LOGBRUSH);
    brush.lbColor = 0x2A2A2A; // Invert alternate bits except highest:  ..#.#.#.

并在移动时仅重绘差异矩形:

    private void DrawSplitBar(int mode)
    {
      ...
      if (mode != DRAW_END)
      {
        var rect = newRect;
        SubtractRect(out newRect, ref newRect, ref oldRect);
        SubtractRect(out oldRect, ref oldRect, ref rect);
      }
      DrawSplitHelper(oldRect);
      ...

在 https://gist.github.com/ArtemAvramenko/e260420b86564cf13d2e 查看我的解决方案

最新更新