Doctor GME Ответов: 1

Управление цветом некоторых элементов combobox с помощью другого выбора combobox


Это моя форма и дизайн базы данных:

https://www5.0zz0.com/2020/04/15/18/890159245.jpg[^]

нижние комбо-боксы зависят друг от друга при загрузке элементов из базы данных
все, что я хочу, это когда я выбираю (доступно) в выпадающем списке с именем (статус), как это:

https://www5.0zz0.com/2020/04/05/13/182870544.jpg[^]

элементы в выпадающем списке (имя), который имеет (Да) в базе данных, соответствующие
чтобы столбец (доступный) изменил цвет на красный.
вот мой код:

Imports System.Data.OleDb
Public Class Form1
    Dim cnn As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Stock.accdb")
    Dim da As OleDbDataAdapter
    Dim cm As OleDbCommandBuilder
    Dim cmd As OleDbCommand
    Dim itemRoute As String()
    Private Sub ComboType_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboType.SelectedIndexChanged

        ComboName.Items.Clear()
        ComboQuantity.Items.Clear()
        ComboStore.Items.Clear()

        If ComboType.SelectedItem = "Food" Then

            Dim dt1 As New DataTable
            dt1.Clear()
            Dim sql As String = "SELECT * FROM Food"
            da = New OleDbDataAdapter(sql, cnn)
            cm = New OleDbCommandBuilder(da)
            da.Fill(dt1)
            For ii As Integer = 0 To dt1.Rows.Count - 1
                ComboName.Items.Add(dt1(ii)(0))
            Next

        End If

        If ComboType.SelectedItem = "Clothes" Then

            Dim dt1 As New DataTable
            dt1.Clear()
            Dim sql As String = "SELECT * FROM Clothes"
            da = New OleDbDataAdapter(sql, cnn)
            cm = New OleDbCommandBuilder(da)
            da.Fill(dt1)
            For ii As Integer = 0 To dt1.Rows.Count - 1
                ComboName.Items.Add(dt1(ii)(0))
            Next

        End If
    End Sub

    Private Sub ComboName_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboName.SelectedIndexChanged

        ComboQuantity.Items.Clear()
        ComboStore.Items.Clear()

        If ComboType.SelectedItem = ("Food") Then

            Dim dt2 As New DataTable
            dt2.Clear()
            Dim sql2 As String = "SELECT * FROM Food WHERE FoodName = '" & ComboName.SelectedItem & "'"
            da = New OleDbDataAdapter(sql2, cnn)
            cm = New OleDbCommandBuilder(da)
            da.Fill(dt2)
            Dim quants As String = dt2(0)(1)
            Dim quant As String() = quants.Split("-")
            For ii As Integer = 0 To quant.Count - 1
                ComboQuantity.Items.Add(quant(ii))
            Next
            Dim stores As String = dt2(0)(2)
            Dim store As String() = stores.Split("-")
            itemRoute = store

        End If

        If ComboType.SelectedItem = ("Clothes") Then

            Dim dt2 As New DataTable
            dt2.Clear()
            Dim sql2 As String = "SELECT * FROM Clothes WHERE ClothesName = '" & ComboName.SelectedItem & "'"
            da = New OleDbDataAdapter(sql2, cnn)
            cm = New OleDbCommandBuilder(da)
            da.Fill(dt2)
            Dim quants As String = dt2(0)(1)
            Dim quant As String() = quants.Split("-")
            For ii As Integer = 0 To quant.Count - 1
                ComboQuantity.Items.Add(quant(ii))
            Next
            Dim stores As String = dt2(0)(2)
            Dim store As String() = stores.Split("-")
            itemRoute = store

        End If

    End Sub

    Private Sub ComboQuantity_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboQuantity.SelectedIndexChanged

        ComboStore.Items.Clear()
        ComboStore.Items.Add(itemRoute(ComboQuantity.SelectedIndex))

    End Sub
End Class


Что я уже пробовал:

попробовал с событием drawitem второго combobox (Comboname)
но не смог изменить привязку базы данных к нему

