Programmatically access controls on GroupBoxes that are on multiple Tab Pages - vb.net

How can you programmatically access controls on Tab Pages?
My code has many properties that I want to allow the user to initially set when the program starts. These properties will be used to define an Excel chart like title name, font colors, font size, chart position and size, X & Y series and many others properties. The program produces many charts each of which have their own set of properties. Each chart has the same set of properties but different values.
My solution using Form1 Designer:
I Placed the TabControl1 control from the VS 2010 toolbox on Form1.
I created 19 TabPages on TabControl1
On each TabPage I created multiple GroupBoxes for organization.
Finally, I placed multiple controls like RadioButtons, TextBoxes, and Spinners in each of the GroupBoxes.
All of the above was done at design time.
Bad Example:
Dim TC1 As TabControl = TabControl1.TabPages.Item(1)
This is a bad example but I’m thinking I need to do something like this, i.e. before I can reach the actual user inputs via the RadioButtons, TextBoxes, or Spinners child controls I need to tell the complier the name of the TabControl1 and each TabPage contained in it. I’m not sure about each of the GroupBoxes that contain groups of RadioButtons and etc.
My Question: How do you programmatically access controls located on multiple GropBoxes located on each of the Tab Pages? I want to retrieve all the user input properties values on each of many TabPages and apply them when I create the Excel Charts.

A TabControl is just a fancy way to organize controls on the same form. The point is that they are on the same form! You probably know that you use Me to access the current form, then .ControlName to access the control. Consider a TabControl with two tabs and on each tab there is a TextBox. These TextBoxes are on the same form, so you still need to use
Me.TextBox1.Text = "Text 1" ' the same as TextBox1.Text = "Text 1", but I like to be explicit
Me.TextBox2.Text = "Text 2"
This you already knew, as your problem stemmed from this behavior. However, there are some tricks to help you. Consider a TabControl with two tabs, and on page 1 there is a control named tc1TextBox1 and on page 2 tc2TextBox1. I'm using the prefix to distinguish between the pages. An additional TextBox on page 1 would be named tc1TextBox2 etc., then say you wanted CheckBoxes, you could have tc1CheckBox1 and tc1CheckBox2 etc. The way I would access them is with LINQ
Define an extension method in a module
Imports System.Runtime.CompilerServices
Public Module ExtensionMethods
<Extension()> _
Public Function ChildControls(Of T As Control)(ByVal parent As Control) As List(Of T)
Dim result As New List(Of Control)
For Each ctrl As Control In parent.Controls
If TypeOf ctrl Is T Then result.Add(ctrl)
result.AddRange(ctrl.ChildControls(Of T)())
Next
Return result.ToArray().Select(Of T)(Function(arg1) CType(arg1, T)).ToList()
End Function
End Module
This method returns you all the Controls inside parent, and all Controls inside those, and so on, recursively. It also gives you an IEnumerable List(Of Control) in contrast to Control.Controls() which is not IEnumerable and only returns controls in the control, not in its containers.
Using LINQ, you can filter the results of ChildControls by Type or Name. For instance, the code
Dim textBoxes = Me.ChildControls(Of TextBox)()
returns all the TextBoxes in Me. Let's take it a step further:
Dim textBoxes = Me.ChildControls(Of TextBox)().Where(Function(tb) tb.Name.StartsWith("tc1"))
returns all the TextBoxes with the prefix "tc1". So you see you can set up the controls on your form to better group them at runtime. Here's an idea specific for Excel sheets
Dim xlBook As Excel.Workbook
For i As Integer = 1 To 20
Dim nameString = "txtSheetName" & i.ToString() ' TextBoxes named txtSheetName1, txtSheetName2, etc.
xlBook.Sheets(i).Name = Me.ChildControls(Of TextBox)().Where(Function(tb) tb.Name = nameString).First().Text
Dim enabledString = "chkEnabled" & i.ToString() ' CheckBoxes named chkEnabled1, chkEnabled2, etc.
xlBook.Sheets(i).Enabled = Me.ChildControls(Of CheckBox)().Where(Function(tb) tb.Name = enabledString).First().Checked
'etc.
Next
which will loop over your tab control indices, and set properties for each corresponding excel sheet. This code should give you a good idea of a direction you can take.

