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

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

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

Может ли кто-нибудь заметить ошибку?

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using System.Windows.Forms;

public class GradientPanel : Panel
{
    private Color ColorA = Color.LightBlue;
    private Color ColorB = Color.Red;
    private LinearGradientMode GradientFillStyle = LinearGradientMode.ForwardDiagonal;
    private Brush gradientBrush;

    public Color colourStart
    {
        get { return ColorA; }
        set { ColorA = value; Invalidate(); }
    }
    public Color colourEnd
    {
        get { return ColorB; }
        set { ColorB = value; Invalidate(); }
    }
    public LinearGradientMode colourGradientStyle
    {
        get { return GradientFillStyle; }
        set { GradientFillStyle = value; Invalidate(); }
    }

    public GradientPanel()
    {
        handlerGradientChanged = new EventHandler(GradientChanged);
        ResizeRedraw = true;        
    }

    private EventHandler handlerGradientChanged;

    protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e)
    {
        gradientBrush = new LinearGradientBrush(ClientRectangle, ColorA, ColorB, GradientFillStyle);

        e.Graphics.FillRectangle(gradientBrush, ClientRectangle);
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (gradientBrush != null) gradientBrush.Dispose();
        }
        base.Dispose(disposing);
    }

    protected override void OnResize(EventArgs eventargs)
    {
        Invalidate();
        //base.OnResize(eventargs);
    }
    protected override void OnSizeChanged(EventArgs e)
    {
        Invalidate();
        //base.OnSizeChanged(e);
    }
    private void GradientChanged(object sender, EventArgs e)
    {
        if (gradientBrush != null) gradientBrush.Dispose();
        gradientBrush = null;
        Invalidate();
    }

}

person Neo    schedule 21.02.2012    source источник
comment
Я бы не стал создавать новый LinearGradientBrush в обработчике OnPaintBackground. Я предполагаю, что вы, возможно, достигли предела своего объекта GDI (который вы можете проверить в диспетчере задач), поскольку я не думаю, что вы правильно размещаете свои кисти. Переместите это в конструктор или в функцию, которая вызывается при изменении свойств цветов и стилей (и избавьтесь от предыдущего перед созданием нового)   -  person musefan    schedule 21.02.2012
comment
@musefan Спасибо за это, я переместил его в конструктор, но теперь он игнорирует переданные ему параметры (вместо того, чтобы показывать горизонтальный градиент от темно-красного до красного, он показывает значение по умолчанию, показанное в коде). Как мне обойти это, как я никогда не приходи к нему раньше   -  person Neo    schedule 21.02.2012
comment
Я отправил вам ответ, чтобы объяснить это подробнее, надеюсь, это сработает для вас.   -  person musefan    schedule 21.02.2012


Ответы (3)


Я бы не стал создавать новый LinearGradientBrush в обработчике OnPaintBackground.

Я предполагаю, что вы, возможно, достигли предела своего объекта GDI (который вы можете проверить в диспетчере задач), поскольку я не думаю, что вы правильно размещаете свои кисти.

Переместите это в конструктор или в функцию, которая вызывается при изменении свойств цветов и стилей (и избавьтесь от предыдущего перед созданием нового)

Вот пример того, что вы можете сделать, позволяя свойствам изменять цвета и т. д.

Изменить свойства:

public Color colourStart
{
    get { return ColorA; }
    set { ColorA = value; RefershBrush(); }
}
public Color colourEnd
{
    get { return ColorB; }
    set { ColorB = value; RefershBrush(); }
}
public LinearGradientMode colourGradientStyle
{
    get { return GradientFillStyle; }
    set { GradientFillStyle = value; RefershBrush(); }
}

Добавить функцию:

private void RefershBrush()
{
    //I think this IF block should work with no problems, been a while working with brush
    if(gradientBrush != null)
    {
        gradientBrush.Dispose();
    }

    gradientBrush = new LinearGradientBrush(ClientRectangle, ColorA, ColorB, GradientFillStyle);
    Invalidate();
}
person musefan    schedule 21.02.2012
comment
@Neo, возможно, это стоит прочитать, если у вас есть такая возможность: codeproject.com/Articles/165443/Disposing-GDI-Objects-in-C-NET ... хотя он не получил особого отклика у читателей, в нем подробно описаны проблемы, с которыми я столкнулся такие вещи, как вы видели сегодня, и я думаю, что там есть кое-что полезное, что нужно знать. плюс вы можете прочитать комментарии для любых рекомендаций от других читателей - person musefan; 21.02.2012

Я делаю что-то подобное, но даже очистка LinearGradientBrush не помогла мне. Глядя на вывод консоли, я заметил: «Первое случайное исключение типа System.ArgumentException произошло в System.Drawing.dll». Я считаю, что это связано с тем, что ClientRectangle равен 0,0, когда компонент свернут. Добавление этого кода, казалось, исправило это для меня:

  protected override void OnPaintBackground(PaintEventArgs e)
  {
     base.OnPaintBackground(e);
     if (this.gradientBrush != null)
        this.gradientBrush.Dispose();
     if (this.ClientRectangle.Width > 0 && this.ClientRectangle.Height > 0)
     {
        this.gradientBrush = new LinearGradientBrush(this.ClientRectangle,
           FROM_GRADIENT_COLOR, TO_GRADIENT_COLOR, LinearGradientMode.Horizontal);

        e.Graphics.FillRectangle(this.gradientBrush, this.ClientRectangle);
     }
  }
person vik    schedule 18.10.2012

Похоже, вы неправильно утилизируете кисть.

Код рефакторинга:

public class GradientPanel : Panel {
  private Color ColorA = Color.LightBlue;
  private Color ColorB = Color.Red;
  private LinearGradientMode GradientFillStyle = LinearGradientMode.ForwardDiagonal;

  public GradientPanel() {
    DoubleBuffered = true;
    ResizeRedraw = true;
  }

  public Color colourStart {
    get { return ColorA; }
    set { ColorA = value; Invalidate(); }
  }

  public Color colourEnd {
    get { return ColorB; }
    set { ColorB = value; Invalidate(); }
  }

  public LinearGradientMode colourGradientStyle {
    get { return GradientFillStyle; }
    set { GradientFillStyle = value; Invalidate(); }
  }

  protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e) {
    using (var gradientBrush = new LinearGradientBrush(ClientRectangle, ColorA, ColorB, GradientFillStyle)) {
      e.Graphics.FillRectangle(gradientBrush, ClientRectangle);
    }
  }
}

Вам не нужно аннулировать каждый Resize или SizeChanged, потому что у вас уже есть набор элементов управления со свойством ResizeRedraw=True. Я также добавил свойство DoubleBuffered=True для управления мерцанием.

Я поместил вашу кисть Gradient в блок Using(), чтобы она утилизировалась.

person LarsTech    schedule 21.02.2012
comment
Я попробовал ваш метод, все равно получил красный крест, но +1 за двойную буферизацию и использование :) - person Neo; 21.02.2012
comment
@Neo Тогда у тебя, скорее всего, заканчиваются ресурсы где-то в другом месте. Убедитесь, что в любых других элементах управления или формах, которые вы отображаете, вы правильно удаляете объекты. Откройте Диспетчер задач и процессы, покажите столбец «Объекты GDI» и посмотрите значение в вашем exe-файле при запуске вашей программы, особенно при минимизации и восстановлении. Если число продолжает расти, вы теряете ресурсы. - person LarsTech; 21.02.2012