Private Sub ComboName_DrawItem(sender As Object, e As DrawItemEventArgs) Handles ComboName.DrawItem

    If e.Index = -1 Then Return

    e.DrawBackground()

    Dim item = DirectCast(ComboName.Items(e.Index), DataRowView)
    'Anothe Failed Line:
    'Dim item As DataRowView = CType(ComboName.Items(e.Index), DataRowView)

    Dim text = ComboName.GetItemText(item)
    Dim available = CBool(item("Available"))

    Using brush As New SolidBrush(If(available, Color.Red, e.ForeColor))
        e.Graphics.DrawString(text, e.Font, brush, e.Bounds)
    End Using

End Sub

Richard Deeming

Dim sql2 As String = "SELECT * FROM Food WHERE FoodName = '" & ComboName.SelectedItem & "'"

Dim sql2 As String = "SELECT * FROM Clothes WHERE ClothesName = '" & ComboName.SelectedItem & "'"

Не делай этого так!

Ваш код уязвим для SQL-инъекция[^]. НИКОГДА используйте конкатенацию строк для построения SQL-запроса. ВСЕГДА используйте параметризованный запрос.

Все, что вы хотели знать о SQL-инъекции (но боялись спросить) | Трой Хант[^]
Как я могу объяснить SQL-инъекцию без технического жаргона? | Обмен Стеками Информационной Безопасности[^]
Шпаргалка по параметризации запросов | OWASP[^]

Dim dt2 As New DataTable
Using cnn As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Stock.accdb")
    Const sql2 As String = "SELECT * FROM Food WHERE FoodName = @Name"
    Dim da As New OleDbDataAdapter(sql2, cnn)
    da.SelectCommand.Parameters.AddWithValue("@Name", ComboName.SelectedItem)
    da.Fill(dt2)
End Using

Кроме того, не храните соединения, команды или адаптеры данных в виде полей. Создайте их в точке, где они необходимы, и оберните соединения и команды в Using блоки.

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

Doctor GME

спасибо за ценные замечания
я буду держать это в моей голове

1 Ответов

Рейтинг:
1

Maciej Los

В соответствии с комментарием Ричарда...

Добавить новое Module к вашему проекту, а затем вставьте ниже функцию:

Public Function GetData(sCommand As String, oParams As OleDbParameter()) As DataTable 
	Dim dt As DataTable = New DataTable()
	Using cnn As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Stock.accdb")
		cnn.Open()
		Using cmd As New OleDbCommand(sCommand, cnn)
			For Each odp As OleDbParameter In oParams
				cmd.Parameters.Add(odp)
			Next
			Using rdr As OleDbDataReader = cmd.ExecuteReader()
				dt.Load(rdr)
			End Using
		End Using
		cnn.Close()
	End Using

	Return dt
	
End Function


Как вы видите, эта функция отвечает за захват данных.

Затем удалите эти строки из модуля Form1:
Dim cnn As New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Stock.accdb")
Dim da As OleDbDataAdapter
Dim cm As OleDbCommandBuilder
Dim cmd As OleDbCommand


Затем улучшите свой код таким образом:
Private Sub ComboType_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboType.SelectedIndexChanged
    ComboName.Items.Clear()
    ComboQuantity.Items.Clear()
    ComboStore.Items.Clear()
	Dim sTable As String = ComboType.SelectedItem.ToString()
	Dim sql As String = String.Format("SELECT * FROM {0};", sTable)
	Dim dt1 As DataTable = GetData(sql, New OleDbPArameter(){})
    For ii As Integer = 0 To dt1.Rows.Count - 1
        Dim dr As DataRow = dt1.Rows(ii)
        ComboName.Items.Add(dr(0))
    Next
End Sub

Таким же образом улучшайте другие подпрограммы...
Обратите внимание, что ниже строка:
Dim sql2 As String = "SELECT * FROM Clothes WHERE ClothesName = '" & ComboName.SelectedItem & "'"

может быть изменен на:
Dim sTable As String = ComboType.SelectedItem.ToString()
Dim sql As String = String.Format("SELECT * FROM {0} WHERE {0}Name=?;", sTable)
'returns string: SELECT * FROM Clothes WHERE ClothesName = ?;
'Note that the name of table, from which data are grabbed, is used twice ;)


