Что может заставить Double Buffering убить мое приложение? - PullRequest
8 голосов
/ 03 февраля 2012

У меня есть несколько пользовательских (winforms) компонентов, которые рисуются на экране с помощью GDI +.

Чтобы предотвратить мерцание при перерисовке, я решил включить двойную буферизацию, поэтому добавил в конструктор строку:

public ColourWheel()
{
    InitializeComponent();
    this.DoubleBuffered = true;
}

, который прекрасно работает на этом компоненте (ColourWheel).Когда я добавляю эту же строку в конструктор любого из двух других (аналогично структурированных) компонентов, я получаю пару странных симптомов:

  1. Когда я пытаюсь запустить форму с включенным компонентом,Я получаю исключение аргумента на Application.Run(new Form());.
  2. Если я переключаюсь в режим разработки, я получаю сообщение об ошибке, что у компонента есть необработанное исключение, связанное с параметром.

ЭтоКажется, не имеет значения, включаю ли я двойную буферизацию на одном или на всех из них, это все еще работает на ColourWheel, но не на других.

Для протокола, я также попробовал несколькодругие двойные методы буферизации .

Что может вызывать двойную буферизацию для одного компонента, но не для других?


РЕДАКТИРОВАТЬ: Вот подробности исключения из симптома времени выполнения:

System.ArgumentException был необработан Сообщение = Параметр недействителен.Source = System.Drawing StackTrace: в System.Drawing.Graphics.GetHdc () в System.Drawing.BufferedGraphics.RenderInternal (HandleRef refTargetDC, буфер BufferedGraphics) в System.Drawing.BufferedGraphics.Render () в System.Windows.Forms.Control.WmPaint (Сообщение & m) в System.Windows.Forms.Control.WndProc (Сообщение & m) в System.Windows.Forms.ScrollableControl.WndProc (Сообщение & m) в System.Windows.Forms.UserControl.WndProc (Сообщение & m) в System.Windows.Forms.Control.ControlNativeWindow.OnMessage (Message & m) в System.Windows.Forms.Control.ControlNativeWindow.WndProc (Message & m) в System.Windows.Forms.NativeWindow.DebuggableCallback (IntPtr hWnd, Int32 msg, IntP, Int32 msg, Int.IntPtr lparam) в System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW (MSG & msg) в System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPwataPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayPlayChintPlayPlayPlayPlayPlayChintPlayPlayPlayPlayPlayPlayChintPlayPlayPlayPlayPlayPlayPlayChillSystem.Windows.Forms.Application.ThreadContext.RunMessageLoopInner (причина Int32, контекст ApplicationContext) в System.Windows.Forms.Application.ThreadContext.RunMessageLoop (причина Int32, контекст ApplicationContext) в System.Windows.Forms.Application.Run (форма mainForm) в TestForm.Program.Main () вD: \ Documents and Settings \ Том Райт \ Мои документы \ Visual Studio 2010 \ Projects \ ColourPicker \ TestForm \ Program.cs: строка 18 в System.AppDomain._nExecuteAssembly (сборка RuntimeAssembly, аргументы String []) в System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String [] args) в Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly () в System.Threading.ThreadHelper.ThreadStart_Context (состояние объекта) в System.Threading.ExecutionContext.Runback, Состояние объекта, Boolean ignoreSyncCtx) в System.Threading.ExecutionContext.Run (ExecutionContext executeContext, ContextCallback callback, Состояние объекта) в System.Threading.ThreadHelper.ThreadStart () InnerException:


РЕДАКТИРОВАТЬ 2: Обработчик OnPaint из одного (более сложного) из двух компонентов, которые вызывают проблемы:

private void ValueSlider_Paint(object sender, PaintEventArgs e)
{
       using (Graphics g = e.Graphics)
       {
           g.DrawImage(this.gradientImage, new Rectangle(0, 0, paintArea.Width, paintArea.Height));
           if (this.showmarker)
           {
               ColourHandler.HSV alt = ColourHandler.RGBtoHSV(new ColourHandler.RGB(this.SelectedColour.R, this.SelectedColour.G, this.SelectedColour.B));
               alt.Saturation = 0;
               alt.value = 255 - alt.value;
               using (Pen pen = new Pen(ColourHandler.HSVtoColour(alt)))
               {
                   pen.Width = (float)MARKERWIDTH;
                   g.DrawRectangle(pen, 0 - pen.Width, this.brightnessPoint.Y - MARKERWIDTH, this.paintArea.Width + (pen.Width * 2), MARKERWIDTH * 2);
               }
           }
        }
}

Ответы [ 2 ]

10 голосов
/ 04 февраля 2012

Вы не должны утилизировать объект Graphics, предоставленный вам во время события Paint, и это то, что неправильно делает ваш блок using.

Симптом состоит в том, что в следующий разСобытие Paint запускается, вы получаете тот же объект Graphics обратно, но он больше не привязан к находящемуся в памяти HDC, что приводит к сбою Graphics.GetHdc(), как видно из трассировки стека.

  1. Вполне возможно, что он переживает одно событие Paint (и это очень вероятно в случае двойной буферизации, хотя это также возможно в случае одиночной буферизации, если установлен стиль окна CS_OWNDC).

  2. Для события Paint может быть несколько обработчиков.

Таким образом, обработчики событий не должны вызывать Dispose для Graphics объектов или позволить блоку using сделать это.Вместо этого .NET Framework очищает ресурсы соответствующим образом после завершения обработки Paint.

1 голос
/ 04 февраля 2012

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

...