2013-05-09 8 views
1

[C# .NET 4.0]Как предотвратить мерцание формы без полей при изменении размера (C#)?

Я изучаю C#, и я пытаюсь построить Windows Form с помощью C#, который имеет FormBorderStyle = FormBorderStyle.None и может быть перемещен/изменении размера с помощью API Windows. В качестве примера я использую округленные угловые или пользовательские (перемещаемые/изменяемые размеры) схемы границ, используемые для Google Chrome и Norton 360 в качестве основы для моей формы.

Я до сих пор добился большого прогресса и получил все, чтобы работать, кроме , когда я изменяю размер формы, на длине правой и нижней границы мерцает черное/белое изображение при изменении размера формы быстро.

Я попытался добавить this.DoubleBuffer = true в конструктор, а также попробовал this.SetStyles(ControlStyles.AllPaintInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.UserPaint, true);.

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

Я должен также упомянуть, что я использую Windows XP, так что я не уверен, что this post мне поможет, так как это, кажется, сосредоточены на Vista/7 (с DWM) ... не то, что я» m достаточно продвинулся, чтобы понять все в этом посте.

Ниже приведены две части кода, которые работают с API. У меня есть публичное перечисление для WM_NCHITTEST для Windows API ... вы можете увидеть значения in this link.

OnPaint Перекрыть метод:

protected override void OnPaint(PaintEventArgs e) 
{ 
    System.IntPtr ptrBorder = CreateRoundRectRgn(0, 0, 
     this.ClientSize.Width, this.ClientSize.Height, 15, 15); 

    SetWindowRgn(this.Handle, ptrBorder, true); 

    Rectangle rc = new Rectangle(this.ClientSize.Width - cGrip, 
     this.ClientSize.Height - cGrip, cGrip, cGrip); 
    ControlPaint.DrawSizeGrip(e.Graphics, this.BackColor, rc); 
    rc = new Rectangle(0, 0, this.ClientSize.Width, 32); 
    e.Graphics.FillRectangle(Brushes.SlateGray, rc); 
} 

WNDPROC Override Метод:

protected override void WndProc(ref Message m) 
{ 
    if (m.Msg == (int)HitTest.WM_NCHITTEST) 
    { 
     // Trap WM_NCHITTEST 
     Point pos = new Point(m.LParam.ToInt32() & 0xffff, m.LParam.ToInt32() >> 16); 
     pos = this.PointToClient(pos); 

     if (pos.Y < cCaption) 
     { 
      m.Result = (IntPtr)HitTest.HTCAPTION; 
      return; 
     } 

     if (pos.X <= cGrip && pos.Y >= this.ClientSize.Height - cGrip) 
     { 
      m.Result = (IntPtr)HitTest.HTBOTTOMLEFT; 
      return; 
     } 

     if (pos.X >= this.ClientSize.Width - cGrip && 
      pos.Y >= this.ClientSize.Height - cGrip) 
     { 
      m.Result = (IntPtr)HitTest.HTBOTTOMRIGHT; 
      return; 
     } 

     if (pos.X >= this.ClientSize.Width - cBorder) 
     { 
      m.Result = (IntPtr)HitTest.HTRIGHT; 
      return; 
     } 

     if (pos.Y >= this.ClientSize.Height - cBorder) 
     { 
      m.Result = (IntPtr)HitTest.HTBOTTOM; 
      return; 
     } 

     if (pos.X <= cBorder) 
     { 
      m.Result = (IntPtr)HitTest.HTLEFT; 
      return; 
     } 
    } 

    base.WndProc(ref m); 
} 

А вот полный код:

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 
using System.Runtime.InteropServices; 

namespace PracticeForm 
{ 
    public partial class Form2 : Form 
    { 
     [DllImport("user32.dll")] 
     private static extern int SetWindowRgn(IntPtr hWnd, IntPtr hRgn, bool bRedraw); 

     [DllImport("gdi32.dll")] 
     private static extern IntPtr CreateRoundRectRgn(int x1, int y1, int x2, int y2, int cx, int cy); 

     [DllImport("gdi32.dll", EntryPoint = "DeleteObject")] 
     private static extern bool DeleteObject(System.IntPtr hObject); 

     private const int cGrip = 20; 
     private const int cCaption = 35; 
     private const int cBorder = 7; 
     private Point mouseOffset; 

     public Form2() 
     { 
      InitializeComponent(); 
      this.FormBorderStyle = FormBorderStyle.None; 
      this.MaximumSize = new Size(670, 440); 
      this.DoubleBuffered = true; 
      this.SetStyle(ControlStyles.ResizeRedraw | 
          ControlStyles.OptimizedDoubleBuffer | 
          ControlStyles.AllPaintingInWmPaint | 
          ControlStyles.UserPaint, true); 
     } 

     protected override void OnPaint(PaintEventArgs e) 
     { 
      System.IntPtr ptrBorder = CreateRoundRectRgn(0, 0, 
       this.ClientSize.Width, this.ClientSize.Height, 15, 15); 

      SetWindowRgn(this.Handle, ptrBorder, true); 

      Rectangle rc = new Rectangle(this.ClientSize.Width - cGrip, 
       this.ClientSize.Height - cGrip, cGrip, cGrip); 
      ControlPaint.DrawSizeGrip(e.Graphics, this.BackColor, rc); 
     } 

     protected override void WndProc(ref Message m) 
     { 
      if (m.Msg == (int)HitTest.WM_NCHITTEST) 
      { 
       // Trap WM_NCHITTEST 
       Point pos = new Point(m.LParam.ToInt32() & 0xffff, m.LParam.ToInt32() >> 16); 
       pos = this.PointToClient(pos); 

       if (pos.Y < cCaption) 
       { 
        m.Result = (IntPtr)HitTest.HTCAPTION; 
        return; 
       } 

       if (pos.X <= cGrip && pos.Y >= this.ClientSize.Height - cGrip) 
       { 
        m.Result = (IntPtr)HitTest.HTBOTTOMLEFT; 
        return; 
       } 

       if (pos.X >= this.ClientSize.Width - cGrip && 
        pos.Y >= this.ClientSize.Height - cGrip) 
       { 
        m.Result = (IntPtr)HitTest.HTBOTTOMRIGHT; 
        return; 
       } 

       if (pos.X >= this.ClientSize.Width - cBorder) 
       { 
        m.Result = (IntPtr)HitTest.HTRIGHT; 
        return; 
       } 

       if (pos.Y >= this.ClientSize.Height - cBorder) 
       { 
        m.Result = (IntPtr)HitTest.HTBOTTOM; 
        return; 
       } 

       if (pos.X <= cBorder) 
       { 
        m.Result = (IntPtr)HitTest.HTLEFT; 
        return; 
       } 
      } 

      base.WndProc(ref m); 
     } 

     private void button1_Click(object sender, EventArgs e) 
     { 
      this.Close(); 
     } 

     private void button2_MouseClick(object sender, MouseEventArgs e) 
     { 
      this.WindowState = FormWindowState.Minimized; 
     } 

     private void panel1_MouseDown(object sender, MouseEventArgs e) 
     { 
      mouseOffset = new Point(-e.X, -e.Y); 
     } 

     private void panel1_MouseMove(object sender, MouseEventArgs e) 
     { 
      if (e.Button == MouseButtons.Left) 
      { 
       Point p = Control.MousePosition; 
       p.Offset(mouseOffset.X, mouseOffset.Y); 
       Location = p; 
      } 
     } 

     private void label1_MouseDown(object sender, MouseEventArgs e) 
     { 
      mouseOffset = new Point(-e.X, -e.Y); 
     } 

     private void label1_MouseMove(object sender, MouseEventArgs e) 
     { 
      if (e.Button == MouseButtons.Left) 
      { 
       Point p = Control.MousePosition; 
       p.Offset(mouseOffset.X, mouseOffset.Y); 
       Location = p; 
      } 
     } 
    } 
} 

Спасибо за помощь.

ответ

0

только установить регион, когда форма фактически изменяет РАЗМЕР, не каждый раз в Paint() событие:

protected override void OnSizeChanged(EventArgs e) 
    { 
     base.OnSizeChanged(e); 

     System.IntPtr ptrBorder = CreateRoundRectRgn(0, 0, 
      this.ClientSize.Width, this.ClientSize.Height, 15, 15); 

     SetWindowRgn(this.Handle, ptrBorder, true); 
    } 

    protected override void OnPaint(PaintEventArgs e) 
    { 

     Rectangle rc = new Rectangle(this.ClientSize.Width - cGrip, 
      this.ClientSize.Height - cGrip, cGrip, cGrip); 
     ControlPaint.DrawSizeGrip(e.Graphics, this.BackColor, rc); 
    } 
+0

Возможно, это немного улучшилось, но оно все еще мерцает очень плохо. Кроме того, по какой-то причине ваш код, по-видимому, переопределил начальную позицию форм (CenterScreen). – user2063351

+0

У вас есть изображение в качестве фона формы? ... или, возможно, более сложная картина происходит в событии Paint()? –

+0

Нет, все в форме создается в коде (и весь мой код, кроме класса перечисления, указан в моем сообщении). Это прямоугольная область, которая накладывает форму без полей, чтобы создать закругленные углы (как я понимаю). И, конечно, есть дополнительный прямоугольник для настраиваемого SizeGrip. Я думаю, что прямоугольник, создающий закругленные углы, вызывает проблемы, потому что он, кажется, не мерцает (или мерцает почти так же плохо), когда я вообще удаляю этот код CreateRoundRectRgn. Но закругленные углы - важная визуальная реализация для меня. – user2063351

1

Мерцание происходит потому, что области дисплея меняют цвет быстро, что, в свою очередь, происходит потому что вы перегружаете - рисуете более одного предмета на одном пикселе.

Это происходит потому, что:

  • если ваша перерисовка медленно, то материал, который был на экране (например, границы окна), которые вы рисуете поверх будут видны на некоторое время. например Пользователь может видеть две копии вашей полосы прокрутки, пока вы не закончите стирание старого с содержимым вашей формы.
  • окна автоматически стирают фон вашего окна для вас., Обычно в белом. Любые области вашего рисунка, которые не являются белыми, поэтому на мгновение вспыхнут белым, прежде чем вы перевернете его с правильным изображением.
  • если вы рисуете несколько вещей в одном месте, вы будете видеть мерцает, как вы сохранить изменения цвета в этой области экрана

Чтобы устранить эти проблемы, необходимо сочетание вещей (более тем лучше)

  • отключить фон стирает или установить цвет стирания для доминирующего цвета в изображении
  • оптимизировать свой код перерисовки, чтобы сделать это быстрее, так что мерцание менее заметным
  • оптимизирует вам r перерисовать код, чтобы устранить лишний ход. Например. чтобы поместить границу вокруг прямоугольной страницы, вы можете нарисовать фоновый цвет и перегрузить его со страницы, но это будет мерцать. Вместо этого нарисуйте верхнюю границу как прямоугольник, затем нижний левый и правый, затем нарисуйте страницу посередине. Поскольку nopixels нарисованы более одного раза, он не будет мерцать
  • включить режим DoubleBuffered на вашем контроле. При этом весь ваш рисунок на самом деле происходит в растровое изображение в памяти, и окончательное изображение затем копируется на экран, так что каждый пиксель отображается только один раз, и нет мерцания.
+0

Я ценю, что вы нашли время, чтобы объяснить вещи ... однако, поскольку я все еще участвую, многие из того, что вы написали, пока не имеют никакого смысла (хотя я буду искать некоторые вещи). Я хочу отметить, что я указал в своем сообщении, что не только использую режим DoubleBuffered, о котором вы указали, но и SetStyles (ControlStyles ...) безрезультатно. – user2063351

+0

Я вижу, что одновременно рисую две вещи в одном и том же месте, но я еще недостаточно опытен, чтобы реализовать ваши предложения по устранению конфликта пиксельной живописи между прямоугольником, который я рисую, который округляет углы моей формы и прямоугольника, который я рисую, служит как SizeGrip. – user2063351

+0

Вы делаете довольно продвинутые вещи для «недостаточно опытных» :-) Попробуйте переопределить OnPaintBackground (ничего не делать, чтобы избежать двойной краски) и оптимизировать ваш код, чтобы вы ничего не делали в своем OnPaint, не нужно - например создайте свой регион/прямоугольники в OnSizeChanged, а не в каждом запросе на краску. Обратите также внимание на то, что изменение области окна может вызвать перерисовку, которая сама по себе может вызвать мерцание. –

