Determining ToolStripButton CheckState from ToolStrip loop - vb.net

I have a VB.Net toolstrip to which I add buttons programmatically. Some of the buttons are checked or unchecked depending on the state they were left in when the user last set up the application (from values stored in the Registry:
Dim OneButton As New ToolStripButton("T", Nothing, Nothing, "Thailandr")
OneButton.CheckOnClick = True
AddHandler OneButton.Click, AddressOf ClickHandlerLayers
tsLayers.Items.Add(OneButton)
If GetSetting(IO.Path.GetFileNameWithoutExtension(Application.ExecutablePath), "Settings", "ThailandSetting", False) Then
OneButton.PerformClick()
End If
OneButton = New ToolStripButton("W", Nothing, Nothing, "World")
OneButton.CheckOnClick = True
AddHandler OneButton.Click, AddressOf ClickHandlerLayers
tsLayers.Items.Add(OneButton)
If GetSetting(IO.Path.GetFileNameWithoutExtension(Application.ExecutablePath), "Settings", "WorldSetting", False) Then
OneButton.PerformClick()
End If
Everything works fine except that I want to save the values of the buttons back into the Registry when the user clicks the Apply button. I want to save the values by looping through the tsLayers toolstrip rather than hard coding (which is possible, but is extra work when I add more buttons). So far I can see the names an
' Save which background layers are to be used
For Each tb As ToolStripItem In tsLayers.Items
Debug.Print(tb.Name)
Debug.Print(tb.GetType.ToString)
Debug.Print(tb.Selected)
Debug.Print(tb.Pressed)
Next
Results are:
Thailand
System.Windows.Forms.ToolStripButton
False
False
World
System.Windows.Forms.ToolStripButton
False
False
even if one of the buttons is pressed/checked at the time of looking at the results. I can't see any other properties that can help me, nor any collections that I can burrow into.
Is there a way to determine the checkState of a toolstripbutton in a toolstrip by looping through the toolstrip?

Cast the ToolStripItem as a ToolStripButton and you'll have access to the CheckState property
If tb.GetType Is GetType(ToolStripButton) Then
Dim tbCast As ToolStripButton = DirectCast(tb, ToolStripButton)
Debug.Print(tbCast.CheckState)
End If

Related

Loop - set Textboxes to ReadOnly in all Groupboxes

I have groupboxes that contain textboxes and I want to set all of them to ReadOnly=True. Here is what I tried (doesn't work):
EDIT (I forgot about Split container):
For Each SplitCon As Control In Me.Controls
If TypeOf SplitCon Is SplitContainer Then
For Each GBox As Control In SplitCon.Controls
If TypeOf GBox Is GroupBox Then
For Each ctrl As Control In GBox.Controls
If TypeOf (ctrl) Is TextBox Then
CType(ctrl, TextBox).ReadOnly = True
End If
Next
End If
Next
End If
Next
You can simplify things a great deal. Rather than looking thru all sorts of Control collections, create an array to act as a ToDo list. Then you can get rid of all the TypeOf and CType in the loop used.
' the ToDo list
Dim GrpBoxes = New GroupBox() {Groupbox1, Groupbox2,
Groupbox3, Groupbox4}
For Each grp In GrpBoxes
For Each tb As TextBox In grp.Controls.OfType(Of TextBox)()
tb.ReadOnly = True
Next
Next
Your code no longer depends on the form layout of the moment. The only thing you have to remember is to add any new GroupBox items to your list. You can also declare the array once ever for the whole form if you prefer (or even in the For Each statement)
Rather than working with Control objects, Controls.OfType(Of T) filters the collection and returns an object variable of that type, so there is no need to cast it or skip over controls you are not interested in. You can also tack on a Where method to further refine the list to include only do those with a certain name or Tag.

How do I iterate through Components on a Windows Form?

I'm using VisualStudio 2008 on a system using .NET Compact Framework 3.5 in VB.NET and have a form that contains a couple of Components, namely Timers and Messageboxes. Part of my application is a sub that iterates through all the Controls in the form and adjusting properties like the Front or BackColor so that we can reskin the entire application on demand.
While I'm iterating through these Controls I'd also like to iterate through the Components and set some properties of the Messageboxes on the form. I've tried using a For Each to access Me.components.Components but that collection appears to be private.
For Each comp As Object in Me.components
<do something>
Next comp
Is there a way to iterate through the components?
EDIT:
I was wrong thinking that me.components.Component is private. Using Me.components.Components I get the following error:
'Components' is not a member of 'System.ComponentModel.IContainer".
You can loop throught timers with this: ....
For Each c As Object In Me.components.Components
If TypeOf c Is Timer Then
Dim tim As Timer = CType(c, System.Windows.Forms.Timer)
tim.Interval = 12345
End If
Next
Change the .Interval = 12345 with anithing you want to do to the timers.
.... And through other controls with:
For Each ctrl As Control In Me.Controls
If (ctrl.GetType() Is GetType(TextBox)) Then
Dim txt As TextBox = CType(ctrl, TextBox)
txt.BackColor = Color.LightYellow
End If
Next
Change the TextBox with the type of control
And .BackColor = Color.LightYellow with anything you want to do with the control.
Unfortunatly i don't know how to do is with MesseageBoxes :(

DataGridView DataBindingComplete doesn't fire until after doing something in the form

In this form, there is a tab control. A new tab page is added for each table in the Data Set. Then to each tab page a Data Grid View is added for displaying the contents of the corresponding table.
This is the form's load event...You can see where I set the Data Source for the data grid view being added:
Public Sub dlgCreateTables_Load(sender As Object, e As System.EventArgs) Handles MyBase.Load
bindingComplete = False
For i = 0 To binData.BinnedTables.Tables.Count - 1
Dim tabPage As TabPage = New TabPage
tabPage.Text = binData.BinnedTables.Tables(i).TableName
tabTables.TabPages.Add(tabPage)
dgvData = New DataGridView
With dgvData
.Dock = DockStyle.Fill
.AllowUserToOrderColumns = False
.AllowUserToAddRows = False
.AllowUserToDeleteRows = False
.DataSource = binData.BinnedTables.Tables(i)
.Columns.Add("Total", "Total")
.Columns("Total").ReadOnly = True
End With
tabPage.Controls.Add(dgvData)
'These following lines have to go after dgvData is added to tabPage because otherwise they don't work
dgvData.AutoResizeColumnHeadersHeight()
For Each col As DataGridViewColumn In dgvData.Columns
col.SortMode = DataGridViewColumnSortMode.NotSortable
Next
'call handler for summing the columns for each row to make Total column once binding is complete
AddHandler dgvData.DataBindingComplete, AddressOf dgvData_Created
'And this to show row headers in the row selectors (but not the triangle when row is selected)
AddHandler dgvData.RowPostPaint, AddressOf MyDGV_RowPostPaint
AddHandler dgvData.KeyDown, AddressOf dgvData_KeyDown
AddHandler dgvData.CellValueChanged, AddressOf dgvData_CellEdit
Next 'DataTable In binData.BinnedTables.Tables
End Sub
For whatever reason, the DataBindingComplete event does not fire until after the form is fully loaded and I do something like click a button or change a value in the grid.
I have a different solution in which I do the same thing and it works in there. I can't for the life of me find anything different, or figure out why dgvData_Created isn't hit until something else happens.
Note that the contents of the tables in the Data Set to which the grids are bound are created via a separate module. Currently I'm just filling everything with a "2" to get this issue working.
UPDATE:
Per Saragis's suggestion, I moved the line to add the Handler for the DataBindingComplete event up to just after instantiating the DataGridView (before "With dgvData"). Now it works in that dgvData_Created does fire when adding dgvData to the tab page, and my table is there and looks great.
However, If there is > 1 table in my data set, the DataBindingComplete event only seems to fire for the first table. After the 1st grid is done, it goes back to adding the 2nd grid but skips DataBindingComplete, and goes to RowPostPaint after the form Load is complete.
As a result, the first grid's row headers are not painted until after I force a repaint (such as by going to a different tab then coming back). And any grids after that do not have the column header row sized right (though they do show the row headers).
How can I at least force a repaint programmatically for that first grid (since that is the one I open the form to)?? I tried .Refresh(), but that didn't work.

DataGridView bound to DataTable is not showing

I am trying to show a DataGridView in a form that is bound to a DataTable, but it's not showing up. I was doing this using a C1TrueDBGrid and that was working...I decided to switch to using a DataGridView due to some complications with the TrueDBGrid. Can anyone help me figure out why nothing is showing?
In the form I declare these:
Public binData As DataSet
Friend WithEvents dgvData As System.Windows.Forms.DataGridView
The binData is filled with tables created via a separate calculation routine. Then this is the form load event:
'create a tab page and add data grid view for every table in the set
For i = 0 To binData.Tables.Count - 1
Dim tabPage As C1.Win.C1Command.C1DockingTabPage = New C1.Win.C1Command.C1DockingTabPage
tabPage.Text = binData.Tables(i).TableName
tabContent.TabPages.Add(tabPage)
Dim dgvData = New System.Windows.Forms.DataGridView
Dim binding As New BindingSource
binding.DataSource = binData.Tables(i)
With dgvData
.Dock = DockStyle.Fill
.AllowUserToOrderColumns = False
.AllowUserToAddRows = False
.AllowUserToDeleteRows = False
.DefaultCellStyle.Alignment = DataGridViewContentAlignment.BottomLeft
.DataSource = binding
.AutoGenerateColumns = True
End With
tabPage.Controls.Add(dgvData)
Next 'DataTable In binData.Tables
When the form loads, the tab pages are there and labeled as expected, but they look empty (no table).
I did try instead setting the DataSource to the DataSet called binData (as opposed to a specific table), and then setting dgvData's DataMember property to the name of the specific table I want to display in it...that made no difference.
Note: I need to be able to do this programmatically at runtime as opposed to using the visual designer because I do not know the exact number of grids I need until the form loads with a particular dataset - the dataset it gets can have a different number of tables depending on what the user wants.
Here's some rough code to add dgvs to a flow panel:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Static dgvCount As Integer
dgvCount += 1
Dim dgvNew As New DataGridView
dgvNew.Width = DataGridView1.Width
dgvNew.Height = DataGridView1.Height
dgvNew.Name = "dgv" & dgvCount
' clone other properties as need
FlowLayoutPanel1.Controls.Add(dgvNew)
Debug.Print(FlowLayoutPanel1.Controls(FlowLayoutPanel1.Controls.Count - 1).Name)
End Sub
Starts with one dgv - DataGridView1 as the model for properties. Anchoring is not working in the flow panel so some custom code may be need to change width.
Flow panel doesn't scroll so may not be the best choice - look into TableLayout as a possibility. TabControl is another option.
OK, well it turns out there was nothing wrong with what I was doing. The issue turned out to be in one line of code that has nothing to do with binding the DGV's datasource or anything.
ComponentOne has a control called a ThemeController in which you can set themes for your forms and the controls within. I had a line of code to set the theme for dgvData to my default application theme (which sets visual style and details regarding colors, fonts, etc.). For whatever reason, THAT was rendering my grid non-visible. I will be logging a ticket with them.

combobox recalling previous datasource

Looking for some insight here...
On my form, I have three ComboBox controls. When the left most receives a selection, it calls a routine to enable and populate the 2nd, then the same happens for the 3rd when a selection is made in the 2nd.
Private Sub cboEquipment_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles cboEquipment.SelectedIndexChanged
Me.transData.isMobile = If(Me.cboEquipment.Text = MOBIEQIP, 1, 0)
If cboMembership.Enabled = False Then
Me.cboMembership.Enabled = True
End If
Call loadGroups()
End Sub
The method 'loadGroups'
Private Sub loadGroups()
Dim cn As New SqlConnection(My.Settings.MyConnectionString)
Dim cm As New SqlCommand("SELECT ... ORDER BY codeHumanName ASC", cn)
Dim ta As New SqlDataAdapter(cm)
Dim dt As New DataTable
Me.cboMembership.DataSource = Nothing
Me.cboMembership.Items.Clear()
Me.transData.membership = Nothing
cn.Open()
ta.Fill(dt)
With Me.cboMembership
.DataSource = dt
.DisplayMember = "codeHumanName"
.ValueMember = "codeID"
End With
cn.Close()
End Sub
This works fine an dandy, everything loads at should, and provides the appropriate .ValueMember results.
I should mention that when the Form first loads, boxes 2 and 3 are empty and disabled. After their initial load, I have to select an item from the list to get values (as expected).
The odd part is that once the 2nd or 3rd Combobox have been 'initialized', they seem to recall last settings. For example, if I call the Reset method, which sets the .DataSource property to Nothing, clear the list, and set the Text to Nothing; the next time I change the SelectedIndex on cboEquipment, the other boxes come back to life with SelectedItems and SelectedValues.
These values aren't wrong according to the new DataTable, but it is odd that they remember at all (they shouldn't have any reference, and behave as a fresh load).
Other points of interest; the populate methods are called during the SelectedIndexChanged event.
Any thoughts would be great!
UPDATE -
This behavior is tied in with the SelectedIndexChange Event bouncing around (raise! raise! raise!)
Removing and adding event handlers at strategic points has my code working as I want/expect it to.
Here is the article that put me on the right path...
On your second and third combo boxes try clearing the .Text/.SelectedText properties before you set the .DataTable
The combo box is probably just being too helpful and assuming that when you set the .DataTable a second time, you'd want the selected item to match your .Text property.
I ended up using AddHandler and RemoveHandler to prevent chain-firing events as the comboboxes were being populated.