Member 10190163 Ответов: 1

элемент управления ListBox элемент, изменить цвет текста


Кто-нибудь знает, почему команда Listbox1.Refresh() не может каждый раз вызывать суб ListBox1_DrawItem?

In Microsoft Visual Basic 2010, a listbox has a forcolor and backcolor property. These properties change the forcolour and backcolor for all the items in the listbox. By default there is no property for the forecolor and backcolor of an individual item on a listbox, I am aware there is on a list view but I would still wish to use a listbox. I am trying to have the ability to change the forecolor and backcolor properties of individual items in the listbox. To do this the listbox's draw item sub must be used with the listbox's drawmode property set to OwnerDrawFixed. Then using a brush colour along with the e.graphics the forecolor or backcolor can be changed. I have seen and followed examples of how to do this for the currently selected item. Such as the one from ehow's website. What I am tiring to do however is change the colour of the litsbox item as it is added depending on a variable.

Вот мой код:
Private Sub listbox_add()

        Me.ListBox1.Items.Add(listbox_text(list_num)) ' adds the line to the list box
        add_item_colour = True
        ListBox1.Refresh()

End Sub



    Private Sub ListBox1_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles ListBox1.DrawItem
    Dim myBrush As Brush = Brushes.Black

    e.DrawBackground()
    If add_item_colour = True Then
        If blue_message = True Then
            myBrush = Brushes.Blue
        Else
            myBrush = Brushes.Black
        End If
        e.Graphics.DrawString(ListBox1.Items.Item(list_num), ListBox1.Font, myBrush, _
        New RectangleF(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height))
        add_item_colour = False
    End If
    e.DrawFocusRectangle()
End Sub


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

Проблема, с которой я, кажется, сталкиваюсь, заключается в том, что команда Listbox1.Refresh (), похоже, не запускает суб ListBox1_DrawItem каждый раз, когда она вызывается. Я нашел это, используя точки торможения. Кто-нибудь знает, почему это может быть так и как я могу это исправить?

Спасибо, любая помощь по этому вопросу будет очень признательна.

Richard C Bishop

Как вы думаете, почему вызов метода .Refresh() приведет к запуску другого метода?

Member 10190163

Потому что у меня сложилось впечатление, что обновление заставит listbox быть перерисованным, а это означает, что будет вызван суб ListBox1_DrawItem. Разве это не так?

Richard C Bishop

Он действительно перерисовывает себя. Щелкните правой кнопкой мыши на методе .Refresh() и выберите пункт "Перейти к определению". Это код, который он запускает.

1 Ответов

Рейтинг:
6

Mike Meinz

успешный пример - Пересмотрено 5 сентября 2013 года 10:57 EDT, чтобы включить RemoveAt
Следующий код работает.
Я тестировал с помощью Visual Studio 2012 на ПК с Windows 8.

С помощью интерактивного отладчика Visual Studio я определил, что все видимые строки в ListBox обновляются при добавлении новой строки и что если новая строка не видна, то она не рисуется. Поэтому мне пришлось сохранить цвет кисти для каждой строки в коллекции до добавление новой строки и использование этого цвета кисти каждый раз, когда рисуется связанная строка списка. Я использую класс Collection для сохранения цветов кисти для каждой строки списка.

То Refresh метод был не нужен.

Dim colColors As Collection = New Collection ' Space to save brush colors for each row in the ListBox

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    ListBox1.DrawMode = DrawMode.OwnerDrawFixed
End Sub

Private Sub listbox_add_Blue()
    SetColorForNewRow(Brushes.Blue)
    ListBox1.Items.Add("Blue") ' adds the line to the list box
End Sub

Private Sub listbox_add_Black()
    SetColorForNewRow(Brushes.Black)
    ListBox1.Items.Add("Black") ' adds the line to the list box
End Sub

Sub listbox_remove_0()
    If ListBox1.Items.Count < 1 Then Exit Sub
    ' Must remove brush color from colColors collection first!
    colColors.Remove(1) ' Collection index starts at 1
    ListBox1.Items.RemoveAt(0)
End Sub