0

Хотя это довольно старая нить, и ОП, скорее всего, нашел решение своей проблемы и двинулся дальше, я хотел добавить несколько дополнительных пунктов, если они окажутся полезными для .NET-разработчика, который работает аналогичная проблема.

Во-первых, моя шляпа отправляется к вам за попытку решить эту проблему в Windows XP. Я был там, провел там много часов и изучил все тяжелые уроки в результате. К сожалению, поскольку в Windows XP отсутствует DWM, к которому привыкли большинство из нас, нет простого решения.

Настройки в ControlStyles должным образом абсолютно необходимо - я бы также включать в себя:

SetStyle(ControlStyles.Opaque, True) 

Дважды буферные элементы управления, которые вы намерены сделать это важно, потому что мерцание вызвано главным управлением быть перерисованы в середине вертикального обратного хода монитора. Просто потому, что вы вызвали Invalidate(), это не обязательно означает, что элемент управления будет перерисовываться, когда вы этого захотите, - вы находитесь во власти Windows, и ОС сделает это, когда оно будет готово. Вы можете обойти это (как и я) в Windows XP, используя функцию WaitForVerticalBlank от DirecDraw 7 (много поддержки в Windows XP для этого API), а также с помощью GetVerticalBlankStatus и GetScanLine для своевременного рендеринга и презентации.

Смежные вопросы