本文介绍了呈现在玻璃上的控件:解决方案发现,需要双缓冲/完善的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我(终于!)发现,在玻璃上,似乎并没有什么重大的缺点,也没有什么大的实施时间呈现Windows.Forms的控制的一种方式。它的设计灵感来自从codeD这篇文章 ,这基本上说明了如何在本机覆盖控件的画吸引他们。

I (finally!) found a way of rendering Windows.Forms controls on glass that doesn't seem to have any major drawback nor any big implementation time. It's inspired by this article from Coded, which basically explains how to natively override the painting of controls to draw over them.

我用这种方法来呈现控件为位图,写生,回来GDI +及以上的NativeWindow的绘画面积适当的Alpha通道。实现较为简单,但可以完善可用性,但这不是对这个问题的地步。然而,结果是,比较让人满意的:

I used that approach to render the control to a bitmap and paint it back with GDI+ and the appropriate alpha channel over the NativeWindow's painting area. The implementation is simple but could be perfected for usability, but that's not the point of this question. The results are, however, quite satisfying:

有需要被固定但是,这是真正有用的,2区。

There are 2 areas that need to be fixed for this to be really usable, however.

  1. 双缓冲,因为这个覆盖图像和实际控制之间的闪烁频繁,可怕的(与code测试自己)。设置基本控制被双缓冲与 SetStyles(this.SetStyle(ControlStyles.OptimizedDoubleBuffer,真)不工作,但我怀疑,我们可以把它一点点工作试错。
  2. 有些控件不工作。我已经能够进行以下工作:

  1. Double-buffering, because the flicker between this overlay image and the real control is frequent and horrible (test yourself with the code). Setting the basic control to be double buffered with SetStyles(this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true) doesn't work, but I suspect we can make it work with a little trial and error.
  2. Some controls don't work. I've been able to make the following work:

  • 在文本框
  • MaskedComboBox
  • 组合框(DropDownStyle == DropDownList的)
  • 列表框
  • 的CheckedListBox
  • 的ListView
  • 在树视图
  • 的DateTimePicker
  • 的MonthCalendar

但我不能让这些工作,虽然我不明白为什么不能。我的猜测是,实际的NativeWindow手柄,我引用了整个控制,而我需要引用了输入(文字)的一部分,可能是一个孩子。从WinAPI的专家的帮助就如何获取输入窗口句柄是值得欢迎的。

But I can't get these to work, although I don't see why not. My educated guess is that the actual NativeWindow handle I'm referencing the whole control, while I need to reference the "input" (textual) part of it, probably a child. Any help from WinAPI experts on how to get that input window handle is welcome.

  • 组合框(DropDownStyle!= DropDownList的)
  • 的NumericUpDown
  • 的RichTextBox

不过,固定双缓冲会在重点的可用性。

But fixing the double buffering would be the main focus for usability.

下面是一个示例用法:

new GlassControlRenderer(textBox1);

这里的code:

Here's the code:

public class GlassControlRenderer : NativeWindow
{
    private Control Control;
    private Bitmap Bitmap;
    private Graphics ControlGraphics;

    protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case 0xF: // WM_PAINT
            case 0x85: // WM_NCPAINT
            case 0x100: // WM_KEYDOWN
            case 0x200: // WM_MOUSEMOVE
            case 0x201: // WM_LBUTTONDOWN
                this.Control.Invalidate();
                base.WndProc(ref m);
                this.CustomPaint();
                break;

            default:
                base.WndProc(ref m);
                break;
        }
    }

    public GlassControlRenderer(Control control)
    {
        this.Control = control;
        this.Bitmap = new Bitmap(this.Control.Width, this.Control.Height);
        this.ControlGraphics = Graphics.FromHwnd(this.Control.Handle);
        this.AssignHandle(this.Control.Handle);
    }

    public void CustomPaint()
    {
        this.Control.DrawToBitmap(this.Bitmap, new Rectangle(0, 0, this.Control.Width, this.Control.Height));
        this.ControlGraphics.DrawImageUnscaled(this.Bitmap, -1, -1); // -1, -1 for content controls (e.g. TextBox, ListBox)
    }
}

我会很高兴来解决这个问题,和一劳永逸有渲染的玻璃,所有的.NET控件,没有WPF的真正途径。

I'd be really glad to fix this, and once and for all have a real way of rendering on glass, for all .NET controls, without WPF.

编辑:为双缓冲/防闪烁可能的路径:

Possible paths for double-buffering/anti-flicker:

  • 删除线 this.Control.Invalidate()删除闪烁,但打破了一个文本框打字。
  • 我已经试过了WM_SETREDRAW方法和SuspendLayout方法,没有运气:

  • Removing the line this.Control.Invalidate() removes the flicker, but breaks the typing in a textbox.
  • I've tried the WM_SETREDRAW approach and the SuspendLayout method, with no luck:

[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();
}

protected override void WndProc(ref Message m)
{
    switch (m.Msg)
    {
        case 0xF: // WM_PAINT
        case 0x85: // WM_NCPAINT
        case 0x100: // WM_KEYDOWN
        case 0x200: // WM_MOUSEMOVE
        case 0x201: // WM_LBUTTONDOWN
            //this.Control.Parent.SuspendLayout();
            //GlassControlRenderer.SuspendDrawing(this.Control);
            //this.Control.Invalidate();
            base.WndProc(ref m);
            this.CustomPaint();
            //GlassControlRenderer.ResumeDrawing(this.Control);
            //this.Control.Parent.ResumeLayout();
            break;

        default:
            base.WndProc(ref m);
            break;
    }
}

推荐答案

下面是一个版本少得多的闪烁,还不够完善,但。

Here is a version with much less flickering, still not perfect though.

public class GlassControlRenderer : NativeWindow
{
    private Control Control;
    private Bitmap Bitmap;
    private Graphics ControlGraphics;

    private object Lock = new object();

    protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case 0x14: // WM_ERASEBKGND
                this.CustomPaint();
                break;

            case 0x0F: // WM_PAINT
            case 0x85: // WM_NCPAINT

            case 0x100: // WM_KEYDOWN
            case 0x101: // WM_KEYUP
            case 0x102: // WM_CHAR

            case 0x200: // WM_MOUSEMOVE
            case 0x2A1: // WM_MOUSEHOVER
            case 0x201: // WM_LBUTTONDOWN
            case 0x202: // WM_LBUTTONUP
            case 0x285: // WM_IME_SELECT

            case 0x300: // WM_CUT
            case 0x301: // WM_COPY
            case 0x302: // WM_PASTE
            case 0x303: // WM_CLEAR
            case 0x304: // WM_UNDO
                base.WndProc(ref m);
                this.CustomPaint();
                break;

            default:
                base.WndProc(ref m);
                break;
        }
    }

    private Point Offset { get; set; }

    public GlassControlRenderer(Control control, int xOffset, int yOffset)
    {
        this.Offset = new Point(xOffset, yOffset);
        this.Control = control;
        this.Bitmap = new Bitmap(this.Control.Width, this.Control.Height);
        this.ControlGraphics = Graphics.FromHwnd(this.Control.Handle);
        this.AssignHandle(this.Control.Handle);
    }

    public void CustomPaint()
    {
        this.Control.DrawToBitmap(this.Bitmap, new Rectangle(0, 0, this.Control.Width, this.Control.Height));
        this.ControlGraphics.DrawImageUnscaled(this.Bitmap, this.Offset); // -1, -1 for content controls (e.g. TextBox, ListBox)
    }
}

这篇关于呈现在玻璃上的控件:解决方案发现,需要双缓冲/完善的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-20 20:29