Create and loop through collection subset of controls - vb.net

Im making a small vb.net windows form application in which I have 4 ComboBoxes. I would like to add the ComboBoxes to a collection and be able to loop through that collection to refer to each one.
There are other ComboBoxes on the form so I cannot just use the collection for the entire form (the form layout cannot be changed, e.g. to add a container, etc).
I was thinking something like the following:
Public Class Form1
Dim IoTypeCombos As New ControlCollection(Me) From {Me.IO1_ComboBox, Me.IO2_ComboBox, Me.IO3_ComboBox, Me.IO4_ComboBox}
Dim IoTypes As New Collection() From {"Out 0", "Out 1", "Input", "Analog"}
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
For Each cb As combobox In Me.IoTypeCombos
FillComboBox(cb, Types)
Next
End Sub
Function FillComboBox(cb As Control, cc As Collection) As Boolean
Dim cbc As ComboBox = CType(cb, ComboBox)
If cc.Count = 0 Then
Return False
End If
For Each cn In cc
cbc.Items.Add(cn)
Next
Return True
End Function
This doesn't raise any exception, BUT it doesn't populate the ComboBoxes either :(
The FillComboBox() works perfectly if I pass a single control to it.
What am I doing wrong? Thanks

This line is illegal:
Public Class Form1
Dim IoTypeCombos As New ControlCollection(Me) From {Me.IO1_ComboBox,
Me.IO2_ComboBox, Me.IO3_ComboBox, Me.IO4_ComboBox }
That code will run before the constructor, before Me or ION_ComboBox exist. In this case, the resulting collection contains nothing since there is nothing to put in it yet.
In other cases, referencing controls before they exist can result in a NullReference being thrown, but due to an odd bug it may not be reported. When that happens, the rest of the code is skipped and the form simply shown.
In either case, the solution is to declare your collection at the form level, but populate it in the form load event once the controls do exist. I would also use a Collection(Of T) instead (an array or List(Of T) will also work, the OP uses/asks about a collection though):
Imports System.Collections.ObjectModel
Public Class Form1
Dim IoTypeCombos As Collection(Of ComboBox) ' form and controls Do No Exist yet
Public Sub New
'...
InitializeComponent()
' NOW they exist
End Sub
Sub Form_Load
IoTypeCombos = New Collection(Of ComboBox)
IoTypeCombos.Add(IO1_ComboBox)
IoTypeCombos.Add(IO2_ComboBox)
...
If you use a List(Of ComboBox), you can populate it different ways:
' in the ctor:
IoTypeCombos = New List(Of ComboBox)({IO1_ComboBox, IO2_ComboBox...})
' using AddRange:
IoTypeCombos.AddRange({IO1_ComboBox, IO2_ComboBox...})

Not sure if you need the where clause, but if you have other comboboxes that do not have names like this and do not want them in the collection then you do need it.
Dim IoTypeComboboxes =
Me.Controls.OfType(Of Combobox)().Where(Function(cb) cb.Name.StartsWith("IO")).ToList()

'on yourFormName
'added :
'45 PictureBox:("PicBarNum1_NotLastOdig" to "PicBarNum45_NotLastOdig")
'added:
'45 PictureBox:("PicBarNum1_UkOdig" to "PicBarNum45_UkOdig")
Public Class yourFormName
Private picbarlistNum1to45_UkOdig As New List(Of PictureBox)
Private picbarlistNum1to45_UkLastNotOdig As New List(Of PictureBox)
Private sub yourFormName_Load
Call AddPicBoxesInList_OdigNoOdig()
End sub
Private Sub AddPicBoxesInList_OdigNoOdig()
picbarlistNum1to45_UkOdig.Clear()
picbarlistNum1to45_UkLastNotOdig.Clear()
picbarlistNum1to45_UkOdig = Me.Controls(0).Controls.OfType(Of PictureBox)()
.Where(Function(pb) pb.Name.StartsWith("PicBarNum") And
pb.Name.EndsWith("_UkOdig")).ToList()
picbarlistNum1to45_UkLastNotOdig = Me.Controls(0).Controls.OfType(Of
PictureBox)().Where(Function(pb) pb.Name.StartsWith("PicBarNum") And
pb.Name.EndsWith("_NotLastOdig")).ToList()
End Sub
End Class

Related

String form name to form reference

Basically, I am rewriting some code working for years. Over the time I have many (60+) references to forms - there's a menuitem with OnClick event for each form, where a form reference was created:
Private Sub SomeForm_Click(sender As Object, e As EventArgs) Handles MenuItemForSomeForm.Click
NewTab("Some Form", New SomeForm, 0)
End Sub
...where first parameter is a name to put in a tabPage.Text where the form is opened, second is a new instance of the (particular) form SomeForm and 0 is a default record to display (0 means no default record).
Now, I created a dynamic menu and stored the form names in a database (due to better access control over the access rights, etc). Now, because the menu is generated at runtime, I can't have the OnClick event with separate instance definition of the form and have to create it at runtime, after the MenuItems are created. The side-effect idea was to cut the code short by using only 1 OnClick event or such with MenuItem.Tag paremeter as FormName. Something like:
Private Sub clickeventhandler(sender As Object, e As EventArgs)
Dim tsmi As ToolStripMenuItem = CType(sender, ToolStripMenuItem)
Dim newForm As New >>>FormFrom(tsmi.Tag.ToString)<<< ' only explanation, this won't work
MainW.OpenModuleInTab(new newForm, tsmi.Tag.ToString, 0)
However I am failing to find a way to create form (instances) from this string reference. Reference through collection (i.e. List(of) or Dictionary) would be fine too, I believe.
The structure is obviously:
Object → Form → Form1 (class) → MyForm1 (instance)
I know I can create an object like this:
' Note that you are getting a NEW instance of MyClassA
Dim MyInstance As Object = Activator.CreateInstance(Type.GetType(NameOfMyClass))
I can re-type it to a Form type:
Dim NewForm as Form = CType(MyInstance,Form)
... to acccess some of the form properties like Width, TopLevel, etc., but that's about it. I can't do:
Dim NewForm1 as Form1 = CType(NewForm,Form1)
...because obviously, Form1 comes as a string "Form1".
I don't know how to create a Form1 reference from a "Form1" text (then it would be easy to create an instance) or how to create an instance directly (MyForm1).
SOLUTION
As sugested, I used reflection to get the form. The only way working for me I found was this:
Dim T As Type = System.Type.GetType(FormName, False)
If T Is Nothing Then 'if not found prepend default namespace
Dim Fullname As String = Application.ProductName & "." & FormName
T = System.Type.GetType(Fullname, True, True)
End If
Dim f2 As New Form ' here I am creating a form and working with it
f2 = CType(Activator.CreateInstance(T), Form)
f2.TopLevel = False
f2.Name = FormName.Replace(" ", "") & Now.ToString("yyyyMMddmmhh")
f2.FormBorderStyle = FormBorderStyle.None
f2.Dock = DockStyle.Fill
I am using VB.net CallByName to set public variable and same function to run a sub method (every form contains RecordID variable and LoadRecords sub):
CallByName(f2, "RecordID", CallType.Set, 111)
CallByName(f2, "LoadRecords", CallType.Method, Nothing)
For testing purposes, I put following into the testing form:
Public RecordID As Int32
Public Sub LoadRecords()
MsgBox("Load records!!!!" & vbCrLf & "RecordID = " & RecordID)
End Sub
Activator.CreateInstance(TypeFromName("Form1"))
TypeFromName Function:
Dim list As Lazy(Of Type()) = New Lazy(Of Type())(Function() Assembly.GetExecutingAssembly().GetTypes())
Function TypeFromName(name As String) As Type
Return list.Value.Where(Function(t) t.Name = name).FirstOrDefault()
End Function
So, let's go with the idea that I have an assembly called "WindowsApp2" and in that assembly I've defined Form1 and Form2. I've also created this module in the same assembly:
Public Module Module1
Public Function GetDoStuffWiths() As Dictionary(Of Type, System.Delegate)
Dim DoStuffWiths As New Dictionary(Of Type, System.Delegate)()
DoStuffWiths.Add(GetType(WindowsApp2.Form1), CType(Sub(f) WindowsApp2.Module1.DoStuffWithForm1(f), Action(Of WindowsApp2.Form1)))
DoStuffWiths.Add(GetType(WindowsApp2.Form2), CType(Sub(f) WindowsApp2.Module1.DoStuffWithForm2(f), Action(Of WindowsApp2.Form2)))
Return DoStuffWiths
End Function
Public Sub DoStuffWithForm1(form1 As Form1)
form1.Text = "This is Form 1"
End Sub
Public Sub DoStuffWithForm2(form2 As Form2)
form2.Text = "This is Form 2"
End Sub
End Module
Now, in another assembly "ConsoleApp1" I write this:
Sub Main()
Dim DoStuffWiths As Dictionary(Of Type, System.Delegate) = WindowsApp2.Module1.GetDoStuffWiths()
Dim formAssembly = System.Reflection.Assembly.Load("WindowsApp2")
Dim typeOfForm = formAssembly.GetType("WindowsApp2.Form1")
Dim form As Form = CType(Activator.CreateInstance(typeOfForm), Form)
DoStuffWiths(typeOfForm).DynamicInvoke(form)
Application.Run(form)
End Sub
When I run my console app I get a form popping up with the message "This is Form 1".
If I change the line formAssembly.GetType("WindowsApp2.Form1") to formAssembly.GetType("WindowsApp2.Form2") then I get the message "Wow this is cool".
That's how you can work with strongly typed objects that you dynamically instantiate.
Dim AssemblyProduct As String = System.Reflection.Assembly.GetExecutingAssembly().GetName.Name
Dim FormName As String = "Form1"
Dim NewForm As Object = Reflection.Assembly.GetExecutingAssembly.CreateInstance(AssemblyProduct & "." & FormName)
If TypeOf (NewForm) Is Form1 Then
Dim NewForm1 As Form1 = CType(NewForm, Form1)
NewForm1.BackColor = Color.AliceBlue
NewForm1.Show()
End If

How to return specific properties of a custom vb.net object

I'm trying to generate a list of all of the TableName and FieldName properties for a custom object type called LxTextBox. I've gotten as far as generating a list of all of the LxTextBox names on my form, but I can't figure out a way to call the properties of the custom object... I've been looking into System.Reflection, but I haven't ever used it. Additionally, I'm returning the list to a RichTextBox while I'm testing this out, but ultimately, I need to return each objects properties as a data row. Example:
ObjectName Table Field
---------------------------------------
LxTextBox23 SomeTbl SomeFld
Here's my code to return the list - updated based on #OneFineDay...
Imports System.Collections.Generic
Imports Application.UDF.Controls
Public Class MeasurementsControl
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim textBoxList As New List(Of Control)
Dim customTbs = GetAllControls(Me)
Dim sb As New System.Text.StringBuilder
For index As Integer = 0 To customTbs.Count - 1
sb.Append(customTbs.Item(index).TableName & "." & customTbs.Item(Index).FieldName & System.Environment.NewLine)
Next
RichTextBox1.Text = sb.ToString
End Sub
Private Function GetAllControls(ByVal searchWithin As Control) As List(Of LxTextbox)
Dim returnList As List(Of LxTextbox) = Nothing
If searchWithin.HasChildren Then
returnList = searchWithin.Controls.OfType(Of LxTextbox).ToList
For Each ctrl As Control In searchWithin.Controls
returnList.AddRange(GetAllControls(ctrl))
Next
End If
Return returnList
End Function
End Class
I made the changes suggested and I'm throwing an error: OfType is not a member of System.Windows.Forms.Control.ControlCollection
FYI - Adding Imports System.Linq did not fix the error.
You are boxing it into a Control the object from where it derives, where you're custom properties cannot be found. You can cast it right from the control collection.
Dim customTbs = GetAllControls(Me)
'recursive function
Private Function GetAllControls(ByVal searchWithin As Control) As List(Of LxTextbox)
Dim returnList As List(Of LxTextbox) = Nothing
returnList = searchWithin.Controls.OfType(Of LxTextbox).ToList
If searchWithin.HasChildren Then
For Each ctrl As Control In searchWithin.Controls
Dim ctrls = GetAllControls(ctrl)
If Not ctrls Is Nothing Then returnList.AddRange(ctrls)
Next
End If
Return returnList
End Function

showing a new Form Using a public function in vb.net

In my application I have to show lot of forms when particular button/panels are clicked. so instead of writing
Frm = New formname
Frm.MdiParent = MDIParent
Frm.Show()
i want to have public function through which i can pass the form name.
for that i have written a function
Public Sub showForm(ByVal formname As Form)
Frm = New formname
Frm.MdiParent = MDIParent1
Frm.Show()
End Sub
Call showForm(myformname)
but problem with this is, it says formname is not defined
EDIT:
I updated my answer to reflect your comment that a form should only be opened once.
I want to have public function through which i can pass the form name.
for that i have written a function
Public Sub showForm(ByVal formname As Form)
You don´t pass the name of a form to your function but an object of type Form instead.
Here is one possible solution with a generic version of showForm:
Public Class FormManager
Private _formByName As New Dictionary(Of String, Form)
Public Sub showForm(Of T As {Form, New})(name As String, parent As Form)
Dim frm As Form = Nothing
If Not _formByName.TryGetValue(name, frm) OrElse _formByName(name).IsDisposed Then
frm = New T()
_formByName(name) = frm
End If
frm.MdiParent = parent
frm.Show()
End Sub
End Class
The FormManager holds a dictionary cache for all opened forms with Key=form name. This is to make sure that a form is only opened once. The check form.IsDisposed makes sure that you can close the form and reopen it.
Usage from the parent form:
Public Class Form1
Private fm = New FormManager()
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
fm.showForm(Of MyForm)("MyForm", Me)
End Sub
End Class
The first parameter is to identify the form name. The real magic is in the Type T which we made sure it is 1) of type or subtype Form and 2) it has a parameterless constructor (MyForm is just a placeholder for this example put in the type of your real form you want to show).
The parent parameter will bring you additional flexibility if it is not always MDIParent1. Remove it if you don´t neet the extra flexibility.
For sure you can also drop the FormManager class and put the showForm to another place.

