Subclass/Superclass - If a subclass is cast as its superclass, is there a way to use the overloaded properties of the subclass? - vb.net

Sorry if the title isn't very clear. This is a VB.NET (2010) question
I have a superclass called "Device" which has a number of subclasses that inherit it. Some of those subclasses also have subclasses. In particular, I have a class called "TwinCatIntegerDevice" which inherits "TwinCatDevice" which inherits "Device."
The relevant parts of Device look like this:
Public Class Device
Private _Setpoints As New List(Of Double)
Public Overridable Property Setpoints As List(Of Double)
Get
Return _Setpoints
End Get
Set(ByVal value As List(Of Double))
_Setpoints = value
_SetpointsTb.Clear()
For Each setpoint In value
Dim setpointTb As New TextBox
setpointTb.Text = setpoint.ToString
_SetpointsTb.Add(setpointTb)
Next
End Set
End Property
Private _SetpointsTb As New List(Of TextBox)
Public Overridable Property SetpointsTb As List(Of TextBox)
Get
Return _SetpointsTb
End Get
Set(ByVal value As List(Of TextBox))
_SetpointsTb = value
Me._Setpoints.Clear()
For Each setpoint In value
Me._Setpoints.Add(setpoint.Text)
Next
End Set
End Property
End Class
The TwinCatDevice class does not overload Setpoints or SetpointsTb.
The TwinCatIntegerDevice class does:
Public Class TwinCatIntegerDevice
Inherits TwinCatDevice
Private _Setpoints As New List(Of Integer)
Public Overloads Property Setpoints As List(Of Integer)
Get
Return _Setpoints
End Get
Set(ByVal value As List(Of Integer))
_Setpoints = value
_SetpointsTb.Clear()
For Each setpoint In value
Dim setpointTb As New TextBox
setpointTb.Text = setpoint.ToString
_SetpointsTb.Add(setpointTb)
Next
End Set
End Property
Private _SetpointsTb As New List(Of TextBox)
Public Overloads Property SetpointsTb As List(Of TextBox)
Get
Return _SetpointsTb
End Get
Set(ByVal value As List(Of TextBox))
_SetpointsTb = value
Me._Setpoints.Clear()
For Each setpoint In value
Me._Setpoints.Add(setpoint.Text)
Next
End Set
End Property
End Class
Now, the problem. I try to set the setpoints using a subroutine like so:
Private Sub FetchDeviceRecipe(ByRef device As Device, ByRef excelSheet As ExcelWorksheet, ByVal row As Integer)
Dim lastCol As Integer = NumberOfProcessSteps + 1
Try
For col = 2 To lastCol
Dim setpoint As New TextBox
setpoint.Text = excelSheet.Cells(row, col).Value
device.SetpointsTb.Add(setpoint)
Next
device.SetpointsTb = device.SetpointsTb
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
(I know that is terrible code :X, I'm a beginner)
Crucially, I'm passing the device as the Device superclass (so that I don't have to have a separate subroutine for each sub-type).
When I do this for a TwinCatIntegerDevice called "ThisDevice" after it has been passed to the subroutine:
MsgBox("As Device: " & CType(ThisDevice, Device).Setpoints.Count.ToString & vbNewLine & _
"As TwinCatDevice: " & CType(ThisDevice, TwinCatDevice).Setpoints.Count.ToString & vbNewLine & _
"As TwinCatIntegerDevice: " & CType(ThisDevice, TwinCatIntegerDevice).Setpoints.Count.ToString)
I get the following (9 is the correct number of setpoints in this case):
As Device: 9
As TwinCatDevice: 9
As TwinCatIntegerDevice: 0
Does anyone know why the TwinCatInteger device class apparently has a different variable for Setpoints when it is cast as its superclass Device?
I'm sorry if this seems a bit incoherent. Any help would be great! Even regarding form or anything else. I'm still trying to figure this whole VB.NET thing out.

I think what you want to do, is remove your "OverLoads" keyword on the "TwinCatIntegerDevice" class, and replace it with the keyword "Shadows".

Related

how to handle properties with this FxCopAnalyzer warning: "CA2227: Set "Images" as read-only by removing the setter for the property"