Related

MS ACCESS VBA - best way to change all properties values of a form's textboxes at once

I have a form with 3 textboxs and i want to change their properties values according to some events.
My idea is to set these properties to a global form's variables in order to use them whenever i want with a function or module (this depends on your suggestion).
Assuming that i want to change all textboxs.enabled property by pressing a button
and i want to change another different property if needed in the future (like bordercolor),
my example is this:
in the form VBA,
Option Compare Database
Option Explicit
Private Const AllTxtboxes = *** all txtboxes ***
in the VBA button
Private Sub button1_Click()
AllTxtboxes.enabled = True
End sub
Thanks in advance.
You need to loop trough all controls and identify those you want to change. The problem is that each control type got their own properties, so you need to make sure you are in the right one.
And for that, there is a property common to all controls named Tag
There, you can specify a value and check it. It's really helpful to select only what you want.
I made a simple form with 6 textboxes:
Note that in design view, properties panel at right, tab others, last property is Tag (sometimes it comes as aditional info or something like it).
There, I typed 99 but only on textboxes 2, 4 and 6, because those are the textboxes I want to handle (that would be kind of your array of textboxes)
I added a command button to change forecolor of those textboxes to red when clicked. My code is:
Private Sub CMD_CHANGE_FONT_COLOR_Click()
Me.Painting = False
Dim MyControl As Control
Dim MyTxt As TextBox
For Each MyControl In Me.Controls
If MyControl.Tag = 99 Then
'we set MyTxt to MyControl so we can use Intellisense, not really needed, but it makes coding easier
Set MyTxt = MyControl
MyTxt.ForeColor = vbRed
Set MyTxt = Nothing
End If
Next MyControl
Me.Painting = True
End Sub
When the form loads, you will see:
But after click on command button, the forecolor of those 3 textboxes will be red:
There you go. So everytime you want to do something with that array of textboxes, you can use this code to loop and change the properties you want.

Cannot grab list box using Visual Basic in a Word document

