CheckChanged event not firing with dynamic checkboxes? - vb.net

I have a gridview control that is dynamically configured after a button-click event. Some of the columns contain dynamically added checkboxes. For some reason, I can't get the OnCheckedChanged event to fire for any of the checkboxes in this gridview.
Here's what fires after the button click event:
Private Sub BuildGridViewColumnList()
Try
' Clear all columns.
grdCommodityConfig.Columns.Clear()
' Add static columns.
Dim CommodityColumn As New BoundField
CommodityColumn.HeaderText = "Commodity"
CommodityColumn.DataField = "Commodity"
grdCommodityConfig.Columns.Add(CommodityColumn)
Dim PartTypeColumn As New BoundField
PartTypeColumn.HeaderText = "Part Type"
PartTypeColumn.DataField = "PartType"
grdCommodityConfig.Columns.Add(PartTypeColumn)
' Add dynamic columns
Dim ColumnHeaders As String = String.Empty
Database.GetCommodityConfig(txtAssyLine.Text, ColumnHeaders)
Dim ColumnList As List(Of String) = ColumnHeaders.Split(New Char() {","c}).ToList
' Add each column found in list returned from DB.
For Each ColumnName As String In ColumnList
Dim ItemTmpField As New TemplateField()
' create HeaderTemplate
ItemTmpField.HeaderTemplate = New DynamicallyTemplatedGridViewHandler(ListItemType.Header, ColumnName, "CheckBox")
' create ItemTemplate
ItemTmpField.ItemTemplate = New DynamicallyTemplatedGridViewHandler(ListItemType.Item, ColumnName, "CheckBox")
'create EditItemTemplate
ItemTmpField.EditItemTemplate = New DynamicallyTemplatedGridViewHandler(ListItemType.EditItem, ColumnName, "CheckBox")
' then add to the GridView
ItemTmpField.ItemStyle.HorizontalAlign = HorizontalAlign.Center
grdCommodityConfig.Columns.Add(ItemTmpField)
Next
Catch ex As Exception
Throw ex
End Try
End Sub
This is the class used to add the gridview & checkboxes:
Imports System.Data
Imports System.Configuration
Imports System.Collections
Imports System.Web
Imports System.Web.Security
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Web.UI.WebControls.WebParts
Imports System.Web.UI.HtmlControls
Imports System.Collections.Specialized
Imports System.Data.SqlClient
Public Class DynamicallyTemplatedGridViewHandler
Implements ITemplate
Private ItemType As ListItemType
Private FieldName As String
Private InfoType As String
Public Sub New(item_type As ListItemType, field_name As String, info_type As String)
ItemType = item_type
FieldName = field_name
InfoType = info_type
End Sub
Public Sub InstantiateIn(Container As System.Web.UI.Control) Implements ITemplate.InstantiateIn
Select Case ItemType
Case ListItemType.Header
Dim header_ltrl As New Literal()
header_ltrl.Text = "<b>" & FieldName & "</b>"
Container.Controls.Add(header_ltrl)
Exit Select
Case ListItemType.Item
Select Case InfoType
Case "CheckBox"
' for other 'non-command' i.e. the key and non key fields, bind textboxes with corresponding field values
Dim field_chkbox As New CheckBox()
field_chkbox.ID = FieldName
field_chkbox.Text = [String].Empty
' if Inert is intended no need to bind it with text..keep them empty
AddHandler field_chkbox.DataBinding, New EventHandler(AddressOf OnDataBinding)
AddHandler field_chkbox.CheckedChanged, New EventHandler(AddressOf OnCheckedChanged)
field_chkbox.CausesValidation = False
Container.Controls.Add(field_chkbox)
Case Else
Dim field_lbl As New Label()
field_lbl.ID = FieldName
field_lbl.Text = [String].Empty
'we will bind it later through 'OnDataBinding' event
AddHandler field_lbl.DataBinding, New EventHandler(AddressOf OnDataBinding)
Container.Controls.Add(field_lbl)
Exit Select
End Select
Exit Select
Case ListItemType.EditItem
If InfoType = "CheckBox" Then
' for other 'non-command' i.e. the key and non key fields, bind textboxes with corresponding field values
Dim field_chkbox As New CheckBox()
field_chkbox.ID = FieldName
field_chkbox.Text = [String].Empty
AddHandler field_chkbox.DataBinding, New EventHandler(AddressOf OnDataBinding)
AddHandler field_chkbox.CheckedChanged, New EventHandler(AddressOf OnCheckedChanged)
field_chkbox.CausesValidation = False
Container.Controls.Add(field_chkbox)
Else
' for other 'non-command' i.e. the key and non key fields, bind textboxes with corresponding field values
Dim field_txtbox As New TextBox()
field_txtbox.ID = FieldName
field_txtbox.Text = [String].Empty
AddHandler field_txtbox.DataBinding, New EventHandler(AddressOf OnDataBinding)
Container.Controls.Add(field_txtbox)
End If
Exit Select
End Select
End Sub
Private Sub OnDataBinding(sender As Object, e As EventArgs)
Dim bound_value_obj As Object = Nothing
Dim ctrl As Control = DirectCast(sender, Control)
Dim data_item_container As IDataItemContainer = DirectCast(ctrl.NamingContainer, IDataItemContainer)
bound_value_obj = DataBinder.Eval(data_item_container.DataItem, FieldName)
Select Case ItemType
Case ListItemType.Item
Dim field_ltrl As CheckBox = DirectCast(sender, CheckBox)
field_ltrl.Checked = CBool(bound_value_obj.ToString())
AddHandler field_ltrl.CheckedChanged, New EventHandler(AddressOf OnCheckedChanged)
field_ltrl.CausesValidation = False
Exit Select
Case ListItemType.EditItem
Dim field_txtbox As CheckBox = DirectCast(sender, CheckBox)
field_txtbox.Checked = CBool(bound_value_obj.ToString())
AddHandler field_txtbox.CheckedChanged, New EventHandler(AddressOf OnCheckedChanged)
field_txtbox.CausesValidation = False
Exit Select
End Select
End Sub

