How to reference controls located on different Tabs (VB.NET) - vb.net

I have an application written in VB.NET that reads data from a file and displays the data on the screen.
Depending on the data in the file, the program has a TabControl with up to 3 tabs and each tab in turn has a DataGridView for displaying data. For example I have a TabControl that has a tab called "Saturday" and a tab called "Sunday".
The problem I am having is that when I read data from a file, the program displays all the data on the Saturday's tab grid because I am not sure how to reference the Grid on the Sunday tab.
To add the DataGridView I am using the following code:
Grid = New DataGridView
Grid.Dock = DockStyle.Fill
Grid.Name = "Grid" & TabControl.SelectedIndex
Grid.Tag = "Grid" & TabControl.SelectedIndex
And this is how I am reading the data in:
If reader.GetAttribute("controltype") = "Tab" Then
SelectedTab = reader.Name
End If
If reader.Name = "cell" Then
y = y + 1
Grid.Rows(i).Cells(y).Style.BackColor = Color.FromName(reader.ReadElementString("cell"))
End If
What I almost want to do is something like (pseudocode):
SelectedTab.Grid.Rows(i).Cells(y).Style.BackColor = Color.FromName(reader.ReadElementString("cell"))
However when I use the above code it complains:
'Grid' is not a member of 'String'
I hope you understand the issue. Let me know if you need clarification

Your code is a little unclear. However, it appears to me that the following line:
If reader.GetAttribute("controltype") = "Tab" Then
SelectedTab = reader.Name
End If
is creating at least one problem. It looks like you are attempting to refer to a Tabpage control by the string representation of its name, but unless I missed something, what that line is actually doing is trying to make a tabpage control type("SelectedTab") refer to a string type. If that is the case, then you will want to try this instead:
If reader.GetAttribute("controltype") = "Tab" Then
TabControl1.SelectedTab = TabControl1.TabPages(reader.name)
End If
It is a little hard to tell from the code you have posted, but that might get you headed down the right path.
++++++++++++
UPDATE: It appears from your code that you are naming each DGV control by appending the index of the tab on which it is located to the string "grid." I am going to assume that you are using a class member variable named "SelectedTab" to represent the current tab selected in the control. I will assume that at the top of your class you have done something like this:
'Form-or-class scoped memebr variables:
Private SelectedTab As TabPage
Private SelectedGrid As DataGridView
You should be able to refer to the active grid control using something like this:
Private Sub TabControl1_SelectedIndexChanged(sender As Object, e As System.EventArgs) Handles TabControl1.SelectedIndexChanged
' Set SelectedTab member variable to refer to the new selected tab page:
SelectedTab = TabControl1.SelectedTab
' Set the SelectedGrid to refer to the grid control hosted on the selected tab page:
SelectedGrid = TabControl1.SelectedTab.Controls("Grid" & TabControl1.SelectedIndex.ToString())
End Sub
From here, you should be able to use the member variable for SelectedGrid to refer to the grid present on which ever tab page is selected in your tab control.
It is challenging to address your concerns with only fragments of your code. If you have additional difficulties, please post more of your code, so we can better see what else is going on.
Hope that helps!

Okay, I would go about something like this. Maybe you can simply use a DataSet to load the XML data in one line (if they have been saved with DataSet.WriteXML before).
Dim ds As New DataSet
Dim p As TabPage
Dim gv As DataGridView
ds.ReadXml("F:\testdata.xml")
For i As Integer = TabControl1.TabPages.Count - 1 To 0 Step -1
TabControl1.TabPages.RemoveAt(i)
Next
For Each dt As DataTable In ds.Tables
p = New TabPage(dt.TableName)
gv = New DataGridView
' ... configure the gv here...
gv.AutoGenerateColumns = True
gv.Dock = DockStyle.Fill
' ...
gv.DataSource = dt
TabControl1.TabPages.Add(p)
p.Controls.Add(gv)
Next

Related

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.

Creating a new instance of an existing control? (a bit more complicated than it sounds)