Private Sub ListBox1_DrawItem(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles ListBox1.DrawItem
    e.DrawBackground()
    If e.Index >= 0 AndAlso e.Index < ListBox1.Items.Count Then
        e.Graphics.DrawString(ListBox1.Items(e.Index).ToString(), _
            e.Font, colColors.Item(1 + e.Index), _
            e.Bounds, StringFormat.GenericDefault)
    End If
    e.DrawFocusRectangle()
End Sub

Sub SetColorForNewRow(ByVal MyBrush As Brush)
    colColors.Add(MyBrush)
End Sub

Private Sub btnBlack_Click(sender As Object, e As EventArgs) Handles btnBlack.Click
    listbox_add_Black()
End Sub

Private Sub btnBlue_Click(sender As Object, e As EventArgs) Handles btnBlue.Click
    listbox_add_Blue()
End Sub

Private Sub btnRemove_Click(sender As Object, e As EventArgs) Handles btnRemove.Click
    listbox_remove_0()
End Sub




_____________________________________________________________________________________________
Первая попытка использовать массив для хранения цветов кисти (частичный успех)
Следующий код работает для небольшого количества элементов в ListBox Я тестировал с помощью Visual Studio 2012 на ПК с Windows 8. Я все еще изучаю, как обращаться с этим DrawItem событие, когда активна вертикальная полоса прокрутки.

С помощью интерактивного отладчика Visual Studio я определил, что все строки в ListBox обновляются при добавлении новой строки. Поэтому мне пришлось сохранять цвет кисти для каждой строки в массиве и использовать этот цвет кисти каждый раз, когда перерисовывается связанная строка списка. То Refresh метод был не нужен.

Это было бы намного проще с a ListView контроль.

Dim blue_message As Boolean = False ' Use Black or Blue Brush
Dim AddMode As Boolean = False  ' Tells DrawItem that it is in Add to ListBox mode
Dim intNewIndex As Integer ' The index of the new item to be added to the ListBox
Dim arrColors(0 To 100) As Brush ' Space to save brush colors for each row in the ListBox

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    ListBox1.DrawMode = DrawMode.OwnerDrawFixed
End Sub

Private Sub listbox_add_Blue()
    blue_message = True
    AddMode = True
    intNewIndex = ListBox1.Items.Count
    ListBox1.Items.Add("Blue") ' adds the line to the list box
End Sub

Private Sub listbox_add_Black()
    blue_message = False
    AddMode = True
    intNewIndex = ListBox1.Items.Count
    ListBox1.Items.Add("Black") ' adds the line to the list box
End Sub

Private Sub ListBox1_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles ListBox1.DrawItem
    Dim myBrush As Brush = Brushes.Black
    e.DrawBackground()
    If AddMode AndAlso e.Index = intNewIndex Then
        If blue_message Then
            myBrush = Brushes.Blue
        End If
        e.Graphics.DrawString(ListBox1.Items(e.Index).ToString(), e.Font, myBrush, _
            e.Bounds, StringFormat.GenericDefault)
        If e.Index > arrColors.GetUpperBound(0) Then ' Is the array filled
            ReDim Preserve arrColors(0 To arrColors.Count + 100) ' Make some more room
        End If
        arrColors(e.Index) = myBrush ' Save which brush we used
        AddMode = False ' Finished drawing the new item (It is the last row of the ListBox
    Else
        e.Graphics.DrawString(ListBox1.Items(e.Index).ToString(), _
            e.Font, arrColors(e.Index), _
            e.Bounds, StringFormat.GenericDefault)
    End If
    e.DrawFocusRectangle()
End Sub

Private Sub btnBlack_Click(sender As Object, e As EventArgs) Handles btnBlack.Click
    listbox_add_Black()
End Sub

Private Sub btnBlue_Click(sender As Object, e As EventArgs) Handles btnBlue.Click
    listbox_add_Blue()
End Sub



_____________________________________________________________________________________________
Оригинальная версия решения
Я посмотрел на странице справки для поля списка.DrawItem event и внес несколько изменений в e.Graphics.DrawString заявление. Кроме того, обратите внимание, что я переместил местоположение add_item_colour = True.

Я не стал проверять.

Private Sub listbox_add()
        add_item_colour = True
        Me.ListBox1.Items.Add(listbox_text(list_num)) ' adds the line to the list box
        ListBox1.Refresh()
End Sub

    Private Sub ListBox1_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles ListBox1.DrawItem
    Dim myBrush As Brush = Brushes.Black

    e.DrawBackground()
    If add_item_colour = True Then
        If blue_message = True Then
            myBrush = Brushes.Blue
        Else
            myBrush = Brushes.Black
        End If
        e.Graphics.DrawString(ListBox1.Items(e.Index).ToString(), e.Font, myBrush, _
            e.Bounds, StringFormat.GenericDefault)
        add_item_colour = False
    End If
    e.DrawFocusRectangle()
End Sub


Member 10190163

Спасибо за предложение, но, к сожалению, оно не сработало.
Я получал исключение каждый раз, когда что-то добавлялось: первое случайное исключение типа "система".InvalidOperationException' произошло в системе.Окна.Forms.dll
Кроме того, я видел, как добавлялись некоторые черные и синие элементы, но проблема заключалась в том, что они добавлялись только к первому элементу, а затем они были очищены.

Member 10190163

Я также связал его с этой линией: e.Graphics.Шнурок(Список Listbox1.Предметы.Пункт(list_num).ToString(), e.Font, myBrush, e.Bounds, StringFormat.GenericDefault)
просто на всякий случай это устраняет проблему, когда он добавляется только к первому элементу и очищается, но опять же у меня была та же проблема.

Member 10190163

Кроме того, если вы собираетесь протестировать его, вам нужно не забыть изменить свойство draw mode в списке.

Mike Meinz

См. пересмотренное решение 1. Мне потребовалось две попытки, чтобы получить версию решения, которая работает правильно. Моя первая версия не работала правильно, когда присутствовала вертикальная полоса прокрутки. См. комментарии в решении 1.

Member 10190163

Я использую Windows 7 и Microsoft Visual Basic 2010 Express. Я создал новый проект и попытался применить к нему ваш код. У меня проблема с линией:
e.графика.Шнурок(Список Listbox1.Пользования(по электронной.Индекса).Метод toString(), электронная.Шрифт, arrColors(электронная.Индекс), адрес электронной.Границы, StringFormat.GenericDefault)
Это вызывает исключение, когда нажимается run, и я не могу продолжить работу с программой, потому что она вызывает диалоговое окно, которое позволяет мне тормозить или продолжать, а когда я продолжаю, отладчик останавливается. Сообщения диалогового окна выглядят следующим образом: необработанное исключение типа 'System.Об' произошли в системе.Окна.Forms.dll
Дополнительная информация: InvalidArgument=значение '-1' недопустимо для 'index'.

Mike Meinz

Я изменил ListBox1_DrawItem в решении 1. Я положил трубку If заявление по всему миру e.Graphics.DrawString оператор, чтобы гарантировать, что он выполняется только тогда, когда e.Index > -1.

Пожалуйста, попробуйте это изменение.

Member 10190163

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

Mike Meinz

Я не думаю, что время является причиной вашей проблемы.

Mike Meinz

Если вы публикуете эту новую проблему как новый вопрос, пожалуйста, ответьте на это сообщение с URL-адресом нового вопроса.

Member 10190163

Ваше право, это не связано с хронометражем, проблема в том, что позже в моем коде я ограничиваю количество записей listbox этой строкой: Me.ListBox1.Предметы.Remove(listbox_text(0)) ' снять верхнюю запись списка
и это будет означать, что суб ListBox1_DrawItem будет вызван, но новый элемент к нему не добавляется, так что я думаю, что именно поэтому он испортился.

Mike Meinz

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

Member 10190163

Хорошо спасибо,
Я увеличил массив до 0 до 550, так как это максимальное значение
и теперь я исправил это с помощью
Для счетчика = от 0 до 549
arrColors(counter) = arrColors(counter + 1)
Следующий
просто до меня.Список listbox1.Предметы.Удалить(listbox_text(0))
линия
Но я не пользовался коллекцией.

Mike Meinz

См. пересмотренное решение 1.
Редакция использует коллекцию и предоставляет listbox_remove_0 для тебя.

Mike Meinz

Я изменил пример в решении 1 так, чтобы он включал в себя listbox_remove_0() и использует коллекцию вместо массива для сохранения цветов кисти.

Member 10190163

Ой, извините, я не видел этого ответа раньше. Я еще раз взгляну на решение 1.

Mike Meinz

Вот пример ListView, который делает то же самое. Гораздо проще!


Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
ListView1.View = View.List
End Sub


Private Sub listview_add(ByVal strText As String, ByVal MyColor As Color)
Dim lstRow As New ListViewItem(strText)
lstRow.ForeColor = MyColor
ListView1.Items.Add(lstRow)
End Sub

Private Sub listview_Remove0()
If ListView1.Items.Count < 1 Then Exit Sub
ListView1.Items.RemoveAt(0)
End Sub

Private Sub btnBlackListView_Click(sender As Object, e As EventArgs) Handles btnBlackListView.Click
listview_add("Black", Color.Black)
End Sub

Private Sub btnBlueListView_Click(sender As Object, e As EventArgs) Handles btnBlueListView.Click
listview_add("Blue", Color.Blue)
End Sub

Private Sub btnRemoveListView_Click(sender As Object, e As EventArgs) Handles btnRemoveListView.Click
listview_Remove0()
End Sub

Member 10190163

Я поместил ваше решение с listbox_remove_0 в проект vb, и оно работает, однако у меня все еще возникают проблемы с синхронизацией, когда я внедряю его в свой другой код. Одна из этих проблем была вызвана тем, что я очистил listbox с помощью listbox1.items.clear (), но теперь я исправил это, очистив коллекцию с помощью colColors.Понятно(). Но у меня все еще есть другие проблемы, потому что он все еще выходит из синхронизации.
Кроме того, я пробовал использовать listview, но он не выполняет того, что я пытаюсь сделать с колонками.

Mike Meinz

Вы должны удалять или добавлять строки в список где-то еще в вашей программе.

Я не понимаю вашего комментария о ListView и столбцах. ListView может создавать столбцы, если для свойства View задано значение View.Details. Когда свойство имеет значение вид.Список, элемент управления ListView ведет себя как элемент управления ListBox (т. е. без колонки). Что вы пытаетесь сделать такого, что, по вашему мнению, ListView не может сделать? Вы тратите много времени, пытаясь заставить ListBox работать, когда ListView должен легко справляться с вашими требованиями.

Member 10190163

Хорошо, теперь я исправил это, это было потому, что я делаю проверку, чтобы увидеть, прерывается ли поток, прежде чем добавить или удалить элемент из списка, но я не делал эту проверку перед добавлением или удалением элемента из коллекции. Теперь это работает.
Однако Спасибо за вашу помощь в этом вопросе.

Mike Meinz

Я все еще удивляюсь, почему вы не можете использовать элемент управления ListView со свойством View, установленным в View.List.

Mike Meinz

Я заметил, что вы еще не приняли решение, так что этот вопрос будет помечен как решенный.