I believe I've found the answer to my own question. After some more Google searches, I added the following lines of code, and the CheckChanged event finally started firing. It's a step in the right direction!
Added this beneath the AddHandler statements in the DynamicallyTemplatedGridViewHandler class:
field_chkbox.AutoPostBack = True
As suggested by Tim S, I added the following in the page that houses the dynamic gridview control:
Private Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
If Me.IsPostBack Then
Try
' Need to build the column list dynamically.
BuildGridViewColumnList()
' Refresh GridView data.
BindGridViewData()
SetErrorMessage(String.Empty)
Catch ex As Exception
SetErrorMessage(ex.Message)
End Try
End If
End Sub

Related

Binding a Long/Int64 field to NumericUpdown leads to OleDbException "Too few parameters" on update

I am stuck with this problem the whole day, so maybe someone has a tip for me.
The environment: MS Access DB, VB.NET, OleDb-Driver, MS Access Database Engine 2016.
I am trying to bind a Long/Int64 field to a NumericUpDown control, but on update I get always the OleDbException "Too few parameters". I am pretty sure that the long field is the problem, because when I delete it from the table (and comment out the code for it) everything works fine. I am not sure that a NumericUpDown is suitable for this task, but it's underlying datatype is decimal and decimal has way more space as a long needs. So here is some code:
In the DataLayer:
Private Sub InitializeDataSet()
Dim LocFields = String.Join("], [", Fields)
Dim SQL As String = "SELECT [" & LocFields & "] FROM [" & Table & "]"
DAMain = New OleDbDataAdapter(SQL, Connection)
Using Builder As New OleDbCommandBuilder(DAMain) With {
.ConflictOption = ConflictOption.OverwriteChanges
}
Builder.GetInsertCommand()
Builder.GetUpdateCommand()
Builder.GetDeleteCommand()
End Using
DS = New DataSet
DAMain.Fill(DS, Table)
End Sub
Public Sub Update()
DAMain.Update(DS, Table) ' <-- Here the exception happens
End Sub
Public ReadOnly Property DataSource As Object
Get
Return DS
End Get
End Property
Public ReadOnly Property DataMember As String
Get
Return Table
End Get
End Property
We are here in a class, so the variables are:
Table = the name of the table
Fields = the list of the fields in the table
Connection = the OleDbConnection
DAMain = the OleDbDataAdapter
DS = the DataSet
In the Form:
Private DL As OleDbDataLayer
Private WithEvents BSource As New BindingSource
Public Sub New(DataLayer As OleDbDataLayer)
InitializeComponent()
DL = DataLayer
BSource.DataSource = DL.DataSource
BSource.DataMember = DL.DataMember
BSource.AllowNew = True
BSource.Sort = DL.OrderBy
BSource.Position = 0
InitializeFields()
End Sub
Private Sub DataUpdate()
BSource.EndEdit()
DL.Update()
End Sub
Private Sub InitializeFields()
NUD.Minimum = Long.MinValue
NUD.Maximum = Long.MaxValue
Dim Binding As New Binding("Value", BSource, "F_Long")
AddHandler Binding.Format, AddressOf FormatDBNull
AddHandler Binding.Parse, AddressOf ParseNumericUpDown
NUD.DataBindings.Add(Binding)
End Sub
Private Sub FormatDBNull(sender As Object, e As ConvertEventArgs)
If Convert.IsDBNull(e.Value) Then
Select Case e.DesiredType
Case = GetType(Decimal) : e.Value = 0
Case = GetType(Date) : e.Value = Today
Case = GetType(Boolean) : e.Value = False
Case Else
End Select
End If
End Sub
Private Sub ParseNumericUpDown(sender As Object, e As ConvertEventArgs)
Select Case e.DesiredType
Case = GetType(Byte)
e.Value = Convert.ToByte(e.Value)
Case = GetType(Short)
e.Value = Convert.ToInt16(e.Value)
Case = GetType(Integer)
e.Value = Convert.ToInt32(e.Value)
Case = GetType(Long)
e.Value = Convert.ToInt64(e.Value)
Case Else
' Do Nothing
End Select
End Sub
Here NUD is the NumericUpDown control which should be obvious.
Maybe I should use another control type? TextBox? MaskedTextBox?