I know that if I want to dynamically create a new instance of an existing form, I can simply do this...
Dim NewForm As New ExistingForm
... and a new instance of all of the controls from ExistingForm will be available in NewForm.
But what if I want to create a new instance of an existing control that's inside a separate existing form?
More specifically, let's say I have 2 forms - FormA and FormB. Inside FormB is an existing panel (we'll call it ExistingPanel). And inside ExistingPanel are several buttons, text boxes, etc., arranged exactly how I want them.
OK, so... inside a button's click event in FormA, I want to do something like this (it doesn't work, obviously, but should give you a good idea as to what I'm trying to accomplish)...
Dim NewExistingPanel As New FormB.ExistingPanel
And then I want NewExistingPanel to contain new instances all of the individual controls from ExistingPanel (so that I can dynamically manipulate each one individually).
So basically, my program will need to create several NewExistingPanels at runtime. And I will need to access each one (and all of its child controls) individually, after they have been created.
How can I accomplish this? I've been experimenting with this for several hours with no luck so far, so figured I'd break down and ask the real professionals :)
When you create a new user control, you could use a dictionary with the tag value as key and the control name as value. Then you can access the control directly by using the IndexOfKey method.
Dim NewForm2 As New Form2
Dim ControlIndex As New Dictionary(Of String, String)
For I = 0 To 2
Dim NewPanel As New WindowsControlLibrary2.MyCustomPanel
NewPanel.Name = "Panel-" + I.ToString
NewPanel.Location = New Point(0 + (NewPanel.Size.Width * I), 0)
NewPanel.Tag = "MyTagValue" + I.ToString
ControlIndex.Add(NewPanel.Tag, NewPanel.Name)
NewForm2.Controls.Add(NewPanel)
NewForm2.Update()
Next
NewForm2.Show()
Dim CurrentPanel As WindowsControlLibrary2.MyCustomPanel =NewForm2.Controls(NewForm2.Controls.IndexOfKey(ControlIndex("MyTagValue1")))
To add control such as panel with textboxes ang button in it ..
In your project - Add > Usercontrol -->
in this part you can make panel with textboxes and button as you want
name it as "myPanel" and save it as "myPanel.vb" --> or whatever you want
In your form and the button named button1 you can add to your button1_click event :
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim mypanel1 As New MyPanel
mypanel1.Location = New Point(0, 0)
Controls.Add(mypanel1)
mypanel1.TextBox1.Text = "Hi, Im new"
End Sub
And you will get your panel at runtime ...
And you will get the textbox1.text fill with "Hi, I'm new" ...
You could create a user control that lays out your sub-controls the way you want them, then create new instances of that. Add New > User Control in the solution menu, then build, and your controls should be ready to drag into the designer at the top of the Toolbox or to instantiate in code like any other control.
To expand on my comment:
Private somePanels As New Dictionary(Of String, YourUserControl)
Then you can just add them as follows:
Dim c As New YourUserControl()
…
Me.Controls.Add(c)
somePanels.Add("SomeTag", c) ' Instead of using Tag; if your tags are numbers, use a List instead of a Dictionary
And access them as:
Dim c = somePanels("SomeTag")

vb.net - click on listview subitem and open window

