даже карета перестала мигать

конец-то!) Нашел способ рендеринга элементов управления Windows.Forms на стекле, который, похоже, не имеет ни существенного недостатка, ни большого времени реализации. Это вдохновленоэта статья из Coded, который в основном объясняет, как естественным образом переопределить рисование элементов управления, чтобы рисовать над ними.

Я использовал этот подход, чтобы отобразить элемент управления в растровое изображение и закрасить его обратно с помощью GDI + и соответствующего альфа-канала над областью рисования NativeWindow. Реализация проста, но может быть усовершенствована для удобства использования, но не в этом вопрос. Результаты, однако, вполне удовлетворительные:

Однако есть две области, которые должны быть исправлены, чтобы это было действительно полезным.

Двойная буферизацияпотому что мерцание между этим оверлейным изображением и реальным контролем часто и ужасно (проверьте себя с помощью кода). Установка базового элемента управления с двойной буферизациейSetStyles(this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true) не работает, но я подозреваю, что мы можем сделать это с небольшим методом проб и ошибок.

Некоторые элементы управления не работают, Я смог сделать следующую работу:

Текстовое окноMaskedComboBoxComboBox (DropDownStyle == DropDownList)ListBoxCheckedListBoxПосмотреть списокВ виде дереваDateTimePickerMonthCalendar

Но я не могу заставить их работать, хотя я не понимаю, почему нет. Мое обоснованное предположение состоит в том, что в фактическом дескрипторе NativeWindow я ссылаюсь на весь элемент управления, в то время как мне нужно ссылаться на его «входную» (текстовую) часть, возможно, дочернюю. Любая помощь от экспертов WinAPI о том, как получить этот дескриптор окна ввода, приветствуется.

ComboBox (DropDownStyle! = DropDownList)NumericUpDownRichTextBox

Но исправление двойной буферизации было быОсновное внимание для удобства использования.

Вот пример использования:

new GlassControlRenderer(textBox1);

Вот код:

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.

РЕДАКТИРОВАТЬ: Возможные пути для двойной буферизации / анти-мерцания:

Удаление линииthis.Control.Invalidate() удаляет мерцание, но прерывает набор текста в текстовом поле.

Я попробовал подход WM_SETREDRAW и метод SuspendLayout, но не повезло:

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

Ответы на вопрос(2)

Ваш ответ на вопрос