What is this programme about:
This program was developed with the VB.Net language, the .NET Framework 4.8 and Visual Studio 2019 CE. The idea of this program is to run a rudimentary database. The list is similar to a classic Internet forum—there are threads, there are different numbers of postings in the threads and in each post there are different numbers of pictures and long texts. If the thread is selected using the ComboBox, all posts with their images and texts are displayed one below the other. When you click on a specific post, only its images are displayed.
The user can create threads and posts. For reasons of legibility, only extended Latin letters are allowed when writing the text (e.g. René, Chloë).
When you close the program, you will be asked whether you want to save the data. These data are read in when the program is loaded. If images are not found, their paths will be displayed in a window.
The user also has the option of searching through all threads and viewing the results with various sorting options. In this case, only the posts found are listed in the ListBox, and here, too, the user can select the posts individually.
The program also reads in user data when it starts. A user can log in and, depending on his role, has certain power to make decisions. A “normal” user can create threads and posts, but only an administrator or moderator can edit and delete texts. If you are not logged in, you can only read threads and posts.
What I would like to know from you
I use the FxCopAnalyzer, which is currently giving me 8 warnings. I am aware that it sometimes exaggerates a little or criticizes certain things that were done on purpose. But I'd like to let you guys have a look to make it better. What exactly is this supposed to mean: "CA2227: Set "Images" (Bilder) as read-only by removing the setter for the property". Because when I remove the setter, I get an error message.
I feel the same way with other properties in other classes. So I need to know, how to fix this, and I need a general solution. I appreciate.
Class structure
There is the class Forum, the class Class_Thread and the class Class_Post. Class_Thread inherits from forum. In the Class_thread there is a list(of Class_Post) which contains instances of Class_Post. ("The thread knows what posts it has"). In FormMain, a List(of Class_Thread) is created, in which the instances of Class_Thread are.
In Class_Post's constructor, the following parameters are transferred: 1) the heading of the posting, 2) the text, 3) the number, 4) images as List(Of System.Drawing.Bitmap), 5) the date, 6) the image paths As List(Of String).
Class_Post.vb
#Disable Warning CA1707 ' Bezeichner dürfen keine Unterstriche enthalten
Public Class Class_Post : Inherits Class_Forum
Private _ueberschrift As String = ""
Private _text_dazu As String = ""
Private _nummer As UInt16
Private _bilder As List(Of System.Drawing.Bitmap)
Private _erstelldatum_dieses_Posts As Date
Private _pfade_der_Bilder As List(Of String)
Public Property Ueberschrift As String
Get
Return _ueberschrift
End Get
Set(value As String)
_ueberschrift = value
End Set
End Property
Public Property Text_dazu As String
Get
Return _text_dazu
End Get
Set(value As String)
_text_dazu = value
End Set
End Property
Public Property Nummer As UShort
Get
Return _nummer
End Get
Set(value As UShort)
_nummer = value
End Set
End Property
Public Property Bilder As List(Of Bitmap)
Get
Return _bilder
End Get
Set(value As List(Of Bitmap))
_bilder = value
End Set
End Property
Public Property Erstelldatum_dieses_Posts As Date
Get
Return _erstelldatum_dieses_Posts
End Get
Set(value As Date)
_erstelldatum_dieses_Posts = value
End Set
End Property
Public Property Pfade_der_Bilder As List(Of String)
Get
Return _pfade_der_Bilder
End Get
Set(value As List(Of String))
_pfade_der_Bilder = value
End Set
End Property
Public Sub New(ByVal Ueberschrift As String, ByVal Text As String, ByVal nr As UInt16, ByVal uebergebene_Bilder As List(Of System.Drawing.Bitmap), ByVal _date As Date, ByVal Pfade As List(Of String))
Me.Ueberschrift = Ueberschrift
Me.Text_dazu = Text
Me.Nummer = nr
Me.Bilder = uebergebene_Bilder
Me.Erstelldatum_dieses_Posts = _date
Me.Pfade_der_Bilder = Pfade
End Sub
End Class
#Enable Warning CA1707 ' Bezeichner dürfen keine Unterstriche enthalten
This is in FormMain, where a new Instance of Class_Post is created This Sub is using a second Form from which the data is taken.
Private Sub Button_neueAntwort_Click(sender As Object, e As EventArgs) Handles Button_neueAntwort.Click
' doppelt abgesichert, falls die Variable SI aus irgendeinem Grunde nicht (-1) ist, obwohl sie das sollte.
If SI <> (-1) OrElse ComboBox1.SelectedItem IsNot Nothing Then
Using FA As New Form_Antwort
Dim DR As DialogResult = FA.ShowDialog(Me)
If DR = DialogResult.Yes Then
Dim neuerPost As New Class_Post(FA.Titel_des_Posts, FA.Beschreibung, CUShort(Liste_mit_allen_Threads(SI).Posts_in_diesem_Thread.Count + 1), FA.NeueListeBitmaps, Date.Now, FA.Liste_mit_Pfaden)
Liste_mit_allen_Threads(SI).Posts_in_diesem_Thread.Add(neuerPost)
alle_Posts_in_diesem_Thread_anzeigen()
End If
End Using
Else
MessageBox.Show("Bitte zuerst einen Thread auswählen.", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information)
End If
End Sub
Thanks to Jimi and Craig, I can come up with a solution here. The thing is, unfortunately I let the FxCopAnalyzer influence me a bit. It had suggested using getters and setters instead of the normal fields, and that's why the warning I reported about came up. But the solution looks very simple, just use properties:
Public Class Class_Post : Inherits Class_Forum
Public ReadOnly Property Ueberschrift As String = ""
Public ReadOnly Property Text_dazu As String = ""
Public ReadOnly Property Nummer As UInt16
Public ReadOnly Property Bilder As List(Of System.Drawing.Bitmap)
Public ReadOnly Property Erstelldatum_dieses_Posts As Date
Public ReadOnly Property Pfade_der_Bilder As List(Of String)
Public Sub New(ByVal Ueberschrift As String, ByVal Text As String, ByVal nr As UInt16, ByVal uebergebene_Bilder As List(Of System.Drawing.Bitmap), ByVal _date As Date, ByVal Pfade As List(Of String))
Me.Ueberschrift = Ueberschrift
Me.Text_dazu = Text
Me.Nummer = nr 'kann maximal 65535 werden
Me.Bilder = uebergebene_Bilder
Me.Erstelldatum_dieses_Posts = _date
Me.Pfade_der_Bilder = Pfade
End Sub
End Class
Edit:
Of course, this is only true until you decide to create a button where a moderator wants to delete an image. Then, ReadOnly has to be removed again and the FxCopAnalyzer complains again.

