Элементы управления отображением на стекле: Решение найдено, требуется двойная буферизация / совершенствование

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

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

Real textbox on glass

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

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

    • TextBox
    • MaskedComboBox
    • ComboBox (DropDownStyle == DropDownList)
    • ListBox
    • CheckedListBox
    • ListView
    • TreeView
    • DateTimePicker
    • MonthCalendar

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

    • ComboBox (DropDownStyle! = DropDownList)
    • NumericUpDown
    • RichTextBox

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

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

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")]
    общедоступный статический extern int SendMessage (IntPtr hWnd, Int32 wMsg, bool wParam, Int32 lParam);
    
    приватная константа int WM_SETREDRAW = 11;
    
    public static void SuspendDrawing (родительский элемент управления)
     {
      SendMessage (parent.Handle, WM_SETREDRAW, ложь, 0);
     }
    
    public static void ResumeDrawing (родительский элемент управления)
     {
      SendMessage (parent.Handle, WM_SETREDRAW, истина, 0);
      parent.Refresh ();
     }
    
    защищенное переопределение void WndProc (ссылка Сообщение m)
     {
      переключатель (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 (ссылка м);
      this.CustomPaint ();
      //GlassControlRenderer.ResumeDrawing(this.Control);
      //this.Control.Parent.ResumeLayout ();
      перемена;
    
      дефолт:
      base.WndProc (ссылка м);
      перемена;
      }
     }
     

46
задан Lazlo 15 August 2011 в 04:12
поделиться