I'm using VS 2012 (vb.net) and I have a form with a flowLayoutPanel which is meant to contain an unknown number of buttons. To make it simple, basically, when the form Loads I fetch items from a table based one some criteria and then a use a For...Next block to add a button for each item to the flowLayoutPanel. So if I find 5 items I add 5 buttons all named differently but the problem is that they seem to pile up on each other instead of lining up nicely. When I use a button to add the items one after the other it works fine but when I use the For...Next block it does not work. I have tried refreshing the flowLayoutPanel after adding each button, I have tried to set the location of each new button relative to the location of the previous button but it still does to work.
I have researched this for over a week now and there is a ton of stuff out there but nothing specifically deals with this.
Thanks for your help.
This is the relevant part of my code:
`Try
If conn.State <> ConnectionState.Open Then
conn.Open()
End If
sql = "SELECT ItemCode, Description, NormalPrice FROM items WHERE items.Class = 'ICE CREAM'"
cmd = New MySqlCommand(sql, conn)
da.SelectCommand = cmd
'fill dataset
da.Fill(ds, "items")
rowscount = ds.Tables("items").Rows.Count
If rowscount > 0 Then 'there are records so go ahead
Dim ID As Integer
Dim desc As String
Dim newbutton As New Button
Dim newCode As New TextBox
For i As Integer = 0 To rowscount - 1
ID = ds.Tables("items").Rows(i).Item("ItemCode")
desc = ds.Tables("items").Rows(i).Item("Description")
newCode.Name = "mnuCode" & i
newCode.Text = ID
newCode.Visible = False
Me.Controls.Add(newCode)
newbutton.Name = "mnuButton" & i
newbutton.Text = desc
newbutton.Size = New Size(150, 100)
newbutton.BackColor = Color.Orange
newbutton.ForeColor = Color.White
newbutton.Font = New Font("Arial", 16, FontStyle.Bold)
newbutton.TextAlign = ContentAlignment.MiddleCenter
newbutton.Text = Regex.Unescape(desc)
newbutton.Top = (150 + (i * 100))
fPanel.Refresh()
AddHandler newbutton.Click, AddressOf ButtonClicked
fPanel.Controls.Add(newbutton)
Next
End If
ds.Reset()
conn.Close()
Catch ex As MySqlException
Finally
conn.Dispose()
End Try`
You are not creating multiple buttons. You just keep changing the properties on the same button over and over again. Since Button is a reference type, you need to call New each time you need a new object instance
For instance, instead of this:
Dim newbutton As New Button
For i As Integer = 0 To rowscount - 1
' ...
fPanel.Controls.Add(newbutton)
Next
Do this:
For i As Integer = 0 To rowscount - 1
Dim newbutton As New Button
' ...
fPanel.Controls.Add(newbutton)
Next
Or:
Dim newbutton As Button = Nothing
For i As Integer = 0 To rowscount - 1
newbutton = New Button
' ...
fPanel.Controls.Add(newbutton)
Next
Related
I have these codes retrieving data from mysql database and display it into the dynamic panels, labels, and buttons. The problem is I don't know how to retrieve those texts from labels(lblProductName) to display it into message box when clicking the dynamic buttons(btnAddto). TIA!
Imports MySql.Data.MySqlClient
Module mdlMenu
Dim Cpanel As Panel
Dim lblProductName As Label
Dim btnAddto As Button
Dim count As Integer
Public Sub LoadMenu()
Try
SQLConnection()
With sqlcom
.CommandText = "SELECT * FROM tblproduct"
.Connection = sqlconn
End With
Dim datareader As MySqlDataReader = sqlcom.ExecuteReader
Do While datareader.Read()
Cpanel = New Panel()
With frmMenu
count = .TableLayoutPanel1.Controls.OfType(Of Panel)().ToList().Count
Cpanel.Location = New Point(10, (25 * count) * 7)
Cpanel.Size = New Size(450, 160)
Cpanel.Name = "Cpanel" & count
Cpanel.AutoScroll = True
Cpanel.BackColor = Color.White
.TableLayoutPanel1.Controls.Add(Cpanel)
lblProductName = New Label()
lblProductName.Location = New Point(165, 10)
lblProductName.AutoSize = True
lblProductName.Name = "lblProd" & count
lblProductName.Text = datareader.Item("ProductName").ToString & count
lblProductName.Font = New Font(lblProductName.Font.FontFamily, 14, FontStyle.Bold)
Cpanel.Controls.Add(lblProductName)
btnAddto = New Button()
btnAddto.Location = New Point(180, 115)
btnAddto.Size = New Size(80, 40)
btnAddto.Name = count
btnAddto.Text = count
btnAddto.Tag = count.ToString
btnAddto.Font = New Font(btnAddto.Font.FontFamily, 10, FontStyle.Bold)
AddHandler btnAddto.Click, AddressOf btnAddto_Click
Cpanel.Controls.Add(btnAddto)
count += 1
End With
Loop
Catch ex As Exception
MsgBox(ex.Message)
Finally
sqlconn.Close()
sqlconn.Dispose()
End Try
End Sub
There are a couple of concepts here:
How to reference a dynamic control
How to bind an event to a dynamic control
Regarding number 1 on how to reference a dynamic control, it looks like you are declaring a form level variable and setting the value. So you should be able to reference lblProductName after it is set in the LoadMenu method. However, if you did not want to create a form level variable, you could simply use the Controls.Find method (documentation).
Regarding number 2 on how to bind an event to a dynamic control, you would use the AddHandler statement (documentation), which it looks like you are already doing here:
AddHandler btnAddto.Click, AddressOf btnAddto_Click
So what would the btnAddto_Click method look like putting it all together?
Private Sub btnAddto_Click(sender As Object, e As EventArgs)
Dim button = DirectCast(sender, Button)
Dim count = If(button.Tag IsNot Nothing, button.Tag.ToString(), String.Empty)
Dim labels = Controls.Find("lblProd" & count, True)
If (labels.Length > 0) Then
Dim label = labels(0)
MessageBox.Show(label.Text)
End If
End Sub
I have a DataGridView that is styled with code and is using data from a SQLite Database
The Database is NOT bound to the DataGridView. A number of events are triggered when I click on a row.
First the Database is updated with today's date.
And the cell that contain's that date reflects the change.
I then call a sort on the column based on the cells value. With this code
dgvLinks.Sort(dgvLinks.Columns(3), ListSortDirection.Ascending)
The process works as expected with no issues IF I omit these lines of code from the Sub Routine ViewSearches() that populates the DataGridView.
If rowCount <= 25 Then
maxRowCount = 25 - rowCount
For iA = 1 To maxRowCount
dgvLinks.Rows.Add(" ")
Next
End If
I can use these empty rows if I make a call to repopulate the DataGridView with ViewSearches()
I am trying to avoid this design as it seems like a over use of resource.
The ERROR that is happening is the 4 rows that contain data are moved to the bottom of the DataGridView and above these 4 rows with data are the empty rows. I will post the relevant code below.
My question How can I keep the empty rows and populate DataGridView so the rows with data are at the top of the DataGridView?
Here is a Screen Shot after I selected LID 2. It is updated and bubbled to the bottom of the DGV.
Private Sub ViewSearches()
Dim intID As Integer
Dim strChannelName As String
Dim strLinkAddress As String
Dim strLastVisit As String
Dim strLinkType As String
Dim rowCount As Integer
Dim maxRowCount As Integer
'Dim emptyStr As String = " "
Using conn As New SQLiteConnection($"Data Source = '{gv_dbName}';Version=3;")
conn.Open()
Using cmd As New SQLiteCommand("", conn)
'cmd.CommandText = "SELECT * FROM LinkTable"
' Line of CODE Above works with If statement in While rdr
'==========================================================
'cmd.CommandText = "SELECT * FROM LinkTable WHERE ytSiteType = 'News'"
cmd.CommandText = "SELECT * FROM LinkTable WHERE ytSiteType = #site"
cmd.Parameters.Add("#site", DbType.String).Value = gvSLT
Using rdr As SQLite.SQLiteDataReader = cmd.ExecuteReader
'dgvLinks.DataSource = rdr
'Statement Above use when DB is bound to dgvLinks
'=================================================
While rdr.Read()
intID = CInt((rdr("LID")))
strChannelName = rdr("ytChannelName").ToString
strLinkAddress = rdr("ytLinkAddress").ToString
strLastVisit = rdr("ytLastVisit").ToString
strLinkType = rdr("ytSiteType").ToString
'If strLinkType = gvSLT Then
dgvLinks.Rows.Add(intID, strChannelName, strLinkAddress, strLastVisit)
rowCount = rowCount + 1
'End If
End While
dgvLinks.Sort(dgvLinks.Columns(3), ListSortDirection.Ascending)
End Using
If rowCount <= 25 Then
maxRowCount = 25 - rowCount
For iA = 1 To maxRowCount
dgvLinks.Rows.Add(" ")
Next
End If
End Using
End Using
'FindEmpty()
End Sub
Click Event with Update to Database
Private Sub dgvLinks_CellClick(sender As System.Object, e As System.Windows.Forms.DataGridViewCellEventArgs) Handles dgvLinks.CellClick
selRow = e.RowIndex
If e.RowIndex = -1 Then
gvalertType = "4"
frmAlert.ShowDialog()
Exit Sub
End If
'Dim col As DataGridViewColumn = Me.dgvLinks.Columns(e.ColumnIndex)
Dim row As DataGridViewRow = Me.dgvLinks.Rows(e.RowIndex)
If row.Cells(2).Value Is Nothing Then
gvalertType = "5"
frmAlert.ShowDialog()
Return
Exit Sub
ElseIf gvTxType = "View" Then
webPAGE = row.Cells(2).Value.ToString()
siteID = CInt(row.Cells(0).Value.ToString())
UpdateSiteData()
''MsgBox("Stop " & selRow)
'dgvLinks.ClearSelection()
'dgvLinks.Refresh()
'dgvLinks.RefreshEdit()
Process.Start(webPAGE)
'dgvLinks.Columns.Clear()
''dgvLinks.Rows.Clear()
''ViewSearches()
ElseIf gvTxType = "Delete" Or gvTxType = "Update" Then
gvID = CInt(row.Cells(0).Value)
gvSiteName = row.Cells(1).Value.ToString
gvSiteURL = row.Cells(2).Value.ToString
frmADE.Show()
Close()
End If
End Sub
Update Routine
Public Sub UpdateSiteData()
Dim dateToday = Date.Today
dateToday = CDate(CDate(Date.Today).ToString("M-d-yyyy"))
Using conn As New SQLiteConnection($"Data Source = '{gv_dbName}';Version=3;"),
cmd As New SQLiteCommand("UPDATE LinkTable SET ytLastVisit = #ytLastVisit WHERE LID =" & siteID, conn)
conn.Open()
cmd.Parameters.Add("#ytLastVisit", DbType.String).Value = dateToday.ToString("M-d-yyyy")
cmd.ExecuteNonQuery()
dgvLinks.Rows(selRow).Cells(3).Value = dateToday.ToString("M-d-yyyy")
'Line of code above INSERTS value in Last Visit Column at the correct ROW
'NOT needed if you reload data from the database
'=========================================================================
'dgvLinks.Refresh()
'dgvLinks.RefreshEdit()
dgvLinks.Sort(dgvLinks.Columns(3), ListSortDirection.Ascending)
End Using
End Sub
You will see a number of things I have tried commented out.
As I said I can FIX the issue if I make a call to the ViewSearches() Sub Routine.
Private Sub StyleDGV()
'Sets Design of the DataGridView
'===============================
dgvLinks.DefaultCellStyle.Font = New Font("Times New Roman", 13.0F, FontStyle.Bold)
dgvLinks.ColumnCount = 4
dgvLinks.Columns(0).Width = 60 'ID
dgvLinks.Columns(1).Width = 325 'Site Name 325
dgvLinks.Columns(2).Width = 860 'Site Url 860
dgvLinks.Columns(3).Width = 154 'LastVisit 140
'Option with no blank rows increase col count to 5
'OR increase width of col(3) WHY? because the scroll bar is not showing
' TOTAL Width 1450 Height 488
'=============================
'To Set Col Header Size Mode = Enabled
'To Set Col Header Default Cell Styles DO in Properties
'dgvLinks.Columns(6).DefaultCellStyle.Format = "c"
dgvLinks.ColumnHeadersHeight = 10 'Sans Serif 'Tahoma
dgvLinks.ColumnHeadersDefaultCellStyle.Font = New Font("Sans Serif", 12.0F, FontStyle.Bold)
dgvLinks.ColumnHeadersDefaultCellStyle.ForeColor = Color.Blue
dgvLinks.DefaultCellStyle.BackColor = Color.LightGoldenrodYellow
'DGV Header Names
dgvLinks.Columns(0).Name = "LID"
dgvLinks.Columns(1).Name = "Site Name"
dgvLinks.Columns(2).Name = "Site URL"
dgvLinks.Columns(3).Name = "Last Visit"
dgvLinks.Columns(0).SortMode = DataGridViewColumnSortMode.NotSortable
dgvLinks.Columns(1).SortMode = DataGridViewColumnSortMode.NotSortable
dgvLinks.Columns(2).SortMode = DataGridViewColumnSortMode.NotSortable
dgvLinks.Columns(3).SortMode = DataGridViewColumnSortMode.NotSortable
End Sub
Any one following this question the FIX that permitted keeping the empty rows was to just omit the sort command in the Update to Database Sub Routine
As far as getting the data from the SQLite DB into the grid… you almost have it in the ViewSearches method. The command text looks good; however you are using an SQLiteDataReader. This is resorting to reading the data line by line.
I suggest you use the SQLiteDataAdapter instead. With it you can get the DataTable from the DB in one shot. First create and initialize a DataSet, then create a new SQLiteDataAdapter then add your command to the data adapter something like…
DataSet ds = new DataSet();
using (SQLiteDataAdapter sqlDA = new SQLiteDataAdapter()) {
conn.Open();
sqlDA.SelectCommand = cmd.CommanText;
sqlDA.Fill(ds, “tableName”);
if (ds.Tables.Count > 0) {
//return ds.Tables[“tableName”];
dgvLinks.DataSource = ds.Tables[“tableName”];
}
}
This will add a DataTable to the DataSet ds called “tableName” if the query succeeded.
Then simply use that DataTable as a DataSource to the grid… something like…
dgvLinks.DataSource = ds.Tables[“tableName”];
I'm new to vb.net so I'm seeking for help,
I have method to create buttons in panel layout, and what I want is to set the TEXT for each button from database table ( but the customer TEXT ),
so how can I do it?
this is the method to generate buttons:
Private Sub GenerateTable(columnCount As Integer, rowCount As Integer)
'Clear out the existing controls, we are generating a new table layout
TableLayoutPanel1.Controls.Clear()
'Clear out the existing row and column styles
TableLayoutPanel1.ColumnStyles.Clear()
TableLayoutPanel1.RowStyles.Clear()
'Now we will generate the table, setting up the row and column counts first
TableLayoutPanel1.ColumnCount = columnCount
TableLayoutPanel1.RowCount = rowCount
For x As Integer = 0 To columnCount - 1
'First add a column
TableLayoutPanel1.ColumnStyles.Add(New ColumnStyle(SizeType.AutoSize))
For y As Integer = 0 To rowCount - 1
'Next, add a row. Only do this when once, when creating the first column
If x = 0 Then
TableLayoutPanel1.RowStyles.Add(New RowStyle(SizeType.AutoSize))
End If
'Create the control, in this case we will add a button
Dim cmd As New Button()
cmd.Width = 200
cmd.Height = 40
Dim count As Integer = TableLayoutPanel1.Controls.OfType(Of Button)().ToList().Count
fill_combobox
cmd.Name = "button_" & (count + 1)
cmd.Text = "button_" & (count + 1)
AddHandler cmd.Click, AddressOf Button_Click
Panel1.Controls.Add(cmd)
'cmd.Text = String.Format("({0}, {1})", x, y)
'Finally, add the control to the correct location in the table
TableLayoutPanel1.Controls.Add(cmd, x, y)
Next
Next
End Sub
this is how to call all customers TEXT or id as list of strings from database :
Dim myList As New List(Of String)()
Private Sub fill_combobox()
Try
If SQL.conn.State = ConnectionState.Open Then
SQL.conn.Close()
End If
SQL.conn.Open()
Dim cmd As New SqlCommand()
cmd.Connection = SQL.conn
cmd.CommandText = "SELECT cust_id FROM customers order by date"
Dim dr As SqlDataReader = cmd.ExecuteReader
While dr.Read
Dim esection As String = dr.Item(0)
myList.Add(String.Format(esection))
End While
dr.Close()
SQL.conn.Close()
Catch ex As SqlException
MsgBox(ex.Message, MsgBoxStyle.Critical, "SQL Error")
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Critical, "General Error")
End Try
End Sub
Im elaborating on Subazs' comment. It could be easier to create a collection or List(of Button) then add all of your buttons into this as you itterate them into the flowtable. You can then call back to these at a later date, they will be stored in the list until you dispose of the form. NOTE they are with the form if you code it like this.
I have altered your code slightly too, you must remember to dispose the cmd as well as closing the connection (I had an awful experience not doing this which cost me!)
Dim myList As New List(Of String)
Dim myButtons As List(Of Button)
Public Sub Run_Me()
Retrieve_Customers()
'You can change the 5 and it will change the layout panel for you... :)
GenerateTable_and_Buttons(5)
MsgBox("All done!")
End Sub
'Replaces your (Fill_Combo_Box) method
Private Sub Retrieve_Customers()
Try
myList = New List(Of String)
If Sql.conn.State = ConnectionState.Open Then Sql.conn.Close()
Sql.conn.Open()
'Do you want to add in the customer name instead of ID? - if so we can change here...
Dim qry As String = "SELECT cust_id FROM customers order by date"
Dim cmd As New SqlCommand(qry, Sql.conn)
Dim dr As SqlDataReader = cmd.ExecuteReader
While dr.Read
Dim s As String = dr.Item(0)
myList.Add(String.Format(s))
End While
dr.Close()
cmd.Dispose()
Sql.conn.Close()
Catch ex As SqlException
MsgBox(ex.Message, MsgBoxStyle.Critical, "SQL Error")
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Critical, "General Error")
End Try
'Now we have built a list of the customer - myList we can go on to count these...
End Sub
Private Sub GenerateTable_and_Buttons(columnCount As Integer)
If myList Is Nothing Then Exit Sub 'Just exit if we have no customers?
myButtons = New List(Of Button)
'This will round UP to make sure we have enough rows - also can do something like +1 on end if want extra?
Dim rowCount As Integer = CInt(Math.Round(Math.Ceiling(myList.Count / columnCount), 0))
Dim tlp As TableLayoutPanel = Me.TableLayoutPanel1
'Clear out the existing controls, we are generating a new table layout
tlp.Controls.Clear()
'Clear out the existing row and column styles
tlp.ColumnStyles.Clear()
tlp.RowStyles.Clear()
'Now we will generate the table, setting up the row and column counts first
tlp.ColumnCount = columnCount
tlp.RowCount = rowCount
Dim count As Integer = 0
For x As Integer = 0 To columnCount - 1
'First add a column
tlp.ColumnStyles.Add(New ColumnStyle(SizeType.AutoSize))
For y As Integer = 0 To rowCount - 1
'Next, add a row. Only do this when once, when creating the first column
If x = 0 Then
tlp.RowStyles.Add(New RowStyle(SizeType.AutoSize))
End If
'Create the control, in this case we will add a button
Dim cmd As New Button
cmd.Width = 200
cmd.Height = 40
'<<Add the customer ID into the TAG of the control, the tag is great place to store things
'We can then access the tag in another method, ie if we want to open it using your Button_Click
'method. We can say :
'Dim CustID As String = sender.Tag
'Then we can do other things with the ID....>>
If mylist.count > count then
cmd.Tag = myList(count)
cmd.Text = myList(count)
end if
'fill_combobox() ' I dont know why this is here?
cmd.Name = "button_" & (count + 1)
cmd.Text = "button_" & (count + 1)
AddHandler cmd.Click, AddressOf Button_Click
myButtons.Add(cmd)
'<<<<<<<<<<<<<<<<<
Panel1.Controls.Add(cmd) '<<<<<<<<< Whats Panel1????
'>>>>>>>>>>>>>>>>>>
'Finally, add the control to the correct location in the table
tlp.Controls.Add(cmd, x, y)
count += 1
Next
Next
End Sub
When I maximise a form in my project and open another form from it (using a button), I'm getting an unusual glitch which I can only explain with the use of the screenshots below.
In this image, I have got an order screen and a freight screen. The order screen is maximised into an MDI screen when I hit the 'Add Freight' button, then upon opening the freight management screen, it is setting both forms to WindowState.Normal, which I'm doing programmatically and I understand why it sets both forms to normal window state, however, as you can, it is leaving a trace of the order screen behind in maximised view.
To explain more clearly, the forms have been changed from WindowState.Maximized to WindowState.Normal, and in the image the form in the top left corner is fine, but the order form is still showing in both normalised and maximised, but the maximised window doesn't exist, it disappears when you move the other screens around.
The strangest thing about this is that I'm using the same code to open other windows in the exact same way elsewhere in the program yet this glitch isn't happening.
There are only 2 "parent" forms which do this when opening other forms from within them.
Has anyone else ever had this issue? What is a way around it? This also happens even if I open the other form in maximised mode, so it's not as simple as just opening it in maximised view.
The code to open the form in the example is the following:
ugProducts.UpdateData()
Try
Dim dt As New DataTable
dt = ugProducts.DataSource
Dim ordnum As Integer
Dim osql As New OleDbCommand
osql.CommandType = CommandType.Text
osql.Connection = con
osql.CommandText = "SELECT [Order_Number] FROM [Order_Freight] WHERE [Order_Reference] = ?"
osql.Parameters.AddWithValue("#onum", lblReference.Text.ToString)
ordnum = Convert.ToInt32(osql.ExecuteScalar)
Dim ORef As String
ORef = lblReference.Text
Dim FNumber As Integer
Dim fsql As New OleDbCommand
fsql.CommandType = CommandType.Text
fsql.Connection = con
fsql.CommandText = "SELECT [Freight_Number] FROM [Order_Freight] WHERE [Order_Reference] = ?"
fsql.Parameters.AddWithValue("#fnum", lblReference.Text.ToString)
FNumber = Convert.ToInt32(fsql.ExecuteScalar)
Dim PC As String
Try
PC = ugProducts.ActiveRow.Cells("Product_Code").Text.ToString
Catch
PC = ugProducts.Rows(0).Cells("Product_Code").Text.ToString
End Try
Dim SC As String
Try
SC = ugProducts.ActiveRow.Cells("Supplier_Code").Text.ToString
Catch
SC = ugProducts.Rows(0).Cells("Supplier_Code").Text.ToString
End Try
Dim f As New frmEditFreight(con, dt, Me, cmbCustCode.Text, ordnum, ORef, FNumber, PC, SC)
f.MdiParent = Me.MdiParent
f.Show()
Catch ex As Exception
errorLog(ex)
End Try
Then, on the form that is loaded, I have this
Me.Location = New Point(0, 0)
Me.WindowState = FormWindowState.Normal
txtTotal.ReadOnly = True
lftable = New DataTable
If isNewOrder = False Then
Try
For Each dc As DataColumn In lineTable.Columns
If dc.ColumnName = "Order_Number" OrElse dc.ColumnName = "Product_Code" OrElse dc.ColumnName = "Supplier_Code" Then
lftable.Columns.Add(New DataColumn(dc.ColumnName, dc.DataType))
End If
Next
Dim product As String = ""
Dim supplier As String = fOrder.cmbSupplier.Text
Dim ds As New DataSet
Dim da As New OleDbDataAdapter("SELECT * FROM [Order_Freight] WHERE [Order_Number] = ?", con)
da.SelectCommand.Parameters.Add("#num", OleDbType.Integer).Value = orderNum
da.Fill(ds)
Dim nDt As New DataTable
nDt = ds.Tables(0).Copy()
For Each row As DataRow In nDt.Rows
product = row.Item("Product_Code")
For Each dr As DataRow In lineTable.Rows
If dr.RowState <> DataRowState.Deleted Then
If dr.Item("Product_Code") = product Then
dr.Delete()
Else
End If
Else
If dr.Item("Product_Code", DataRowVersion.Original) = product Then
dr.Delete()
Else
End If
End If
Next
Next
For Each dr As DataRow In lineTable.Rows
If dr.RowState <> DataRowState.Deleted Then
dr.Item("Order_Number") = orderNum
End If
Next
Me.ugProducts.DataSource = lineTable
For Each dc As UltraGridColumn In ugProducts.DisplayLayout.Bands(0).Columns
Dim cName As String = dc.ToString
Select Case cName
Case "Product_Code"
dc.Hidden = False
Case "Order_Number"
dc.Hidden = False
Case "Supplier_Code"
dc.Hidden = False
Case Else
dc.Hidden = True
End Select
Next
loadAddresses(custCode, con)
dtEstDelivery.Value = Date.Today
ugProducts.DisplayLayout.Bands(0).Override.CellClickAction = CellClickAction.RowSelect
selectedTable.Columns.Add("Product_Code")
selectedTable.Columns.Add("Supplier_Code")
selectedTable.Columns.Add("RowIndex")
freightTable = New DataTable
With freightTable.Columns
.Add("Freight_Number")
.Add("Address_Code")
.Add("Est_Delivery")
.Add("Product_Code")
.Add("Freight_Desc")
.Add("Freight_Val", GetType(Decimal))
.Add("Supplier_Code")
.Add("freeDelivery", GetType(Boolean))
End With
ugFreight.DataSource = freightTable
ugSelected.DataSource = selectedTable
ugSelected.DisplayLayout.Bands(0).Override.CellClickAction = CellClickAction.RowSelect
Catch ex As Exception
errorLog(ex)
End Try
Else
Try
Me.ugProducts.DataSource = lineTable
For Each dc As UltraGridColumn In ugProducts.DisplayLayout.Bands(0).Columns
Dim cName As String = dc.ToString
Select Case cName
Case "Product_Code"
dc.Hidden = False
Case "Product_Description"
dc.Hidden = False
dc.Header.Caption = "Description"
Case "Supplier_Code"
dc.Hidden = False
dc.Header.Caption = "Supplier"
Case Else
dc.Hidden = True
End Select
Next
loadAddresses(custCode, con)
dtEstDelivery.Value = Date.Today
ugProducts.DisplayLayout.Bands(0).Override.CellClickAction = CellClickAction.RowSelect
selectedTable.Columns.Add("Product_Code")
selectedTable.Columns.Add("Supplier_Code")
selectedTable.Columns.Add("RowIndex")
freightTable = New DataTable
With freightTable.Columns
.Add("Freight_Number")
.Add("Address_Code")
.Add("Est_Delivery")
.Add("Product_Code")
.Add("Freight_Desc")
.Add("Freight_Val", GetType(Decimal))
.Add("Supplier_Code")
.Add("freeDelivery", GetType(Boolean))
End With
ugFreight.DataSource = freightTable
ugSelected.DataSource = selectedTable
ugSelected.DisplayLayout.Bands(0).Override.CellClickAction = CellClickAction.RowSelect
Catch ex As Exception
errorLog(ex)
End Try
End If
bLoading = False
Followed by some ordinary stuff, setting DataTables, loading in data, etc.
The other form which is having the issue has this code:
Try
Dim grid As UltraGrid = DirectCast(sender, UltraGrid)
Dim lastElement As UIElement = ugRates.DisplayLayout.UIElement.LastElementEntered
Dim rowElement As RowUIElement
If TypeOf lastElement Is RowUIElement Then
rowElement = DirectCast(lastElement, RowUIElement)
Else
rowElement = DirectCast(lastElement.GetAncestor(GetType(RowUIElement)), RowUIElement)
End If
If rowElement Is Nothing Then Return
Dim row As UltraGridRow = DirectCast(rowElement.GetContext(GetType(UltraGridRow)), UltraGridRow)
If (row Is Nothing) Then Return
Dim MousePosition As Point = grid.PointToClient(Control.MousePosition)
If Not lastElement.AdjustableElementFromPoint(MousePosition) Is Nothing Then Return
Select Case row.Cells("cType").Value
Case "Acquisition Rate"
Dim supplier As String
If rbtnAllSuppliers.Checked = True Then
supplier = row.Cells("Supp_Code").Value
Else
supplier = cmbSupplier.Text
End If
Dim f As New frmCommission(con, row.Cells("Comm_Code").Value, supplier, True)
f.MdiParent = Me.MdiParent
f.Show()
With the rest of the select case below. The form being opened in this case then has the code
Me.Location = New Point(0, 0)
Me.WindowState = FormWindowState.Normal
Me.Cursor = Cursors.Default
isUpdate = False
Me.ugComm.ContextMenuStrip = cmCommRate
With updateDT.Columns
.Add("Apply", GetType(Boolean)).DefaultValue = False
.Add("Product_Code")
.Add("Commission_Rate")
.Add("Multiplier")
End With
btnCustomerSearch.Enabled = False
Try
Dim da As New OleDb.OleDbDataAdapter
Dim ds As New DataSet
sql = "SELECT * FROM [System Settings]"
da = New OleDb.OleDbDataAdapter(sql, con)
Dim dt As New DataTable
da.Fill(dt)
If dt.Rows(0).Item("enableAqDate") = False Then
rbtnAcq.Enabled = False
End If
If cCode <> "" And sCode <> "" Then
cmbSupp.Text = sCode
cmbSupp_ValueChanged(sender, New EventArgs)
End If
disableControls()
cmbSupp.Refresh()
txtCommCode.Refresh()
ugComm.Refresh()
cmbSupp.Enabled = False
isLoad = False
If isEnquiry = True Then
txtCommCode.Text = cCode
loadEnquiry()
ugComm.Enabled = True
cmdAddRange.Enabled = False
cmdUpdateRange.Enabled = True
cmbSupp.Value = sCode
txtCommCode.Text = cCode
End If
Catch ex As Exception
errorLog(ex)
End Try
When trying to insert the code suggested for making dpiaware = True, I've not been able to see anywhere in the code that this would go?
The culprit seems to be setting the WindowState property to Normal inside the Load event of a mdi child form when active mdi child form is maximized.
At this time the target form is in some intermediate state - the event is called as part of the window handle creation, the handle is already created, Visible property is True (but the form is not really shown on screen), WindowState returns Maximized and trying to set it to Normal leads internally to ShowWindow API call.
It's hard to tell where exactly is the bug (there are a lot of internal flags and windows messages handling inside the Form class), but the end effect is that the MDIClient and the currently active mdi child form are not correctly invalidated, hence the visual effect you are experiencing.
With that being said, there are several workarounds:
(1) At the form being shown side:
Move the Me.WindowState = FormWindowState.Normal to the Shown event.
(2) At the caller side:
(A) Add Me.WindowState = FormWindowState.Normal before f.Show()
or
(B) Add Me.MdiParent.Invalidate(True) after f.Show()
or
(C) Add Me.MdiParent.Refresh() after f.Show()
My personal preference would be the option (1) since it encapsulates a logic which is specific to the called form inside that form.
I have a bunch os items in lst1 which I want to put into lst2 but without repeating them on each ListBox.
Interface I'm using:
This is working, but you may need it to understand my doubt.
Dim dtTa_Enc As DataTable = New DataTable("Ta_Enc")
Dim dsTa As DataSet = New DataSet("Ta_E")
Dim adapter As New MySqlDataAdapter
Dim ds As DataSet = New DataSet
adapter.SelectCommand = New MySqlCommand
adapter.SelectCommand.Connection = connection
adapter.SelectCommand.CommandText = query
connection.Open()
adapter.Fill(ds, "tables")
connection.Close()
lst1.DataSource = ds.Tables("tables")
lst1.DisplayMember = "name"
lst1.ValueMember = "codta"
dtTa_Enc.Columns.Add("codta")
dtTa_Enc.Columns.Add("name")
dsTa.Tables.Add(dtTa_Enc)
lst2.DataSource = dsTa.Tables("Tables")
lst2.DisplayMember = "name"
lst2.ValueMember = "codta"
dtTa_Enc.Rows.Add(lst1.ValueMember, lst1.GetItemText(lst1.SelectedItem))
Doubt:
Now, The user presses a button to add his selected item of lst1 to lst2. Easy! However, what if he tries to add the same item. Can VB.Net stop him from doing it?
If not dtTa_Enc.find("codTa = " + lst1.valuemember) Then
dtTa_Enc.Rows.Add(lstTabelas.ValueMember, lstTabelas.GetItemText(lstTabelas.SelectedItem))
End If
Under the Add event handler, you could place some logic at the begining to prevent adding the repeated item.
Dim lst1Selected As String = CType(lst1.SelectedItem, DataRowView)("name").ToString
Dim flag As Integer = 0
For Each item As Object In lst2.Items
Dim istr As String = CType(item, DataRowView)("name").ToString
If istr = lst1Selected Then
flag = 1
Exit For
End If
Next
If flag = 1 Then
Exit Sub
End If
You can enumerate through your lst2 to determine whether any of the values match your selected item, lst1.SelectedItem.
So loop,
Dim found As Boolean = False
For Each itm As String in lst2.Items
If itm = lst1.SelectedItem Then
found = True
End If
Next
If Not found Then
'Add it.
End If
Or you can simply use the Contains method:
If Not lst2.Items.Contains(lst1.SelectedItem) Then
'Add it.
End if.