Check the item in ToolStripMenuItem dynamically through Sub Function

I am new to .Net Visual Basic, I am currently self learning and trying to make some small application.
I need a help on Checking a sub menu item of ToolStripMenuItem
Complete concept is like:
I have a datagridview in which user will be able to rearrange the column or make a column visible or Hidded for this I have Sub / Function like below:
Public Sub Fun_Grid_Colomn_Visibility(ByVal GridName As DataGridView, ByRef ColName As String, ByVal MS_col As ToolStripMenuItem, ChkVal As Boolean)
If ChkVal = True Then
With GridName
.Columns("" & ColName & "").Visible = False
End With
MS_col.Checked = False
Exit Sub
End If
If ChkVal = False Then
GridName.Columns("" & ColName & "").Visible = True
MS_col.Checked = True
Exit Sub
End If
End Sub
On the form close I will be saving the user grid format as below (Got code from another Q/A Post) :
Public Sub WriteGrideViewSetting(ByVal dgv As DataGridView, ByVal FileName As String)
Dim settingwriter As XmlTextWriter = New XmlTextWriter("C:\Users\<username>\Desktop\temp\" & FileName & ".xml", Nothing)
settingwriter.WriteStartDocument()
settingwriter.WriteStartElement(dgv.Name)
Dim count As Integer = dgv.Columns.Count
For i As Integer = 0 To count - 1
settingwriter.WriteStartElement("column")
settingwriter.WriteStartElement("Name")
settingwriter.WriteString(dgv.Columns(i).Name)
settingwriter.WriteEndElement()
settingwriter.WriteStartElement("width")
settingwriter.WriteString(dgv.Columns(i).Width.ToString())
settingwriter.WriteEndElement()
settingwriter.WriteStartElement("headertext")
settingwriter.WriteString(dgv.Columns(i).HeaderText)
settingwriter.WriteEndElement()
settingwriter.WriteStartElement("displayindex")
settingwriter.WriteString(dgv.Columns(i).DisplayIndex.ToString())
settingwriter.WriteEndElement()
settingwriter.WriteStartElement("visible")
settingwriter.WriteString(dgv.Columns(i).Visible.ToString())
settingwriter.WriteEndElement()
settingwriter.WriteEndElement()
Next
settingwriter.WriteEndElement()
settingwriter.WriteEndDocument()
settingwriter.Close()
End Sub
End Module
If the user is reopening the form I used the below (Q/A code) to rearrange Datagridview column as pervious :
Public Sub ReadDataGridViewSetting(ByVal dgv As DataGridView, ByVal FileName As String, ByRef Frm_name As Form)
Dim xmldoc As XmlDocument = New XmlDocument()
Dim xmlnode As XmlNodeList
Dim CMSN_ToolName As String
Dim Var_file_Chk As String = "C:\Users\<user>\Desktop\temp\" & FileName & ".xml"
If System.IO.File.Exists(Var_file_Chk) = True Then
Dim fs As FileStream = New FileStream(Var_file_Chk, FileMode.Open, FileAccess.Read)
xmldoc.Load(fs)
xmlnode = xmldoc.GetElementsByTagName("column")
For i As Integer = 0 To xmlnode.Count - 1
Dim columnName As String = xmlnode(i).ChildNodes.Item(0).InnerText.Trim()
Dim width As Integer = Integer.Parse(xmlnode(i).ChildNodes.Item(1).InnerText.Trim())
Dim headertext As String = xmlnode(i).ChildNodes.Item(2).InnerText.Trim()
Dim displayindex As Integer = Integer.Parse(xmlnode(i).ChildNodes.Item(3).InnerText.Trim())
Dim visible As Boolean = Convert.ToBoolean(xmlnode(i).ChildNodes.Item(4).InnerText.Trim())
dgv.Columns(columnName).Width = width
dgv.Columns(columnName).HeaderText = headertext
dgv.Columns(columnName).DisplayIndex = displayindex
dgv.Columns(columnName).Visible = visible
Next
fs.Close()
End If
End Sub
Now what I need is that a Function or Sub for the Itemmenu. If a Particular column is Visible in the datagridview then the particular Itemmenu should be checked else it would be unchecked. I need this function when Itemmenu is being displayed / opened.
what I tried just (for sample) in Itemmenu opening is like
Private Sub ColumnsToolStripMenuItem_DropDownOpening(sender As Object, e As EventArgs) Handles ColumnsToolStripMenuItem.DropDownOpening
If DGV_CompList.Columns("DGC_Est").Visible = True Then
Dim CMSN_ToolName = MS_CV_Est.Name
Dim unused As ToolStripMenuItem = New ToolStripMenuItem(CMSN_ToolName) With {
.Checked = True
}
End If
End Sub
DGV_CompList -> DataGridView
DGC_Est -> Column Name of datagridview
MS_CV_Est -> - ToolStripMenuItem which need to checked
(Note: I will be changing the MenuItem Name to Match Datagrid Column name for Sync)
But the ToolStripMenuItem is not getting checked.
Actually I need function / Sub where I will be able to pass the grid name and the Menuname and loop through the grid columns and check if the column is visible or not if the particular column is visible then I need to check that item in the itemmenu.
I am requesting for the sub / function because it can be used for any toolstripmenuitem in any form.
Thanks and Regards.
As per #Jimi's hint, assigned each required Menuitem's Tag property with the datagridview column name and created the below sub / function :
Public Sub Fun_ToolStripMenuItem_Check(ByVal dgv As DataGridView, ByVal TS_Menu_Items As ToolStripItemCollection)
For Each item As ToolStripMenuItem In TS_Menu_Items.OfType(Of ToolStripMenuItem)
If Not item.Tag = "" Then
If dgv.Columns(item.Tag).Visible = True Then
item.Checked = True
Else
item.Checked = False
End If
End If
For Each submenu_item As ToolStripMenuItem In item.DropDownItems.OfType(Of ToolStripMenuItem)
If Not submenu_item.Tag = "" Then
If dgv.Columns(submenu_item.Tag).Visible = True Then
submenu_item.Checked = True
Else
submenu_item.Checked = False
End If
End If
Next
Next
End Sub
Note in the loop used - " OfType(Of ToolStripMenuItem) " because I have ToolStripSeparator between the Itemmenus.
On mouse over called the Sub by :
Private Sub MS_ColumnVisible_DropDownOpening(sender As Object, e As EventArgs) Handles MS_ColumnVisible.DropDownOpening
Fun_ToolStripMenuItem_Check(DGV_CompList, MS_CompDGV.Items)
End Sub
'DGV_CompList' - Datagridview Name and 'MS_CompDGV' - ContextMenuStrip Name
More important is that I did not assign any value to the Tag property to Menuitems which are not used show or hide the datagridview columns.

Data column expression with variable

I have following code and it is not working and calculate cost is zero. What is the mistake here?
.Columns.Add("Total", Type.GetType("System.Int32"))
.Columns("Total").Expression = "200 * " & FxRate.ToString
FxRate is a public variable.
when FxRate changes, the Total fields should automatically update.
The DataColumn.Expression does not provide a way to reference external variables. However, you can update the expression at any time.
You will need to call the update method when FxRate changes. If FxRate is a property, you can call the update method from its setter method.
Changes to the Expression will not be automatically be reflect on bound control. The update method will also need to tell the bindings to update. If you are binding through a BindingSource instance, call its ResetBindings(False) method. If you are binding directly to the DataTable, a more complex method would be to obtain theDataTable'sCurrencyManagerand call itsRefresh` method.
Assuming the DataTable variable is named dt, the following code will acquire the CurrencyManager; Me refers to the containing Form.
Dim mngr As CurrencyManager = CType(Me.BindingContext.Item(dt), CurrencyManager)
mngr.Refresh()
Edit: A working example using a BindingSource as requested in the comments.
In a new WinForm project, replace the contents of Form1.vb with the following code (all controls are created by this code - no designer support).
The code creates: a TextBox for changing the "Quantity" field", a Label to display the "Total" field, a NumericUpDown to allow changing the value of the FxRate property, and two button to allow navigating through the records in the DataTable.
The DataTable has three records. A record comprise two fields "Quantity" and "Total". "Total" will be computed as the product of the form's FxRate property and and the field "Quantity" by setting the Expression property of the "Quantity" column each time FxRate changes.
Public Class Form1
Inherits Form
Private tbInput As TextBox
Private lblTotal As Label
Private nudFxRate As NumericUpDown
Private btnNext As Button
Private btnPrevious As Button
Private bs As BindingSource
Private _FxRate As Int32
Private dt As DataTable
Public Sub New()
MyBase.New()
SetupControls()
dt = New DemoTable ' create a preconfigured DataTable
bs = New BindingSource(dt, Nothing)
SetBindings()
FxRate = 5
AttachControlEventHandlers()
End Sub
Public Property FxRate As Int32
Get
Return _FxRate
End Get
Set(value As Int32)
If value <> _FxRate Then
_FxRate = value
UpdateTotalExpression() ' only update the Expression if the value changes
End If
End Set
End Property
Private Sub UpdateTotalExpression()
' Change the expression to reflect the current value of FxRate
Dim totalColumn As DataColumn = dt.Columns("Total")
totalColumn.Expression = $"[Quantity] * {FxRate}"
' Expression changes do not notify the BindingSource of value changes
' so tell the BindingSource to reload all values
bs.ResetBindings(False)
End Sub
Private Sub tbInput_KeyPress(sender As Object, e As KeyPressEventArgs)
If e.KeyChar = Convert.ToChar(Keys.Enter) Then
Me.Validate() ' force tbInput to Validate
e.Handled = True ' eat the enter key
End If
End Sub
Private Sub tbInput_Validated(sender As Object, e As EventArgs)
' if tbInput successfully validated, push the values in the BindingSource to the DataTable
bs.EndEdit() ' push the editted value to the DataTable, causing Total to update
End Sub
Private Sub SetBindings()
' Update BindingSource once tbInput Validates successfully
tbInput.DataBindings.Add("Text", bs, "Quantity", True, DataSourceUpdateMode.OnValidation)
' lblTotal never updates the BindingSource
lblTotal.DataBindings.Add("Text", bs, "Total", True, DataSourceUpdateMode.Never)
' nudFxRate updates the FxRate property
nudFxRate.DataBindings.Add("Value", Me, "FxRate", True, DataSourceUpdateMode.OnPropertyChanged)
End Sub
Private Sub SetupControls()
tbInput = New System.Windows.Forms.TextBox()
lblTotal = New System.Windows.Forms.Label()
nudFxRate = New System.Windows.Forms.NumericUpDown()
btnNext = New System.Windows.Forms.Button()
btnPrevious = New System.Windows.Forms.Button()
CType(nudFxRate, System.ComponentModel.ISupportInitialize).BeginInit()
SuspendLayout()
'
'tbInput
'
tbInput.Location = New System.Drawing.Point(27, 40)
tbInput.Name = "tbInput"
tbInput.Size = New System.Drawing.Size(100, 22)
tbInput.TabIndex = 0
'
'lblTotal
'
lblTotal.AutoSize = False
lblTotal.BackColor = Color.Yellow
lblTotal.Location = New System.Drawing.Point(299, 42)
lblTotal.Name = "lblTotal"
lblTotal.Size = New System.Drawing.Size(100, 17)
lblTotal.TabIndex = 1
lblTotal.Text = "0"
'
'nudFxRate
'
nudFxRate.Location = New System.Drawing.Point(28, 94)
nudFxRate.Name = "nudFxRate"
nudFxRate.Size = New System.Drawing.Size(120, 22)
nudFxRate.TabIndex = 3
nudFxRate.Value = 5
'
'btnNext
'
btnNext.Location = New System.Drawing.Point(27, 136)
btnNext.Name = "btnNext"
btnNext.Size = New System.Drawing.Size(75, 23)
btnNext.TabIndex = 4
btnNext.Text = "Next"
btnNext.UseVisualStyleBackColor = True
'
'btnPrevious
'
btnPrevious.Location = New System.Drawing.Point(28, 171)
btnPrevious.Name = "btnPrevious"
btnPrevious.Size = New System.Drawing.Size(75, 23)
btnPrevious.TabIndex = 5
btnPrevious.Text = "Previous"
btnPrevious.UseVisualStyleBackColor = True
'
'Form1
'
AutoScaleDimensions = New System.Drawing.SizeF(8.0!, 16.0!)
AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
ClientSize = New System.Drawing.Size(800, 450)
Controls.Add(btnPrevious)
Controls.Add(btnNext)
Controls.Add(nudFxRate)
Controls.Add(lblTotal)
Controls.Add(tbInput)
Name = "Form1"
Text = "Form1"
CType(nudFxRate, System.ComponentModel.ISupportInitialize).EndInit()
ResumeLayout(False)
PerformLayout()
End Sub
Private Sub AttachControlEventHandlers()
AddHandler btnNext.Click, Sub() bs.MoveNext() ' move to next record in bindingsource
AddHandler btnPrevious.Click, Sub() bs.MovePrevious() ' move to previous record in bindingsource
AddHandler tbInput.KeyPress, AddressOf tbInput_KeyPress ' allow enter key to validate textbox
AddHandler tbInput.Validated, AddressOf tbInput_Validated ' update bindingsource on validation
End Sub
Private Class DemoTable : Inherits DataTable
Public Sub New()
Columns.Add("Quantity", GetType(Int32))
Columns.Add("Total", GetType(Int32))
Rows.Add(New Object() {10})
Rows.Add(New Object() {20})
Rows.Add(New Object() {30})
End Sub
End Class
End Class
Add the column FxRate. Change the expression of the Total column to "200 * FxRate", and the value of the Total column will be automatically set based on whatever FxRate is set to.
.Columns.Add("FxRate", Type.GetType("System.Int32"))
.Columns.Add("Total", Type.GetType("System.Int32"))
.Columns("Total").Expression = "200 * FxRate"
' Example of setting FxRate in row 0.
' (Assuming row 0 exists.)
.Rows(0).Item("FxRate") = 3

Why I am getting System.Data.DataRowViewā€¯ instead of real values from my Listbox in vb.net

Could someone help here?
I need to extract data from a Database into a combolistbox in VB.net. I have got the data, but now find that The first and the 'x' line need to be removed from the combolistbox (they are validation entries for another software) and shouldn't be selected for this application.
I tried to simply remove the offending entries from lists by using :- cbCubeARivet.Items.RemoveAt(index), but had an error letting me know I cannot use "Items" with a DataSource.
I decided to send the data to a listbox, and then try to transfer the entries to the combolistbox. This then lead me to getting multiple entries of System.Data.DataRowView in the combolist box. To demonstrate my problem I include an example code modified from MSDN.
Imports System
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Collections
Imports System.Data.SqlClient
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' Populate the list box using an array as DataSource.
Dim SQLConnectionString As String = "Data Source=HL605\RIVWARE;Database=RIVWARE;Integrated Security=true;"
Dim mySQLConnection As New SqlConnection(SQLConnectionString)
mySQLConnection.Open()
Dim SQLDataTable As New System.Data.DataTable
'Create new DataAdapter
'Use DataAdapter to fill DataTable
Dim mySQLDataAdapter = New SqlDataAdapter("SELECT * FROM [Rivware].[dbo].[RivetTypes]", mySQLConnection)
mySQLDataAdapter.Fill(SQLDataTable)
ListBox1.DataSource = SQLDataTable
ListBox1.DisplayMember = "RivetType"
'original code from MSDN
'Dim USStates As New ArrayList()
'USStates.Add(New USState("Alabama", "AL"))
'USStates.Add(New USState("Washington", "WA"))
'USStates.Add(New USState("West Virginia", "WV"))
'USStates.Add(New USState("Wisconsin", "WI"))
'USStates.Add(New USState("Wyoming", "WY"))
'ListBox1.DataSource = USStates
' Set the long name as the property to be displayed and the short
' name as the value to be returned when a row is selected. Here
' these are properties; if we were binding to a database table or
' query these could be column names.
' Bind the SelectedValueChanged event to our handler for it.
AddHandler ListBox1.SelectedValueChanged, AddressOf ListBox1_SelectedValueChanged
' Ensure the form opens with no rows selected.
ListBox1.ClearSelected()
End Sub 'NewNew
Private Sub ListBox1_SelectedValueChanged(ByVal sender As Object, ByVal e As EventArgs)
If ListBox1.SelectedIndex <> -1 Then
TextBox1.Text = ListBox1.SelectedValue.ToString()
' If we also wanted to get the displayed text we could use
' the SelectedItem item property:
' Dim s = CType(ListBox1.SelectedItem, USState).LongName
End If
End Sub
End Class 'ListBoxSample3
Public Class USState
Private myShortName As String
Private myLongName As String
Public Sub New(ByVal strLongName As String, ByVal strShortName As String)
Me.myShortName = strShortName
Me.myLongName = strLongName
End Sub 'NewNew
Public ReadOnly Property ShortName() As String
Get
Return myShortName
End Get
End Property
Public ReadOnly Property LongName() As String
Get
Return myLongName
End Get
End Property
End Class 'USState
I may not get this correct so here goes, the following uses mocked up data to display a string in both a ListBox and ComboBox where a integer is available by casting the current item as a DataRowView, access Row then access the data via Row.Field(Of Integer)("ID").
In the code I use a copy of the underlying DataTable for the ComboBox as using the same data table for listbox and combobox will cause one to traverse when is generally unwanted. The cast aspect would be done in another event but wanted to stay simple. Again I may not be on track here, let me know and can adjust to better suit your question.
Code using Option Infer On for the Linq anonymous statement which could be strongly typed too.
Dim dt As New DataTable
dt.Columns.Add(New DataColumn With {.ColumnName = "ID", .DataType = GetType(Integer)})
dt.Columns.Add(New DataColumn With {.ColumnName = "Name", .DataType = GetType(String)})
Dim data =
(
From M In System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.MonthNames
Where Not String.IsNullOrEmpty(M)).ToList.Select(
Function(monthName, index) New With
{
.ID = index, .Name = monthName
}
).ToList
For Each item In data
dt.Rows.Add(New Object() {item.ID, item.Name})
Next
ListBox1.DataSource = dt
ListBox1.DisplayMember = "Name"
ComboBox1.DataSource = dt.Copy
ComboBox1.DisplayMember = "Name"
Dim theTable As DataTable = CType(ComboBox1.DataSource, DataTable)
Dim theRow As DataRow = theTable.AsEnumerable _
.Where(
Function(row) row.Field(Of String)("Name") = "September") _
.FirstOrDefault()
If theRow IsNot Nothing Then
theTable.Rows.Remove(theRow)
End If
Thanks again #Karen Payne,
I used your code to lead me in the right direction.
I created a new application and added a textbox, and two Listboxes, then paste the code. To run you will need to point to your own Server, Database, and Table.
This is what I came up with. It is useful as this will give you the actual data in a useable form:-
Imports System
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Collections
Imports System.Data.SqlClient
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' get the data
Dim SQLConnectionString As String = "Data Source=HL605\RIVWARE;Database=RIVWARE;Integrated Security=true;"
Dim mySQLConnection As New SqlConnection(SQLConnectionString)
' Populate the list box using an array as DataSource.
mySQLConnection.Open()
Dim SQLDataTable As New System.Data.DataTable
'Create new DataAdapter
Dim mySQLDataAdapter = New SqlDataAdapter("SELECT * FROM [Rivware].[dbo].[RivetTypes]", mySQLConnection)
mySQLDataAdapter.Fill(SQLDataTable)
ListBox1.DataSource = SQLDataTable
ListBox1.DisplayMember = "RivetType"
' remove validation data from list
Dim Count As Integer
Dim X As Integer
'get the column of data to search for
For Count = 0 To ListBox1.DataSource.Columns.Count - 1
X = InStr(ListBox1.DataSource.Columns.Item(Count).ToString, "Name")
If X <> 0 Then Exit For
Next
' now search for any invalid rows, in that column. those containing "---"
Dim TheTable As DataTable = CType(ListBox1.DataSource, DataTable)
Dim theRow As DataRow() = TheTable.Select()
Dim RowNumber As Integer
For RowNumber = 0 To UBound(theRow) - 1
If theRow(RowNumber).Item(Count).ToString <> "---" Then
' data is OK so transer it to the other listbox
ListBox2.Items.Add(theRow(RowNumber).Item(Count - 1).ToString)
End If
Next
ListBox2.ClearSelected()
End Sub 'NewNew
Private Sub ListBox2_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ListBox2.SelectedIndexChanged
TextBox1.Text = ListBox2.SelectedItem.ToString()
End Sub
End Class 'ListBoxSample3

General function for opening ChildWinforms using reflection?

I'm not sure how to put this so the title may seem a little strange. I have a VB.NET MDI Winform application. In this app when I click a menuitem a child window is opened. To handle what child window must be opened I have case statement in my ToolStripMenuItem_Click event. The problem is that in every case-item the code is nearly the same. So I was wondering if it would be possible to have a general function so the code becomes more readable.
Example:
Case "mnuPrint"
Dim ChildWindowFound As Boolean = False
If Not (MdiChildren.Length.Equals(0)) Then
For Each ChildWindow As Form In MdiChildren
If ChildWindow.Name.Equals("Form1") Then
ChildWindow.Activate()
ChildWindowFound = True
End If
Next
End If
If Not ChildWindowFound Then
Dim childForm As New Form1()
childForm.Name = "Form1"
childForm.MdiParent = Me
childForm.Show()
End If
Case "mnuSearch"
Dim ChildWindowFound As Boolean = False
If Not (MdiChildren.Length.Equals(0)) Then
For Each ChildWindow As Form In MdiChildren
If ChildWindow.Name.Equals("Form2") Then
ChildWindow.Activate()
ChildWindowFound = True
End If
Next
End If
If Not ChildWindowFound Then
Dim childForm As New Form2()
childForm.Name = "Form2"
childForm.MdiParent = Me
childForm.Show()
End If
As you can see the code is nearly the same but with different forms, here Form1 and Form2 which are different, but it can be many different forms of course. The function I'm talking about would look something like:
Public Sub OpenNewForm(ByVal frm As Form, ByVal parent As Form, ByVal singleInstance As Boolean)
Dim ChildWindowFound As Boolean = False
If Not (parent.MdiChildren.Length.Equals(0)) Then
For Each ChildWindow As Form In parent.MdiChildren
If ChildWindow.Name.Equals(frm.Name) Then
ChildWindow.Activate()
ChildWindowFound = True
End If
Next
End If
If Not ChildWindowFound Then
Dim childForm As New Form
childForm.Name = frm.Name
childForm.MdiParent = Me
childForm.Show()
End If
End Sub
This doesn't work because I pass the parameters (first two) as Form-type, where it should be Form1 and ParentForm for example, not Form. I think it is possible, just don't know where to start. May be using reflection or something?
UPDATE:
Based on the answers given I came up with this code which is working fine:
'Code contributed by Rod Paddock (Dash Point Software)
'www.dashpoint.com
'Dim oForm As Form = ObjectFactory.CreateAnObject("MyApplication.frmTwo")
'oForm.Show()
Public Shared Function CreateAnObject(ByVal ObjectName As String) As Object
Dim Assem = [Assembly].GetExecutingAssembly()
Dim myType As Type = Assem.GetType(ObjectName.Trim)
Dim o As Object = Nothing
Try
o = Activator.CreateInstance(myType)
Catch oEx As TargetInvocationException
MessageBox.Show(oEx.ToString)
End Try
Return o
End Function
Public Shared Sub ActivateChildWindow(ByVal frmName As String, ByRef ParentMDIWindow As Form)
Dim ChildWindowFound As Boolean = False
With ParentMDIWindow
If Not (.MdiChildren.Length.Equals(0)) Then
For Each ChildWindow As Form In .MdiChildren
If ChildWindow.Name.Equals(frmName) Then
ChildWindow.Activate()
ChildWindowFound = True
End If
Next
End If
End With
If Not ChildWindowFound Then
Dim childForm As Form = CreateAnObject(frmName)
childForm.Name = frmName
childForm.MdiParent = ParentMDIWindow
childForm.Show()
End If
Maybe something like this can help you:
Dim myForm As Form = Nothing
Dim FormName As String = String.Empty
Dim formType As Type
Select Case Options
Case "mnuPrint"
FormName = "Form1"
myForm = getWindowByName(FormName)
Case "mnuSearch"
FormName = "Form2"
myForm = getWindowByName(FormName)
End Select
If myForm Is Nothing Then
formType = Type.GetType("WindowsApplication2." + FormName) 'WindowsApplication2 is my project's name
myForm = Activator.CreateInstance(formType)
myForm.Name = FormName
myForm.MdiParent = Me
myForm.Show()
Else
myForm.Activate()
End If
And the function:
Function getWindowByName(ByVal FormName As String) As Form
Dim frm As Form = Nothing
If Not (MdiChildren.Length.Equals(0)) Then
For Each ChildWindow As Form In MdiChildren
If ChildWindow.Name.Equals(FormName) Then
frm = ChildWindow
End If
Next
End If
Return frm
End Function
See if this will get you started. Create a project with three forms, form1 with two buttons on. Then, add this code to Form1:
Public Class Form1
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click, Button2.Click
Dim t As Type
Select Case sender.name
Case "Button1"
t = GetType(Form2)
Case "Button2"
t = GetType(Form3)
End Select
Dim f = System.Activator.CreateInstance(t)
f.show()
End Sub
End Class