How can I edit null values in a BindingList when a DataGridView is bound to it?

I am still very new to data binding in Windows Forms with Visual Basic .NET, but trying to get familiar with it. I tried looking for information on this already, but to no avail.
I want to set up two-way binding between a DataGridView control and a list of objects (let's say they are of a made-up type called MyListElementClass), in a manner similar to what I saw in this answer to another question. Below is my implementation for MyListElementClass, in a file called MyListElementClass.vb:
Imports System.ComponentModel
Imports System.Runtime.CompilerServices
<Serializable>
Public NotInheritable Class MyListElementClass
Implements INotifyPropertyChanged
Implements IMyListElementClass
#Region "Fields"
Private _a As UShort
Private _b As Double
Private _c, _d, _e As Boolean
' End fields region.
#End Region
#Region "INotifyPropertyChanged implementation"
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(<CallerMemberName()> Optional ByVal propertyName As String = Nothing)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
' End INotifyPropertyChanged implementation region.
#End Region
#Region "IMyListElementClass implementation"
Public Property PropertyA As UShort Implements IMyListElementClass.PropertyA
Get
Return _a
End Get
Set(value As UShort)
If _a <> value Then
_a = value
NotifyPropertyChanged()
End If
End Set
End Property
Public Property PropertyB As Double Implements IMyListElementClass.PropertyB
Get
Return _b
End Get
Set(value As Double)
If _b <> value Then
_b = value
NotifyPropertyChanged()
End If
End Set
End Property
Public Property PropertyC As Boolean Implements IMyListElementClass.PropertyC
Get
Return _c
End Get
Set(value As Boolean)
If _c <> value Then
_c = value
NotifyPropertyChanged()
End If
End Set
End Property
Public Property PropertyD As Boolean Implements IMyListElementClass.PropertyD
Get
Return _d
End Get
Set(value As Boolean)
If _d <> value Then
_d = value
NotifyPropertyChanged()
End If
End Set
End Property
Public Property PropertyE As Boolean Implements IMyListElementClass.PropertyE
Get
Return _e
End Get
Set(value As Boolean)
If _e <> value Then
_e = value
NotifyPropertyChanged()
End If
End Set
End Property
' End IMyListElementClass implementation region.
#End Region
#Region "Constructors"
Public Sub New()
PropertyA = 0
PropertyB = 0
PropertyC = False
PropertyD = False
PropertyE = False
End Sub
Public Sub New(a As UShort, b As Double, c As Boolean, d As Boolean, e As Boolean)
Me.PropertyA = a
Me.PropertyB = b
Me.PropertyC = c
Me.PropertyD = d
Me.PropertyE = e
End Sub
Public Sub New(other As IMyListElementClass)
If other Is Nothing Then Throw New ArgumentNullException(NameOf(other))
CopyFrom(other)
End Sub
' End constructors region.
#End Region
#Region "Methods"
Public Sub CopyFrom(other As IMyListElementClass)
If other Is Nothing Then Throw New ArgumentNullException(NameOf(other))
With other
PropertyA = .PropertyA
PropertyB = .PropertyB
PropertyC = .PropertyC
PropertyD = .PropertyD
PropertyE = .PropertyE
End With
End Sub
' End methods region.
#End Region
End Class
The idea here is that the DataGridView control will show a list of available "slots" (rows) that instances of MyListElementClass can be entered into. However, some of these slots could be empty, and may need to be filled in or cleared later. The number of rows in the table is specified by a number entered elsewhere, so the user cannot add or remove rows on the fly; They have to work with the space that's given.
My current attempt at this is to have the DataGridView control bound to a BindingList(Of MyListElementClass), where its size is always equal to the number of available slots and empty slots are represented by null elements. However, I found that if I have these null values present in the BindingList(Of MyListElementClass), these rows cannot be edited by the user in the DataGridView control which is bound to it, and I'm not really sure how to handle this.
An example of what I'm trying to do in my user control which contains the DataGridView (named dgvDataGridView here and with columns already set up through the designer):
Public Class MyUserControl
Private _myBindingList As BindingList(Of MyListElementClass)
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
dgvDataGridView.AutoGenerateColumns = False ' Columns already created through the Visual Studio designer with the ordering and header text I want.
SetUpTableDataBinding()
End Sub
Private Sub SetUpTableDataBinding()
colA.DataPropertyName = NameOf(MyListElementClass.PropertyA)
colB.DataPropertyName = NameOf(MyListElementClass.PropertyB)
colC.DataPropertyName = NameOf(MyListElementClass.PropertyC)
colD.DataPropertyName = NameOf(MyListElementClass.PropertyD)
colE.DataPropertyName = NameOf(MyListElementClass.PropertyE)
Dim initialList As New List(Of MyListElementClass)(Enumerable.Repeat(Of MyListElementClass)(Nothing, 1)) ' First row will contain a null value, and hence be "empty".
_myBindingList = New BindingList(Of MyListElementClass)(initialList)
Dim source = New BindingSource(_myBindingList, Nothing)
dgvDataGridView.DataSource = source
' Some test data for data binding.
_myBindingList.AddNew() ' Adds a new MyListElementClass instance with default property values.
_myBindingList.Add(New MyListElementClass(2345, 7.4, False, True, False)) ' Just some sample values.
End Sub
End Class
After this user control loads, I can see an empty row, a row with default values for the MyListElementClass, and a row with some sample values appear, for three rows total. I can edit the second and third rows, but not the first (any values I enter immediately vanish).
Again, in completely unfamiliar territory here, so bear with me. If I cannot get this to work, then I will abandon this idea and return to manually setting and retrieving data in the DataGridView cells like I've always done up until now.
Null values cannot be edited, replace them with empty strings instead and I think you will find that it will work as intended.

Extracting property values from a dictionary

I am attempting to write a subroutine that will deserialize a dictionary from a .ser file (this bit works fine) and then repopulate several lists from this dictionary (this is the bit I cannot do).
The dictionary contains objects (I think) of a custom class I wrote called "Photo Job" which has properties such as ETA, notes, medium etc. (Declared as such)
Dim photoJobs As New Dictionary(Of String, PhotoJob)
In short, I want to be able to extract every entry of each specific property into an separate arrays (one for each property) and I can go from there.
Any help would be appreciated, I may be going about this completely the wrong way, I'm new to VB. The relevant code is below:
Photo Job Class:
<Serializable()> _Public Class PhotoJob
Private intStage As Integer 'Declare all local private variables
Private ID As String
Private timeLeft As Integer
Private material As String '
Private note As String
Private path As String
Private finished As Boolean = False
'Declare and define properties and methods of the class
Public Property productionStage() As Integer
Get
Return intStage
End Get
Set(ByVal Value As Integer)
intStage = Value
End Set
End Property
Public Property photoID() As String
Get
Return ID
End Get
Set(ByVal Value As String)
ID = Value
End Set
End Property
Public Property ETA() As Integer
Get
Return timeLeft
End Get
Set(ByVal Value As Integer)
timeLeft = Value
End Set
End Property
Public Property medium() As String
Get
Return material
End Get
Set(ByVal Value As String)
material = Value
End Set
End Property
Public Property notes() As String
Get
Return note
End Get
Set(ByVal Value As String)
note = Value
End Set
End Property
Public Property imagePath() As String
Get
Return path
End Get
Set(ByVal Value As String)
path = Value
End Set
End Property
Public Property complete() As Boolean
Get
Return finished
End Get
Set(value As Boolean)
finished = value
End Set
End Property
Public Sub nextStage()
If intStage < 4 Then
intStage += 1
ElseIf intStage = 4 Then
intStage += 1
finished = True
End If
End Sub
End Class
Subroutines involved in de/serialisation:
Private Sub BackupAllToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles BackupAllToolStripMenuItem.Click
Dim formatter As New BinaryFormatter
Dim backupFile As New FileStream(Strings.Replace(Strings.Replace(Now, ":", "_"), "/", ".") & ".ser", FileMode.Create, FileAccess.Write, FileShare.None)
formatter.Serialize(backupFile, photoJobs)
backupFile.Close()
MsgBox("Collection saved to file")
End Sub
Private Sub RestoreFromFileToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles RestoreFromFileToolStripMenuItem.Click
With OpenFileDialog 'Executes the following sets/gets/methods of the OpenFileDialog
.FileName = ""
.Title = "Open Image File"
.InitialDirectory = "c:\"
.Filter = "Serial Files(*.ser)|*ser"
.ShowDialog()
End With
Dim backupPathStr As String = OpenFileDialog.FileName
Dim deSerializer As New BinaryFormatter
Dim backupFile As New FileStream(backupPathStr, FileMode.Open)
photoJobs = deSerializer.Deserialize(backupFile)
backupFile.Close()
End Sub
From what I can see using the autos menu, the saving/restoring of the dictionary works just fine.
First, if you are using VS2010+, you can greatly reduce boilerplate code using autoimplemented properties:
<Serializable()>
Public Class PhotoJob
Public Property productionStage() As Integer
Public Property photoID() As String
Public Property ETA() As Integer
etc
End Class
That is all that is needed, all the boilerplate code is handled for you. Second, with this line:
photoJobs = deSerializer.Deserialize(backupFile)
Your deserialized photojobs will be a generic Object, not a Dictionary. You should turn on Option Strict so VS will enforce these kinds of errors. This is how to deserialize to Type:
Using fs As New FileStream(myFileName, FileMode.Open)
Dim bf As New BinaryFormatter
PhotoJobs= CType(bf.Deserialize(fs), Dictionary(Of String, PhotoJob))
End Using
Using closes and disposes of the stream, CType converts the Object returned by BF to an actual dictionary
To work with the Dictionary (this has nothing to do with Serialization) you need to iterate the collection to get at the data:
For Each kvp As KeyValuePair(Of String, PhotoJob) In PhotoJobs
listbox1.items.Add(kvp.value.productionStage)
listbox2.items.Add(kvp.value.ETA)
etc
Next
The collection is a made of (String, PhotoJob) pairs as in your declaration, and when you add them to the collection. They comeback the same way. kvp.Key will be the string key used to identify this job in the Dictionary, kvp.Value will be a reference to a PhotoJobs object.
As long as VS/VB knows it is a Dictionary(of String, PhotoJob), kvp.Value will act like an instance of PhotoJob (which it is).

how to pick value and name for item in listbox in vb.net

i want to pick payrollcode when listbox is selected though list should appear as payrollname.Once I have the paycode i can then use it in another query.I have a php background so this is alittle tricky for me.
Dim cmd As New SqlCommand("select tblPayrollCode.payrollcode_name ,tblPayrollCode.payrollcode_code from tblPayrollCode where tblPayrollCode.systemcode_id=0 and tblPayrollCode.deduction= 'false'", Getconnect)
Dim dr As SqlDataReader
Getconnect()
dr = cmd.ExecuteReader
While dr.Read
lsttrans.Items.Add(dr.Item("payrollcode_name"), payrollcode_code)
End While
dr.Close()
Create a class to create objects that keep each display item/data pair. The class needs to specify an overriden ToString method. This method overrides the ToString method of the base class (Object) to ensure that your display item and not the class name is displayed in the list box.
Public Class CListItem
Private m_sItemData As String
Private m_sItemDisplay As String
Public Sub New(ByVal sValue As String, ByVal sData As String)
m_sItemData = sData
m_sItemDisplay = sValue
End Sub
Public Overrides Function ToString() As String
Return m_sItemDisplay
End Function
Public Property ItemData() As String
Get
Return m_sItemData
End Get
Set(ByVal Value As String)
m_sItemData = Value
End Set
End Property
Public Property ItemDisplay() As String
Get
Return m_sItemDisplay
End Get
Set(ByVal Value As String)
m_sItemDisplay = Value
End Set
End Property
End Class
Execute the loop like this. This adds a CListItem object to the list box's items collection and displays the first parameter that is passed to the constructor.
While dr.Read
lsttrans.Items.Add(New CListItem(dr.Item("payrollcode_name").ToString, dr.Item("payrollcode_code").ToString))
End While
You can then retrieve the payrollcode_name and payrollcode_code by adding this code and double clicking on the list box:
Private Sub lsttrans_DoubleClick(sender As Object, e As EventArgs) Handles lsttrans.DoubleClick
Dim sSelectedDisplay As String = DirectCast(lsttrans.SelectedItem, CListItem).ItemDisplay
Dim sSelectedData As String = DirectCast(lsttrans.SelectedItem, CListItem).ItemData()
MessageBox.Show("The selected payrollcode_name is " & sSelectedDisplay & " and the selected payrollcode_code is " & sSelectedData)
End Sub
Adding onto #Guru Josh's answer
While dr.Read
lsttrans.Items.Add(New CListItem(dr.Item("payrollcode_name"), dr.Item("payrollcode_code")))
End While
I changed the last bit to the above works fine now.
string col1Value = dr["ColumnOneName"].ToString()

how to add value to combobox item

How can I add data value of each item to combobox in Visual Basic 2010?
Like html drop-down box.
Or is there anyway to add values to each item ?
I am adding item from MySQL database like this:
Command = New MySqlCommand("SELECT * FROM `maillist` WHERE l_id = '" & id & "'", connection)
Command.CommandTimeout = 30
Reader = Command.ExecuteReader()
If Reader.HasRows = True Then
While Reader.Read()
ComboBox1.Items.Add(Reader("name"))
End While
End If
I need to add Reader("ID") as value of each item...
Although this question is 5 years old I have come across a nice solution.
Use the 'DictionaryEntry' object to pair keys and values.
Set the 'DisplayMember' and 'ValueMember' properties to:
Me.myComboBox.DisplayMember = "Key"
Me.myComboBox.ValueMember = "Value"
To add items to the ComboBox:
Me.myComboBox.Items.Add(New DictionaryEntry("Text to be displayed", 1))
To retreive items like this:
MsgBox(Me.myComboBox.SelectedItem.Key & " " & Me.myComboBox.SelectedItem.Value)
I am assuming that you are wanting to add items to a ComboBox on an Windows form. Although Klaus is on the right track I believe that the ListItem class is a member of the System.Web.UI.WebControls namespace. So you shouldn't be using it in a Windows forms solution. You can, however, create your own class that you can use in its place.
Create a simple class called MyListItem (or whatever name you choose) like this:
Public Class MyListItem
Private mText As String
Private mValue As String
Public Sub New(ByVal pText As String, ByVal pValue As String)
mText = pText
mValue = pValue
End Sub
Public ReadOnly Property Text() As String
Get
Return mText
End Get
End Property
Public ReadOnly Property Value() As String
Get
Return mValue
End Get
End Property
Public Overrides Function ToString() As String
Return mText
End Function
End Class
Now when you want to add the items to your ComboBox you can do it like this:
myComboBox.Items.Add(New MyListItem("Text to be displayed", "value of the item"))
Now when you want to retrieve the value of the selected item from your ComboBox you can do it like this:
Dim oItem As MyListItem = CType(myComboBox.SelectedItem, MyListItem)
MessageBox.Show("The Value of the Item selected is: " & oItem.Value)
One of the keys here is overriding the ToString method in the class. This is where the ComboBox gets the text that is displayed.
Matt made an excellent point, in his comment below, about using Generics to make this even more flexible. So I wondered what that would look like.
Here's the new and improved GenericListItem class:
Public Class GenericListItem(Of T)
Private mText As String
Private mValue As T
Public Sub New(ByVal pText As String, ByVal pValue As T)
mText = pText
mValue = pValue
End Sub
Public ReadOnly Property Text() As String
Get
Return mText
End Get
End Property
Public ReadOnly Property Value() As T
Get
Return mValue
End Get
End Property
Public Overrides Function ToString() As String
Return mText
End Function
End Class
And here is how you would now add Generic items to your ComboBox. In this case an Integer:
Me.myComboBox.Items.Add(New GenericListItem(Of Integer)("Text to be displayed", 1))
And now the retrieval of the item:
Dim oItem As GenericListItem(Of Integer) = CType(Me.myComboBox.SelectedItem, GenericListItem(Of Integer))
MessageBox.Show("The value of the Item selected is: " & oItem.Value.ToString())
Keep in mind that the type Integer can be any type of object or value type. If you want it to be an object from one of your own custom classes that's fine. Basically anything goes with this approach.
If you want to use SelectedValue then your combobox must be databound.
To set up the combobox:
ComboBox1.DataSource = GetMailItems()
ComboBox1.DisplayMember = "Name"
ComboBox1.ValueMember = "ID"
To get the data:
Function GetMailItems() As List(Of MailItem)
Dim mailItems = New List(Of MailItem)
Command = New MySqlCommand("SELECT * FROM `maillist` WHERE l_id = '" & id & "'", connection)
Command.CommandTimeout = 30
Reader = Command.ExecuteReader()
If Reader.HasRows = True Then
While Reader.Read()
mailItems.Add(New MailItem(Reader("ID"), Reader("name")))
End While
End If
Return mailItems
End Function
Public Class MailItem
Public Sub New(ByVal id As Integer, ByVal name As String)
mID = id
mName = name
End Sub
Private mID As Integer
Public Property ID() As Integer
Get
Return mID
End Get
Set(ByVal value As Integer)
mID = value
End Set
End Property
Private mName As String
Public Property Name() As String
Get
Return mName
End Get
Set(ByVal value As String)
mName = value
End Set
End Property
End Class
Instead of adding Reader("Name") you add a new ListItem. ListItem has a Text and a Value property that you can set.
Yeah, for most cases, you don't need to create a class with getters and setters. Just create a new Dictionary and bind it to the data source. Here's an example in VB using a for loop to set the DisplayMember and ValueMember of a combo box from a list:
Dim comboSource As New Dictionary(Of String, String)()
cboMenu.Items.Clear()
For I = 0 To SomeList.GetUpperBound(0)
comboSource.Add(SomeList(I).Prop1, SomeList(I).Prop2)
Next I
cboMenu.DataSource = New BindingSource(comboSource, Nothing)
cboMenu.DisplayMember = "Value"
cboMenu.ValueMember = "Key"
Then you can set up a data grid view's rows according to the value or whatever you need by calling a method on click:
Private Sub cboMenu_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cboMenu.SelectionChangeCommitted
SetListGrid(cboManufMenu.SelectedValue)
End Sub
Now you can use insert method instead add
' Visual Basic
CheckedListBox1.Items.Insert(0, "Copenhagen")