Почему моему методу рисования в c # не хватает памяти? - PullRequest
4 голосов
/ 08 сентября 2010

Я новичок в c # и пытаюсь учиться, написав несколько простых приложений для знакомства с синтаксисом и библиотекой .NET.Самый последний минипроект, который я взял, - это полярные часы , подобные найденным здесь .

Одна из проблем, которые я заметил ранее, заключалась в том, что приложение постоянно "мерцало", что действительноот презентации, поэтому я прочитал в Интернете о том, как реализовать двойной буфер, который устранил эту проблему, но может иметь или не иметь какое-то отношение к проблеме.Вот мой onPaint метод;он вызывается каждые 33 мс (~ 30 FPS) с помощью таймера.Большая часть остального приложения - это просто обработчики для перетаскивания приложения (поскольку оно безрамное и с прозрачным фоном), выход при двойном щелчке и т. Д.конец метода я буду в безопасности, но, похоже, это не поможет.Кроме того, интервал между временем выполнения и OutOfMemoryException не является постоянным;однажды это произошло всего через несколько секунд, но обычно это занимает минуту или две.Вот несколько общеклассовых объявлений переменных.

    private Bitmap _backBuffer;

    private float startAngle = -91F;
    private float brushWidth = 14;
    private float spaceStep = 6;

И снимок экрана (правка: ссылки на снимок экрана с представлением кода):

Снимок экрана http://www.ggot.org/inexplicable/pc2.jpg

РЕДАКТИРОВАТЬ: Stacktrace!

System.OutOfMemoryException: Out of memory.
   at System.Drawing.Graphics.CheckErrorStatus(Int32 status)
   at System.Drawing.Graphics.DrawArc(Pen pen, Single x, Single y, Single width, Single height, Single startAngle, Single sweepAngle)
   at PolarClock.clockActual.OnPaint(PaintEventArgs e) in C:\Redacted\PolarClock\clockActual.cs:line 111
   at System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer, Boolean disposeEventArgs)
   at System.Windows.Forms.Control.WmPaint(Message& m)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

Кажется, что это та же самая строка, на которой произошел сбой в последний раз, основной drawArc внутри цикла.

Ответы [ 6 ]

8 голосов
/ 08 сентября 2010

Убедитесь, что вы утилизируете объекты Pen и Brush и используете блоки, чтобы убедиться, что вы утилизируете объекты, даже если есть исключения.

В качестве примечания: избегайте воссоздания и утилизации _backBuffer каждый раз, когда вырисовать.Либо поймайте событие resize и утилизируйте _backBuffer там, либо просто проверьте, имеет ли _backBuffer правильные размеры для каждого события Paint, и утилизируйте и воссоздайте, если измерения не совпадают.

6 голосов
/ 21 сентября 2011

Просто для других, поиск этой страницы через Google:

Возможная причина System.OutOfMemoryException, если вы используете System.Drawing.DrawArc, также может быть ошибкой, если вы пытаетесь напечатать небольшие углы.

Для углов <1 эта ошибка возникала несколько раз в моем коде. </p>

См. Также:

http://connect.microsoft.com/VisualStudio/feedback/details/121532/drawarc-out-of-memory-exception-on-small-arcs

3 голосов
/ 08 сентября 2010

Я не нашел ничего ужасного в вашем коде.Можете ли вы указать точную линию, по которой происходит OutOfMemoryException?

Просто, чтобы вы знали, мне действительно потребовалось несколько месяцев, чтобы понять: OutOfMemoryException не означает нехватку памяти.;-) Это происходит в GDI +, когда что-то просто пошло не так (показывает плохой стиль кодирования внутри GDI, IMHO), как будто вы пытались загрузить недопустимое изображение или изображение с недопустимым форматом пикселей и т. Д.

2 голосов
/ 08 сентября 2010

Не совсем ответ на почему , но возможное решение:

Вы не должны каждый раз создавать новое растровое изображение. Просто очищайте его каждый раз, когда вы рисуете новую рамку.

Однако вы должны создать новое растровое изображение при изменении вашего размера.

1 голос
/ 08 сентября 2010

Зачем вам нужно новое растровое изображение каждый раз, когда вы хотите что-то нарисовать с OnPaint ?!Вам нужно ровно 1. Попробуйте что-то вроде этого:

private Bitmap _backBuffer = new Bitmap(this.Width, this.Height);

protected override void OnPaint(PaintEventArgs e) { 

    Graphics g = Graphics.FromImage(_backBuffer);

    //Clear back buffer with white color...
    g.Clear(Color.White);

    //Draw all new stuff...
}
0 голосов
/ 08 сентября 2010

Не ответ на ваш вопрос, и, возможно, есть веская причина, почему вы делаете это таким образом (я мог бы чему-то научиться), но зачем сначала создавать растровое изображение, рисовать на растровом изображении и затем рисовать растровое изображение на форме?Разве не было бы более эффективно рисовать прямо в форме?Что-то вроде этого:

protected override void OnPaint(PaintEventArgs e) {
    base.OnPaint(e);
    //_backBuffer = new Bitmap(this.Width, this.Height);
    Graphics g = Graphics.FromImage(_backBuffer);

    //Rest of your code
    //e.Graphics.DrawImageUnscaled(_backBuffer, 0, 0);

    //g.Dispose();
    //e.Dispose();
    //base.OnPaint(e);

    //_backBuffer.Dispose();
    //_backBuffer = null;
}

Также в соответствии с MSDN

При переопределении OnPaint в производном классе, обязательно вызовите базовый классМетод OnPaint, чтобы зарегистрированные делегаты получали событие.

...