Итак, вы должны получить:
Private Sub ComboName_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboName.SelectedIndexChanged
    ComboQuantity.Items.Clear()
    ComboStore.Items.Clear()

	Dim sTable As String = ComboType.SelectedItem.ToString()
	Dim sql As String = String.Format("SELECT * FROM {0} WHERE {0}Name=?;", sTable)
	'returns string: SELECT * FROM Clothes WHERE ClothesName = ?;
	'Note that the name of table, from which data are grabbed, is used twice ;)
	Dim oParams As OleDbParameter() = 
	    {
	       New OleDbParameter("?", OleDbType.VarChar) With {.Value=ComboName.SelectedItem.ToString()}
	    }
	'finally:
	Dim dt2 As DataTable = GetData(sql, oParams)
    Dim quants As String = dt2(0)(1)
    Dim quant As String() = quants.Split("-")
    For ii As Integer = 0 To quant.Count - 1
		ComboQuantity.Items.Add(quant(ii))
    Next
    Dim stores As String = dt2(0)(2)
    Dim store As String() = stores.Split("-")
    itemRoute = store

End Sub


Последняя часть состоит в том, чтобы изменить цвет ComboName предметы. Я бы предложил создать пользовательский combobox, который наследуется от combobox. Видеть: Как изменить цвет фона элементов combobox во время выполнения?[^]
Затем вы сможете изменить цвет шрифта и фона для элементов.
Если вы не хотите использовать пользовательский combox, используйте решение metnioned в этом потоке: c# - цвет отдельных элементов в выпадающем списке winforms? - переполнение стека[^]


Doctor GME

после добавления Вашей функции и изменения кода
я попробовал использовать событие drawitem с этим кодом:

Private Sub Comboname_DrawItem(sender As Object, e As DrawItemEventArgs) обрабатывает ComboName.DrawItem
Если e.Index = -1, то верните
е.Метод drawbackground()

Dim item = DirectCast(ComboName.Элементы(например, индекс), DataRowView)
Тусклый текст = ComboName.GetItemText(item)
Dim available = CBool(item("Available"))

Использование кисти в качестве новой сплошной кисти(Если(доступно, цвет.Красный, e.ForeColor))
e.графика.Шнурок(текст, электронная.Шрифты, кисти, Эл.Границы)
Конец Использования
Конец Подводной Лодки

но я понял ошибку:
Система.InvalidCastException: "невозможно привести объект типа" система.Строка' для ввода 'System.Data.DataRowView'.'

Maciej Los

2 возможные ошибки:
1.

Dim item = DirectCast(ComboName.Items(e.Index), DataRowView)

ComboName.Items(e.Index) это тип объекта, а не a DataRowView
Таким образом, вы не можете эксплицитно преобразовать его в DataRowView!

Я думаю, что этой строки должно быть достаточно:
Dim text = ComboName.Items(e.Index).ToString()


2.
Dim available = CBool(item("Available"))

Вы не можете преобразовать Yes/No в true/false, если только база данных access не отображает yes/no в виде логического значения.

Doctor GME

я изменил текст в базе данных на: True\false
но без строки (элемента) available не может быть преобразован в boolean
что необходимо для самой важной линии:
Использование кисти в качестве новой сплошной кисти(Если(доступно, цвет.Красный, e.ForeColor))
любые идеи!!!!

Maciej Los

Dim available As Boolean = False
If itemtext = "Yes" Then available = true

Обратите внимание, что itemtext это переменная, которая содержит текст из элемента ;)

Doctor GME

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

Тусклый текст = ComboName.Пользования(по электронной.Индекса).Метод toString()
Dim доступен как Boolean = False
Если itemtext = "Yes", то available = True

itemtext не определен и как вы сказали Он содержит текст из item
которую я не могу определить как эту линию:

Dim item = DirectCast(ComboName.Элементы(например, индекс), DataRowView)

из-за проблемы конверсии
Итак, как я могу определить, что itemtext-это текст элемента в
столбец (доступный), который противоположен тому же значению в столбце
(Имя еды)!!!