Я (наконец-то!) Нашел способ отрисовки элементов управления Windows.Forms на стекле, который, похоже, не имеет серьезных недостатков или большого времени на реализацию. Он вдохновлен этой статьей от Coded, в которой в основном объясняется, как изначально переопределить рисование элементов управления для рисования поверх них.
Я использовал этот подход для визуализации элемента управления в растровое изображение и его повторной окраски с помощью GDI + и соответствующий альфа-канал в области рисования NativeWindow. Реализация проста, но ее можно улучшить для удобства использования, но вопрос не в этом. Однако результаты вполне удовлетворительны:
Однако есть 2 области, которые необходимо исправить, чтобы это действительно можно было использовать.
SetStyles (this.SetStyle (ControlStyles.OptimizedDoubleBuffer, true)
не работает, но я подозреваю, что мы можем заставить его работать с небольшим методом проб и ошибок. Некоторые элементы управления не работают . Мне удалось выполнить следующие действия:
Но я не могу заставить их работать, хотя я не понимаю, почему нет. Мое обоснованное предположение состоит в том, что на настоящий дескриптор NativeWindow я ссылаюсь на весь элемент управления, в то время как мне нужно сослаться на его "вводную" (текстовую) часть,наверное ребенок. Любая помощь экспертов WinAPI в том, как получить этот дескриптор окна ввода, приветствуется.
Но исправление двойной буферизации будет основной задачей для удобства использования.
Вот пример использования:
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 (ссылка м);
перемена;
}
}