Переопределение DrawItem для ListBox - невыбранные элементы не перерисовываются

Это настольное приложение на C#. DrawStyle собственность моего ListBox установлен в OwnerDrawFixed,

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

Вот мой код:

private void listDevices_DrawItem(object sender, DrawItemEventArgs e)
{
    e.DrawBackground();

    string textDevice = ((ListBox)sender).Items[e.Index].ToString();

    e.Graphics.DrawString(textDevice,
        new Font("Ariel", 15, FontStyle.Bold), new SolidBrush(Color.Black), 
        e.Bounds, StringFormat.GenericDefault);


    // Figure out where to draw IP
    StringFormat copy = new StringFormat(
        StringFormatFlags.NoWrap |
        StringFormatFlags.MeasureTrailingSpaces
    );
    copy.SetMeasurableCharacterRanges(new CharacterRange[] {new CharacterRange(0, textDevice.Length)});

    Region[] regions = e.Graphics.MeasureCharacterRanges(
        textDevice, new Font("Ariel", 15, FontStyle.Bold), e.Bounds, copy);

    int width = (int)(regions[0].GetBounds(e.Graphics).Width);
    Rectangle rect = e.Bounds;
    rect.X += width;
    rect.Width -= width;

    // draw IP
    e.Graphics.DrawString(" 255.255.255.255",
        new Font("Courier New", 10), new SolidBrush(Color.DarkBlue),
        rect, copy);

    e.DrawFocusRectangle();
}

listDevices.Items.Add("Device001");
listDevices.Items.Add("Device002");

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

2 ответа

Решение

Поместите следующий код в событие Resize:

private void listDevices_Resize(object sender, EventArgs e) {
    listDevices.Invalidate();
}

Это должно привести к перерисовке всего.

Чтобы остановить мерцание, вам нужна двойная буферизация.

Для этого создайте новый класс, производный от ListBox, и поместите в конструктор следующее:

this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

Или просто вставьте это в файл кода:

using System.Windows.Forms;

namespace Whatever {
    public class DBListBox : ListBox {
        public DBListBox(): base() {
            this.DoubleBuffered = true;
            // OR
            // this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
        }
    }
}

Замените "Что угодно" пространством имен, используемым вашим проектом, или сделайте его более полезным. После компиляции вы сможете добавить DBListBox в конструктор форм.

Я повторяю проблему. В коде есть несколько ошибок, имя шрифта "Arial", вы не должны корректировать rect.Width, вы забыли вызвать Dispose() для шрифтов, кистей и областей. Но они не объясняют поведение. Что-то не так с областью отсечения, которая препятствует правильному обновлению текста. Я не вижу, где это происходит, состояние объекта Graphics в порядке.

Graphics.DrawString () - очень проблемный метод, вам следует избегать его. Все элементы управления Windows Forms, включая ListBox, используют TextRenderer.DrawText(). Это решает проблему, когда я его использую. Я знаю, что измерение труднее, вы можете обойти это, отображая IP-адрес с фиксированным смещением. Выглядит лучше, они выстроятся в столбец таким образом.

Мерцает, потому что вы используете e.DrawBackground(). Это стирает существующий текст, вы рисуете текст обратно на него. Я не думаю, что двойная буферизация это исправит, вам нужно нарисовать весь элемент, чтобы не рисовать фон. Сложно, если вы не можете получить точный размер текста крупным шрифтом, обходной путь - сначала нарисовать растровое изображение.

Другие вопросы по тегам