My goal for what I am trying to do overall is have one box on a document that holds a database type and when someone select that database type the document will hide/show certain sections.
Well my issue right now is that I cannot seem to grab the the list box ContentControl I created.
I have a listbox ContentControl with a tag boxDB.
Currently this is all that I can get
Sub ListBox_AfterUpdate()
Dim box
Set box = ActiveDocument.SelectContentControlsByTag("boxDB")
End Sub
I have tried several things with box to try and get it to have data. My first assumption was that since it is a collection I would just find the first item by using the first index of the items..but it always says that there is no item at that index.
Is there another method I should be using to grab the value that was set?
In order to access the control and it's features by tag, read the reference documentation for SelectContentControlsByTag. This shows that the returned object is a collection of controls that match the tag, so clearly you might have several controls with the same tag string.
You will help yourself quite a lot by always using Option Explicit in all your code and by specifically declaring each variable with a type. That way you can use the Intellisense feature of the VBA Editor to reveal what methods and properties are available as you write your code.
Assuming that your ContentControl is already established with the tag (and that you only have one control with that tag
Option Explicit
Sub SetupListbox()
Dim theControls As ContentControls
Dim box As ContentControl
Set theControls = ThisDocument.SelectContentControlsByTag("boxDB")
Set box = theControls.Item(1)
box.DropdownListEntries.Add "Red"
box.DropdownListEntries.Add "Green"
box.DropdownListEntries.Add "Blue"
box.DropdownListEntries.Add "Yellow"
box.DropdownListEntries.Add "Orange"
End Sub
Sub GetSelectedItem()
Dim theControls As ContentControls
Dim box As ContentControl
Set theControls = ThisDocument.SelectContentControlsByTag("boxDB")
Set box = theControls.Item(1)
Debug.Print box.Range.Text
End Sub

How do I reference a dynamically named control on a UserControl from the Parent Form?

How do I reference a dynamically named control on a UserControl from the Parent Form? (In Winforms).
I have a single parent form but it may load any 1 of around 20 or so UserControls. We will call them ucA, ucB, etc.
Each UserControl has a different number of textboxes, but are named tbA01, tbA02, etc on ucA and tbB01, tbB02, etc on ucB.
How would I reference the value of the textboxes?
I cannot seem to reference the name of the UserControl directly. I know the name of the UserControl as a string, but canot seem to cast it as a control. Likewise with the textboxes on the UserControl. I am sure I can use Control.Find() for the name of the textbox from a simple string. But this doesn't appear to be working, which I assume it will only be looking for controls on ParentForm and not the collection of controls on the UserControl. I assume there would be a method using TryCast or DirectCast and using the Control.Find() in the arguments. But I have not found a solution.
Any help would be appreciated!
Thank you Jimi! Your answer did help me look at a different method in coming up with a solution. I have it working now. Here is what I used:
Dim matches() As Control
matches = Me.Controls.Find(TblName, True)
If matches.Length > 0 AndAlso TypeOf matches(0) Is TextBox Then
Dim tb As TextBox = DirectCast(matches(0), TextBox)
...
Endif

Do you have to show every tab before all textboxes actually populate?

I have a vb.net form that uses multiple textboxes across several different tabs. Within one of those tabs, I have a sub set of tabs. My save functionality calls stored procs for each tab and cycles through the values on each page to either do an update or a "add new". I noticed that while testing, some of the pages do not save or update any of the values in the textboxes. After a few days of investigating, I realized that if I edit something, then physically click through the other tabs, it all saves/updates properly. If I don't click through them, they don't all save. Is there a reason for this that I am missing? When you enter a search value, I cycle through the pages and populate them all at the same time so I was assuming it wrote those values BEFORE it physically rendered...I guess I am wrong?
From the TabPage documentation Remarks section
Controls contained in a TabPage are not created until the tab page is shown, and any data bindings in these controls are not activated until the tab page is shown.
So the answer to your question is "Yes the tabpage must be shown".
However, the definition of "shown" is subject to interpretation. In reality, all you need to do set the TabPage.Visible property to True and not actually cycle through and display each TabPage.
A recursive scan of the form for TabPage controls will work:
Private Shared Sub TabPagesVisible(parent As Control)
For Each c As Control In parent.Controls
If TypeOf c Is TabPage Then c.Visible = True
TabPagesVisible(c)
Next
End Sub
Example usage:
Sub SaveFormTabData()
TabPagesVisible(Me) ' Me refers to the containing form
' code to save control data
End Sub

VB.NET - Is there a way to take a string name of a panel and find the panel associated?

I'm working in VB.NET and I'm rather new. I'm doing a search for each button on a panel with:
For Each _x_ As Control In _y_.Controls
I have the y as a string name, is there a way to convert this string to the actual panel or controls group that is associated so I don't have to code for each individually?
Is there an easier way to do this?
i.e.
private function return_button(ByVal y As integer)
Dim z As String = R_ + ToString(y)
(z is now the panel name I am using, but it's a string not the panel itself)
All controls are referenced in their parent's Controls collection. So, if the panel you are looking for is a child of your form, you can look for it in your form's Controls collection. The Controls collection is indexed by the controls' names, so you can retrieve them easily by name, like this:
Dim z As String = ...
Dim p As Panel = DirectCast(Me.Controls(z), Panel)
I figured a solution to this issue. For whatever reason it wouldn't allow me to cast the variables to an instantiated value, it always remained null/nothing. So instead I wrote:
Dim p As String = "<String related to the panel I am looking for>"
Dim panel() = Me.Controls.Find(p, true)
Now I can prove the panels are equal by calling:
MsgBox(panel(0).GetHashCode = <panel name>.GetHashCode)
and was now certain they were the same object so I did a the for loop and it worked with the panel. Now I can loop for the entire TabPage and find all the Panels on this tab. That will make my code far more streamlined than it was prior. Thank you for setting me on the right path, Steven.