Класс, производный от System.Windows.Forms.ProgressBar, не получает своего переопределения OnPaint

Я создал класс, производный от System.Windows.Forms.ProgressBar. Я следовал методу, рекомендованному MSDN:

  • Добавьте пользовательский элемент управления в свой проект
  • Откройте код пользовательского элемента управления
  • Вместо того, чтобы наследовать его от System.Windows.Forms.UserControl, наследовать от ProgressBar.

Кроме того, я переопределяю OnPaint, поэтому могу нарисовать его сам:

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    // etc, do your own painting.

Однако эта функция никогда не вызывается. Точка останова здесь не нарушается. Полоса прогресса красится нормально. Что мне не хватает?


person Harald Coppoolse    schedule 18.06.2013    source источник
comment
Индикатор выполнения - это собственный элемент управления Windows, он отрисовывается самостоятельно. Класс ProgressBar - это всего лишь небольшой класс-оболочка .NET для него, у него отключен стиль UserPaint, как и для всех собственных элементов управления Windows. Вы делаете классическую ошибку, не объясняя, почему, по вашему мнению, вам это нужно.   -  person Hans Passant    schedule 18.06.2013


Ответы (1)


Следующая процедура в точности соответствует предложению MSDN. Индикатор выполнения имеет функцию OnPaint, поэтому, согласно MSDN, ваш OnPaint должен вызываться.

Причина, по которой он не вызывается, заключается в том, что вы должны заявить, что сами возьмете на себя управление. В старом MFC это называлось OwnerDrawn. Чтобы сообщить системе, что вы хотите нарисовать свой элемент управления самостоятельно, вы должны изменить стиль элемента управления. Это делается с помощью Control.SetStyle:

public partial class ColorProgressBar : System.Windows.Forms.ProgressBar
{
    public ColorProgressBar()
    {
        InitializeComponent();
        this.SetStyle(ControlStyles.UserPaint, true);
        // etc, other initializations
    }

Это позаботится о том, чтобы ваш OnPaint был вызван.

В качестве примера полный ColorProgressBar. Код этого класса можно найти в другом месте, но здесь он переписан как производный класс от System.Windows.Forms.ProgressBar. Это делает код намного меньше. Кроме того, у вас есть все функции Progressbar.

Этот индикатор выполнения имеет не простой цвет для полосы, а цвет градиента между двумя цветами. Его можно добавить с помощью панели инструментов, как и любой другой элемент управления. Свойства можно изменить, как если бы вы изменили свойства индикатора выполнения. Внизу окна свойств вы увидите дополнительные свойства.

Для его создания:

  • Создайте свой проект
  • В Solution Exploser - Add - пользовательский элемент управления дайте ему имя, например ColorProgressBar.
  • Откройте код ColorProgressBar в редакторе
  • Измените базовый класс с UserControl на System.Windows.Forms.ProgressBar
  • Измените класс в соответствии с вашими потребностями, см. Пример ниже

Самая важная функция - это OnPaint, которая изменяет внешний вид ProgressBar. Остальное довольно просто:

  • Добавьте два свойства для описания цветов градиента
  • В конструкторе SetStyle, как описано выше, чтобы убедиться, что ваш OnPaint вызывается:

    общедоступный частичный класс ColorProgressBar: System.Windows.Forms.ProgressBar {общедоступный Color BarColorOutside {получить; установленный; } общедоступный Color BarColorCenter {получить; установленный; }

    public ColorProgressBar()
    {
        BarColorOutside = Color.Black;
        BarColorCenter = Color.Yellow;
        InitializeComponent();
        this.SetStyle(ControlStyles.UserPaint, true);
    }
    
    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        // your own painting will be added later
    }
    

Теперь проверим, работает ли эта база:

  • Строить
  • Создайте форму. Добавьте индикатор выполнения в форму
  • Используйте свойства, чтобы присвоить индикатору выполнения значение и начальные цвета.
  • Отладка, чтобы проверить, вызывается ли ваш onPaint.

Теперь файл onPaint. Залившаяся часть индикатора выполнения будет залита градиентным цветом. Для этого нам нужно знать ширину заливки и высоту индикатора выполнения. Мы можем сделать два прямоугольника: один заполнит верхнюю половину, а другой - нижнюю. Заливка будет выполняться градиентной кистью: верхняя половина от barColorOutside до barColorCenter, нижняя половина от barColorCenter до barColorOutside. Таким образом, центральный цвет будет в центре полосы прогресса.

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);

    // the part that has to be filled with a colored progress:
    int fillWidth = (Width * Value) / (Maximum - Minimum);
    if (fillWidth == 0)
    {    // nothing to fill
        return;
    }

    // the two rectangles:
    Rectangle topRect = new Rectangle(0, 0, fillWidth, Height / 2);
    Rectangle bottomRect = new Rectangle(0, Height / 2, fillWidth, Height);

    // Paint upper half: the gradient fills the complete topRect,
    // from background color to foreground color
    LinearGradientBrush brush = new LinearGradientBrush(topRect, BarColorOutside,  
        BarColorCenter, LinearGradientMode.Vertical);
    e.Graphics.FillRectangle(brush, topRect);
    brush.Dispose();

    // paint lower half: gradient fills the complete bottomRect,
    // from foreground color to background color
    brush = new LinearGradientBrush(bottomRect, BarColorCenter, BarColorOutside, 
        LinearGradientMode.Vertical);
    e.Graphics.FillRectangle(brush, bottomRect);
    brush.Dispose();

    // we have missed one line in the center: draw a line:
    Pen pen = new Pen(BarColorCenter);
    e.Graphics.DrawLine(pen, new Point(0, topRect.Bottom),
        new Point(fillWidth, topRect.Bottom));
    pen.Dispose();

    // if style is blocks, draw vertical lines to simulate blocks
    if (Style == ProgressBarStyle.Blocks)
    {
        int seperatorWidth = (int)(this.Height * 0.67);
        int NrOfSeparators = (int)(fillWidth / seperatorWidth);
        Color sepColor = ControlPaint.LightLight(BarColorCenter);

        for (int i = 1; i <= NrOfSeparators; i++)
        {
            e.Graphics.DrawLine(new Pen(sepColor, 1),
            seperatorWidth * i, 0, seperatorWidth * i, this.Height);
        }
    }
}
person Harald Coppoolse    schedule 18.06.2013