Pass an object from form to another in VB

I have searched through the internet and couldn't find the answer to my problem, but, the issue is that I have 2 forms;
frm_bookManeger
and
frm_addBook
The first one is the main form and has a list of books (named listBook), a TreeView and a button to invoke the second form to add a new book.
After filling in all of the TextBoxes and information of a book, I press "Add". Then, the second form will be closed and all info of that book will be kept in an instance of Book class. The problem is: how can I pass this instance to the first form to store it in listBook.
For example:
If I create a constructor in form 1 to get form 2 then in form 2:
Dim f1 As form1 = New form1(me)
f1.Show()
f2.Close()
I can't do it because form 1 will start up instantly when I start program, and the default right now doesn't have any parameter in OnCreateMainForm():
Protected Overrides Sub OnCreateMainForm()
Me.MainForm = Global.WindowsApplication5.frm1
End Sub
How can I do it?
First form:
Public Class frm_bookManeger
'list of Book
Dim listBook As List(Of Book) = New List(Of Book)
Private frm_addBook As frm_addBook
Public Sub New(frm_addBook As frm_addBook) 'got error
Me.frm_addBook = frm_addBook
End Sub
Second form:
Public Class frm_addBook
Dim Public tempBook As Book = New Book()
'add book
Private Sub btn_add_Click(sender As Object, e As EventArgs) Handles btn_add.Click
tempBook.BookName1 = TextBox_name.Text
tempBook.Author1 = TextBox_author.Text
tempBook.Price1 = TextBox_price.Text
tempBook.Genre1 = TextBox_genre.Text
tempBook.EstablishedDay1 = dtp_established.Value.Date
Dim frm_Mngr As frm_bookManeger = New frm_bookManeger(Me)
End Sub
End Class
Dim frm As New form1
frm.textbox.Text = Me.passing value.Text
frm.Show()
or you can try
Public Class Form1
Private loginLabel As String
Public Sub New(ByVal loginParameter As String)
InitializeComponent()
Me.loginLabel = loginParameter
End Sub
End Class
dim frm as new Form1(label.Text)
Your frm_addBook needs a reference to the instance of frm_bookManeger so that it can use methods in the latter.
That can be done by passing a reference to the current instance of frm_bookManeger to the New constructor of frm_addBook.
Also, you probably want the book adding form to be a dialog form rather than an ordinary form.
I made a simple "Book" class and used a TextBox to display the books, so the first form is this:
Imports System.Text
Public Class frm_BookManager
Dim bookList As List(Of Book)
Public Class Book
Property Name As String
Property Author As String
End Class
Public Sub AddBook(b As Book)
If bookList Is Nothing Then
bookList = New List(Of Book)
End If
bookList.Add(b)
End Sub
Private Sub ShowBooks()
Dim sb As New StringBuilder
For Each b In bookList
sb.AppendLine(b.Name & " by " & b.Author)
Next
TextBox1.Text = sb.ToString()
End Sub
Private Sub btn_add_Click(sender As Object, e As EventArgs) Handles btn_add.Click
Using addBook As New frm_addBook(Me)
Dim result = addBook.ShowDialog()
If result = DialogResult.OK Then
ShowBooks()
End If
End Using
End Sub
Private Sub frm_BookManager_Load(sender As Object, e As EventArgs) Handles MyBase.Load
AddBook(New Book With {.Name = "Wuthering Heights", .Author = "Emily Brontë"})
ShowBooks()
End Sub
End Class
For the form to add a book, I added "Cancel" and "OK" buttons.
Public Class frm_addBook
Dim myParent As frm_BookManager
Private Sub bnOK_Click(sender As Object, e As EventArgs) Handles bnOK.Click
Dim b As New frm_BookManager.Book With {.Name = TextBox_name.Text, .Author = TextBox_author.Text}
myParent.AddBook(b)
End Sub
Public Sub New(parent As frm_BookManager)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
myParent = parent
' set the DialogResult for each button so the parent can tell what happened
bnCancel.DialogResult = DialogResult.Cancel
bnOK.DialogResult = DialogResult.OK
End Sub
End Class
Notice that a new Book can be added with myParent.AddBook(b) because myParent refers to an instance of frm_BookManager.
You could modify it so that the dialog stays open and has a button to just add a book and not close the dialog. I made the ShowBooks() method Private so you can't call it from outside the class it is in - you could modify that.
There are many possibilities for small modifications to the code I showed to achieve greater functionality. And I could not resist correcting the spelling of "Maneger" to "Manager" ;)
I think the easiest way would be to have the frm_addBook form have a property which will contain the book that was added. In the frm_bookManager form, show that form using ShowDialog and if the user clicks OK on that form, the property will contain the book added. Be sure to dispose the frm_addBook form after you get the book from the public property.
Public Class Book
Public Property Name As String
Public Property Author As String
End Class
Public Class frm_bookManager
Dim bookList As New List(Of Book)()
Private Sub btnAddBook_Click(sender As Object, e As EventArgs) Handles btnAddBook.Click
Using addBookForm As New frm_addBook()
If addBookForm.ShowDialog() = DialogResult.OK Then
bookList.Add(addBookForm.BookToAdd)
End If
End Using
End Sub
End Class
Public Class frm_addBook
Public Property BookToAdd As Book
Private Sub btnOK_Click(sender As Object, e As EventArgs) Handles
'User filled in the fields and clicked this OK button
Me.BookToAdd = New Book()
Me.BookToAdd.Name = txtName.Text
Me.BookToAdd.Author = txtAuthor.Text
End Sub
End Class
I would not pass the main form instance into the add book form because it would create a tight coupling between the two forms and the add book form would only be usable by the main form. You might wish to use the add book form from other forms in the app.