I'm not sure if this is possible after doing a bunch of googling, but hopefully it is. I have an application that pulls a list of information from a MySQL database and populates a listview (Unfortunately, I can't change to a datagrid at this time.) What I'm tasked to do is make it so that when clicking on a certain column, a window will open and, based on the ID of the row that was clicked on, retrieve another set of results from the same database.
The list view is created as such:
Do While result.Read()
Dim siteid = (result.Item("idsite").ToString())
Dim sitename = (result.Item("name").ToString())
Dim last_import_date = (result.Item("import_finished").ToString())
Dim last_import_file = (result.Item("file_name").ToString())
Dim last_line = (result.Item("last_line").ToString())
Dim status = (result.Item("status").ToString())
Dim lv As ListViewItem = ListView1.Items.Add(siteid)
lv.SubItems.Add(sitename)
lv.SubItems.Add(last_import_date)
lv.SubItems.Add(last_import_file)
lv.SubItems.Add(last_line)
lv.SubItems.Add(status)
Loop
So preferably I'd like to click on "Last_import_file" and have that open the window. I've tried a bunch of ItemClicked type commands, but haven't had much luck.
Is what I'm attempting possible? I don't need any special text formatting, just want to register the click and pop open the dialog.
Thanks!
Yes it is possible. To do this in a Listview it is a bit more complicated than a DataGridView. You'll need to make use of the ListViewHitTestInfo class. Using the MouseDown Event of your listview, use this code:
Private Sub ListView1_MouseDown(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles ListView1.MouseDown
Dim info As ListViewHitTestInfo = ListView1.HitTest(e.X, e.Y)
MsgBox(info.Location.ToString())
If Not IsNothing(info.SubItem) Then
'info will contain the information of the clicked listview column. You can then go through it's subitems for more information, if any.
End If
End Sub

Add tooltip control dynamically

I have a child form that is completely created in code.
I would like to add tool tips to the textbox controls on the form. I know how to set up the tool tips on the fly but can't find a way to add the tooltip control to the form on the fly. All the hits I find on google refer to dragging the control from the designers toolbox.
I would need to do something like:
' Add tool tip control
Dim toolTip1 As New ToolTip()
toolTip1.ShowAlways = True
frm.Controls.Add(toolTip1)
This does not work
I tried adding a sub to handle the from.load event(with a handler that poitn to the sub) at design time but can not get passed the error "Tooltip1 is not declared" when adding the tooltip to the iundividual dynamic controls.
If I call this dynamic form from a parent form and add the tooltip control to the parent form I can use it for the child form. But how would I do this if I was creating the form from a routine that does not live on a parent form?
thanks
Dim frm As New Form
' Add tool tip control
''Dim toolTip1 As New ToolTip()
''toolTip1.ShowAlways = True
'Draw the Form object
'close the dynamic frm if existing already
If frm IsNot Nothing Then
frm.Close()
End If
frm = New Form()
frm.AutoScaleDimensions = New System.Drawing.SizeF(6.0F, 13.0F)
frm.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
frm.Name = "frm_test"
'dimension is irrelevant at the moment
frm.ClientSize = New System.Drawing.Size(10, 10)
'the parent will be the current form
'frm.MdiParent = this;
'splash screen mode form, why not...
frm.ControlBox = True
frm.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
frm.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink
frm.BackColor = System.Drawing.Color.LightGray
For Each item As MYFILE.Class in the Collection
Dim aTextBox As New TextBox()
aTextBox.Font = New System.Drawing.Font(sFont, Single.Parse(sSizeFont), System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CByte(0))
aTextBox.BackColor = System.Drawing.Color.Yellow
aTextBox.Location = New System.Drawing.Point(iNewColumnPosition + 5 + intMaxWidthLabel, intVertPos)
aTextBox.Size = New System.Drawing.Size(intWidthTextBox + 10, intGapHeight)
'store the biggest width, so that the textboxes can be vertically aligned
If intWidthTextBox > intMaxWidthText Then
intMaxWidthText = intWidthTextBox
End If
'giving a name to all your object will be the only way
'to retrieve them and use them
'for the purpose of this sample, the name can be the
'same for all textboxes.
aTextBox.Name = item.ParameterName
'giving the maximun size in caracters for the textbox.
aTextBox.MaxLength = Integer.Parse(item.ParameterLength)
toolTip1.SetToolTip(aTextBox, "TIP" & intIndex.ToString)
'tab have to be ordered
aTextBox.TabIndex = intIndex
intIndex += 1
'Vertical position is to be manage according the
'tallest object in the form, in this case the
'textbox it self
intVertPos += intGapHeight
'adding the textbox to the form
frm.SuspendLayout()
aTextBox.SuspendLayout()
frm.Controls.Add(aTextBox)
Next
I left a lot of code out but this should give you an idea as to what I am doing
In vb it is
Dim tooltip As New ToolTip(components)
tooltip.SetToolTip(textBox1, "This is a textbox tooltip")
Sorry this is not in VB.NET but I am pretty sure you can easily convert this. The first line is to set ToolTip control to your form components. The second is how you set a tooltip to a control and give it the related text.
ToolTip tooltip = new ToolTip(components);
tooltip.SetToolTip(textBox1, "This is a textbox tooltip");

should changing the parent of a listbox change the selected index?

You wouldn’t think so, but it does when the listbox is bound to a datasource (as far as I can see).
I’ve reduced the behaviour to the code below. The "if" line toggles between loading a list via data binding and loading a list “manually” (both use the same data table). In each case I set the selected index afterwards, and then change the parent form. With manual loading, the selected index is retained, with binding it is lost. I cannot see how this makes any sense – I don't see why changing the host form should alter any property of the list. Is this a bug?
Public Class Form1
Sub main() Handles Me.Load
Dim ListControl1 As ListBox = New ListBox
ListControl1.Parent = Me
Dim dt = New DataTable
dt.Columns.Add("intColourID")
dt.Columns.Add("strName")
dt.Rows.Add({1, "Red"})
dt.Rows.Add({2, "Green"})
dt.Rows.Add({3, "Blue"})
ListControl1.ValueMember = dt.Columns(0).ColumnName
ListControl1.DisplayMember = dt.Columns(1).ColumnName
If False Then
ListControl1.DataSource = dt
Else
For i = 0 To dt.Rows.Count - 1
ListControl1.Items.Add(dt.Rows(i)("strName").ToString)
Next
End If
ListControl1.SelectedIndex = 2
Dim z As Form = New Form
ListControl1.Parent = z
z.Show()
End Sub
End Class
The correct way of adding a control to a form is not to set its parent, but to add it to the Controls collection of the form. If I do it like this I do not get an exception (the three last lines commented out as you write in your comment).
Me.Controls.Add(ListControl1) ' Instead of ListControl1.Parent = Me