I'm using VB.NET and Visual Studio 2010
I've got a windows form with a combobox.
I fill the combobox using the following
Dim objSizes As List(Of ASME_Hub_Sizes) = ASME_Hub_Sizes.GetAllHubSizes()
If Not objSizes Is Nothing Then
With Me.cboSize
.DisplayMember = "Size"
.ValueMember = "ID"
.DataSource = objSizes
End With
End If
This works fine, but i would like to add a "Select Size..." option but i'm unsure how to do this.
It seems so much easier to do this in asp.net, but this has me baffled
Thanks
Mick
You could try adding a custom objSize object to the objSizes collection that has ID = 0 and value = "Select size..." as it's ID 0 it should be at the top (I think) and won't clash with any values in your database, upon saving the record you can validate the combobox to avoid writing the "Select size..." object to your database. I'll have a bit of a code and see if this will work...
Ok, I've had another look. You can do as I suggested but you must sort the list before passing it to the combobox. Here is my example:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
With Me.ComboBox1
.DisplayMember = "Description"
.ValueMember = "ID"
.DataSource = GetListOfObjects.returnListOFObjects
End With
End Sub
End Class
Public Class dummyObjectForCombo
Public Property ID As Integer
Public Property Description As String
Public Sub New(ByVal id As Integer,
ByVal description As String)
_ID = id
_Description = description
End Sub
End Class
Public Class GetListOfObjects
Public Shared Function returnListOFObjects() As List(Of dummyObjectForCombo)
Dim col As New List(Of dummyObjectForCombo)
Dim obj0 As New dummyObjectForCombo(-1, "Herp")
Dim obj1 As New dummyObjectForCombo(1, "Jamie")
Dim obj2 As New dummyObjectForCombo(2, "Bob")
col.Add(obj1)
col.Add(obj2)
col.Add(obj0)
'using a lambda to sort by ID as per http://stackoverflow.com/questions/3309188/c-net-how-to-sort-a-list-t-by-a-property-in-the-object
Return col.OrderBy(Function(x) x.ID).ToList
End Function
End Class
I've used -1 as the topmost record instead of 0.
So you'd get your list as usual, add in the extra dummy record, then sort it as per the above code before assigning it as your combo boxes datasource.
simply add the item before setting the datasource property
C# : cboSize.items.add(...);
Related
I have a ComboBox that I use on multiple WinForms. Instead of dropping a ComboBox on each WinForm and then filling the ComboBox with data from a DataTable on each individual WinForm, couldn't I create a User Control (ComboBox) that has the data populated already and just use that UC on my Winforms?
Below is how I fill the data for each individual combobox now. (I have a public class for the sql stuff)
The Variable SQL comes from a Class called SQLControl. the Class has all the sql connection stuff.
Public Sub Fillcombobox()
sql.AddParam("#ExaminerType", 3)
sql.ExecQuery("MyStoredProcedure")
ComboBoxExaminer.ValueMember = "Examiner_ID"
ComboBoxExaminer.DisplayMember = "Last_Name"
ComboBoxExaminer.DataSource = sql.DBDT
End Sub
Private Sub MyWinform_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Call Fillcombobox()
End Sub
You can put a small Class Examiner
Public Class Examiner
Public Property Examiner_ID As Integer
Public Property Last_Name As String
Public Sub New(ID As Integer, lname As String)
Examiner_ID = ID
Last_Name = lname
End Sub
End Class
Then, when the first form loads, get the data in a list declared in a module so it can be accessed from any form in the application. Of course, you may have other things in the Module.
Module Module1
Public ExaminerData As New List(Of Examiner)
End Module
Private Sub MyWinform_Load(sender As Object, e As EventArgs) Handles MyBase.Load
FillExaminerList()
ComboBoxExaminer.ValueMember = "Examiner_ID"
ComboBoxExaminer.DisplayMember = "Last_Name"
ComboBoxExaminer.DataSource = ExaminerData
End Sub
Any other form that needs the data to fill a combo box can use ExaminerData. You only call FillExaminerList once at the beginning of the application. There is only a single hit on the database.
Private OPConStr As String = "Your connection string."
Private Sub FillExaminerList()
Dim dt As New DataTable
Using cn As New SqlConnection(OPConStr),
cmd As New SqlCommand("MyStoredProcedure", cn)
cmd.Parameters.Add("#ExaminerType", SqlDbType.Int).Value = 3
Using reader = cmd.ExecuteReader
dt.Load(reader)
End Using
End Using
For Each row As DataRow In dt.Rows
Dim ex As New Examiner(CInt(row("Examiner_ID")), row("Last_Name").ToString)
ExaminerData.Add(ex)
Next
End Sub
I have a simple custom combobox that just has an added property "ValueMember2".
I have the combobox datasource added to a dataset
I can set the property in code just as I would the normal property "ValueMember"
cboRadioType.ValueMember = "ID"
cboRadioType.ValueMember2 = "FREQMIN"
cboRadioType.DisplayMember = "MODEL"
The normal ValueMember property will return the ID from the datasource
The new ValueMember2 property just returns the string "FREQMIN"
My custom combobox code is as followed:
Imports System.ComponentModel
Public Class NewCBO
Inherits ComboBox
Dim vm2 As String
<Description("Gets/Sets the ValueMember2 of Control")>
Property ValueMember2() As String
Get
ValueMember2 = vm2
End Get
Set(ByVal Value As String)
vm2 = Value
End Set
End Property
End Class
What do I need to change my custom property to make it return the value from the connected datasource? All searches have turned up is how to "display a ValueMember"
Here is a custom ComboBox class that includes a fairly rigorous implementation of ValueMember2 and SelectedValue2 based on the existing implementations of ValueMember and SelectedValue:
Imports System.ComponentModel
Public Class ComboBoxEx
Inherits ComboBox
Private _valueMember2 As BindingMemberInfo
Public Property ValueMember2 As String
Get
Return _valueMember2.BindingMember
End Get
Set(value As String)
If value Is Nothing Then
value = String.Empty
End If
Dim bindingMemberInfo As New BindingMemberInfo(value)
If bindingMemberInfo.Equals(_valueMember2) Then
Return
End If
'This part is implemented in ValueMember but cannot be here because SetDataConnection is Private.
'It may be possible to provide our own implement of SetDataConnection but that is not done here.
'If DisplayMember.Length = 0 Then
' SetDataConnection(DataSource, bindingMemberInfo, False)
'End If
'This part is implemented in ValueMember but cannot be here because BindingMemberInfoInDataManager is Private.
'It may be possible to provide our own implement of BindingMemberInfoInDataManager but that is not done here.
'If DataManager IsNot Nothing AndAlso
' value <> String.Empty AndAlso
' Not BindingMemberInfoInDataManager(bindingMemberInfo) Then
' Throw New ArgumentException("...", NameOf(value))
'End If
_valueMember2 = bindingMemberInfo
'A rigorous implementation should also include implementation of corresponding events.
'OnValueMember2Changed(EventArgs.Empty)
'OnSelectedValue2Changed(EventArgs.Empty)
End Set
End Property
Public Property SelectedValue2 As Object
Get
Return If(SelectedIndex <> -1 AndAlso DataManager IsNot Nothing,
FilterItemOnProperty(DataManager.List(SelectedIndex), _valueMember2.BindingField),
Nothing)
End Get
Set(value As Object)
If DataManager Is Nothing Then
Return
End If
Dim bindingField = _valueMember2.BindingField
If String.IsNullOrEmpty(bindingField) Then
Throw New InvalidOperationException("...")
End If
'This part is implemented in ValueMember but cannot be here because DataManager.Find is Private.
'SelectedIndex = DataManager.Find(DataManager.GetItemProperties().Find(bindingField, True), value, True)
'The following replaces the call to DataManager.Find above.
If value Is Nothing Then
Throw New ArgumentNullException(NameOf(value))
End If
Dim newSelectedIndex = -1
Dim [property] = DataManager.GetItemProperties().Find(bindingField, True)
If [property] IsNot Nothing Then
Dim list=DataManager.List
Dim bindingList = TryCast(list, IBindingList)
If bindingList?.SupportsSearching Then
newSelectedIndex = bindingList.Find([property], value)
Else
For i = 0 To list.Count - 1
Dim obj = list(i)
If value.Equals(obj) Then
newSelectedIndex = i
Exit For
End If
Next
End If
End If
SelectedIndex = newSelectedIndex
End Set
End Property
End Class
There are some notes in there that you ought to read. Also note that ValueMember has design-time support that ValueMember2 lacks. It may not be difficult to use the same editor but I didn't look into that. It's also worth noting that ValueMember and SelectedValue are implemented in ListControl and thus are inherited by ListBox too. You'd have to implement this code separately in a custom ListBox.
I tested that control with the following code:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim table As New DataTable
With table.Columns
.Add("Id", GetType(Integer))
.Add("Name", GetType(String))
.Add("DateOfBirth", GetType(Date))
End With
With table.Rows
.Add(1, "Peter", #1/1/2001#)
.Add(2, "Paul", #2/2/2002#)
.Add(3, "Mary", #3/3/2003#)
End With
BindingSource1.DataSource = table
With ComboBoxEx1
.DisplayMember = "Name"
.ValueMember = "Id"
.ValueMember2 = "DateOfBirth"
.DataSource = BindingSource1
End With
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
MessageBox.Show($"ID: {ComboBoxEx1.SelectedValue}; Date of Birth: {ComboBoxEx1.SelectedValue2}", ComboBoxEx1.Text)
End Sub
End Class
It worked exactly as expected. The same code should also work for other data sources, e.g. a List(Of T), although I didn't do any additional testing.
Just to post what I have found out. Not sure if it the correct way but it works.
I added the following to my custom control:
Dim mds As New DataTable
then in the property I changed to this:
<Description("Gets/Sets the ValueMember2 of Control")>
Property ValueMember2() As String
Get
mds = Me.DataSource
ValueMember2 = mds.Rows(Me.SelectedValue - 1).Item(vm2)
End Get
Set(ByVal Value As String)
vm2 = Value
End Set
End Property
I'm not sure why I have to do the -1 but works like a charm.
In the normal code, instead of combobox.selectedvalue I just use combobox.ValueMember2 and it returns the proper item.
What i'm trying to do is to display two columns on a listbox so the user has more information.
The way i fill the listbox is by using a SqlDataSource with a custom query and then i attach that Data Source to the list box.
My problem is in the Data Source i can only pick two values one for the listbox display and the other values is to select the datafield value for the list box.
How can i display multiple column values from a SqlDataSource on a Listbox?
First create a Class for your data. I added a parameterized constructor so it would be easy to create a Coffee without separately setting properties. I also overrode the .ToString method so the ListBox would display what I want. If you really want columns you get into padding to a set length.
Public Class Coffee
Public Property Name As String
Public Property Type As String
Public Sub New(cofName As String, cofType As String)
Name = cofName
Type = cofType
End Sub
Public Overrides Function ToString() As String
Return $"{Name}, {Type}"
End Function
End Class
To fill the list, create a new coffee and add it to the list.
Private CoffeeList As New List(Of Coffee)
Private Sub FillCoffeeList()
Using cn As New SqlConnection(My.Settings.CoffeeConnection),
cmd As New SqlCommand("Select Top 10 Name, Type From Coffees;", cn)
cn.Open()
Using reader = cmd.ExecuteReader
While reader.Read
Dim c As New Coffee(reader.GetString(0), reader.GetString(1))
CoffeeList.Add(c)
End While
End Using
End Using
End Sub
Then in the Page.Load
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
If Not IsPostBack Then
FillCoffeeList()
ListBox1.DataSource = CoffeeList
ListBox1.DataBind()
End If
End Sub
The ListBox calls .ToString on each Coffee object in CoffeeList and displays what we determined in the Class.
I am trying to have a generic widget composed of a label and a value. The value is set by a combobox. Here is the structure:
Structure tParam
Dim label As Label
Dim comboBox As ComboBox
Dim valueX As String
End Structure
Dim parameter1 As tParam
I'd like to modify the valueX as the SelectedIndexChanged event is fired.
For now I have set
parameter1.label.text = "Id"
parameter1.comboBox.Tag = parameter1 ' the struct itself
AddHandler parameter1.comboBox.SelectedIndexChanged, AddressOf updateParam
and in the handler
Private Sub updateParam(sender As Object, e As System.EventArgs)
Dim parameterX As tParam = sender.Tag
With parameterX
Select Case .label.Text
Case "Id"
parameter1.valueX = .comboBox.SelectedIndex
End Select
End Sub
The problem is that I have a lot (>50) parameters of type tParam and I like not to check every parameter name with the select case.
Note that I am calling directly parameter1 in the handler, because parameterX (=sender.Tag) is read-only, as any update to parameterX is local.
I cant quite tell what you are trying to do, but tStruct.ComboBox.Tag = Me seems a convoluted way to track your widgets. Using a class, you could internalize and simplify some of what it seems you are trying to do:
Public Class CBOWidgetItem
Private WithEvents myCBO As ComboBox
Private myLbl As Label
Public Property Name As String
Public Property Value As String
Public Sub New(n As String, cbo As ComboBox, lbl As Label)
Name = n
myCBO = cbo
myLbl = lbl
End Sub
Private Sub myCBO_SelectedIndexChanged(sender As Object,
e As EventArgs) Handles myCBO.SelectedIndexChanged
Value = myCBO.SelectedIndex.ToString
End Sub
Public Overrides Function ToString() As String
Return Name
End Function
End Class
The widget is able to handle the Value change itself (again, I dont quite know what you are up to). You might have other wrapper props to expose certain info the widget is managing:
Public ReadOnly Property LabelText As String
Get
If myLbl IsNot Nothing Then
Return myLbl.Text
Else
Return ""
End If
End Get
End Property
To use it:
' something to store them in:
Private widgets As List(Of CBOWidgetItem)
...
widgets = New List(Of CBOWidgetItem)
' long form
Dim temp As New CBOWidgetItem("ID", ComboBox1, Label1)
widgets.Add(temp)
' short form:
widgets.Add(New CBOWidgetItem("foo", ComboBox2, Label2))
Elsewhere if you need to find one of these guys:
Dim find = "ID"
Dim specificItem = widgets.Where(Function(s) s.Name = find).FirstOrDefault
If specificItem IsNot Nothing Then
Console.WriteLine(specificItem.Name)
End If
Alternatively, you could use a Dictionary(Of String, CBOWidgetItem) and get them back by name.
I need to create a repeater section that will show 4 columns - First Name, Last Name, a link based off of stored column data that says.
All the data plus some extra not being used is in a players profile. How do I link the data on the code-behind to the repeater control with the databinders?
I am using visual studio 2008, VB.NET for the code behind.
Have you considered using a DataGrid instead of a repeater?
Here's a bit of a breakdown on when to use each.
http://msdn.microsoft.com/en-us/library/aa479015.aspx
To more directly answer your question you'll need to set the Repeater's DataSource property to a DataView or an ArrayList. As such:
Sub Page_Load(Sender As Object, e As EventArgs)
If Not IsPostBack Then
Dim values As New ArrayList()
values.Add(New PositionData("Microsoft", "Msft"))
values.Add(New PositionData("Intel", "Intc"))
values.Add(New PositionData("Dell", "Dell"))
Repeater1.DataSource = values
Repeater1.DataBind()
Repeater2.DataSource = values
Repeater2.DataBind()
End If
End Sub
Public Class PositionData
Private myName As String
Private myTicker As String
Public Sub New(newName As String, newTicker As String)
Me.myName = newName
Me.myTicker = newTicker
End Sub
Public ReadOnly Property Name() As String
Get
Return myName
End Get
End Property
Public ReadOnly Property Ticker() As String
Get
Return myTicker
End Get
End Property
End Class