Bind DataSource to new DevExpress Report Designer?

I'm trying to figure out how to set my DataSource as the default when a user clicks New Report, or for any new report, in the DevExpress User Data Report Designer.
Right now, the Blank Report I have load on Form_Load has my DataSources just fine, but anytime I hit New Report, they're gone.
I've googled and followed the docs, but they all seem to be geared towards opening a specific report (as above).
Can anyone help?
0. ICommandHandler interface
You need to handle the ReportCommand.NewReport command by implementing the ICommandHandler interface. You must pass an object that implementing this interface to the XRDesignMdiController.AddCommandHandler method. You can get XRDesignMdiController object from ReportDesignTool.DesignForm.DesignMdiController property or from ReportDesignTool.DesignRibbonForm.DesignMdiController property according to what type of form you want to use.
Here is example:
Private Sub ShowReportDesigner()
Dim tool As New ReportDesignTool(CreateReport)
Dim controller = tool.DesignRibbonForm.DesignMdiController
Dim handler As New NewCommandHandler(controller, AddressOf CreateReport)
controller.AddCommandHandler(handler)
tool.ShowRibbonDesigner()
End Sub
Private Function CreateReport() As XtraReport
Dim report As New XtraReport
report.DataSource = YourDataSourceObjectHere
Return report
End Function
Public Class NewCommandHandler
Implements ICommandHandler
Private ReadOnly _controller As XRDesignMdiController
Private ReadOnly _createReport As Func(Of XtraReport)
Public Sub New(controller As XRDesignMdiController, createReport As Func(Of XtraReport))
_controller = controller
_createReport = createReport
End Sub
Public Function CanHandleCommand(command As ReportCommand, ByRef useNextHandler As Boolean) As Boolean Implements ICommandHandler.CanHandleCommand
useNextHandler = command <> ReportCommand.NewReport
Return Not useNextHandler
End Function
Public Sub HandleCommand(command As ReportCommand, args() As Object) Implements ICommandHandler.HandleCommand
_controller.OpenReport(_createReport())
End Sub
End Class
1. DesignPanelLoaded event
The another way is to subscribe to XRDesignMdiController.DesignPanelLoaded event. In this event you can check where the DataSource of report in loaded panel is empty and set it to your data source.
Here is example:
Private Sub ShowReportDesigner()
Dim report As New XtraReport
report.DataSource = YourDataSourceObjectHere
Dim tool As New ReportDesignTool(New XtraReport)
Dim controller = tool.DesignRibbonForm.DesignMdiController
AddHandler controller.DesignPanelLoaded, AddressOf mdiController_DesignPanelLoaded
tool.ShowRibbonDesigner()
End Sub
Private Sub mdiController_DesignPanelLoaded(ByVal sender As Object, ByVal e As DesignerLoadedEventArgs)
Dim panel = DirectCast(sender, XRDesignPanel)
Dim report = panel.Report
If IsNothing(report.DataSource) Then
report.DataSource = YourDataSourceObjectHere
End If
End Sub