Dynamically Launching A WinForm Via Object Factory
This article is focused on answering a simple yet not-so-easy to answer question. “How do instantiate an object when the class I want to instantiate is determined at runtime, not compile time?” If you’re wondering why you would want to do that in the first place, this article provides a real world reason why as well.
A project I have been assisting a client’s development team on required a list of canned reports be available for the user to select from. A report selection form (frmReportListing) has a list box containing all the reports is displayed when the user selects “Reports” from the application toolbar. The list box is populated from records in a Reports table where each record contains information about a specific report. So far so good.
Continuing, each report has its own filtering criteria, selecting a beginning & ending date for one report and maybe selecting a specific product category for inventory analysis for another. In order to provide the user with the ability to specify the reporting filtering criteria, each report has a filtering criteria form that provides the user the ability to filter the data in the report. Again, so far so good.
Where things begin to get interesting is that the structure of the Reports table looks like this:
Title (displayed to the user in the report selection form list box)
FormName (name of the selection criteria form to display when the report is selected)
The issue that needed to be solved was how to create an instance of a form class based on its name being retrieved from a Report table record, frmSalesReport for example. One approach that works would be to use a Select Case statement to determine which form to launch:
Select Case lstReports.Text Case "Sales Report" Dim oCriteriaForm As New frmSalesReportCriteria Case "Inventory Analysis Report" Dim oCriteriaForm As New frmInventoryAnalysisCriteria 'Continue adding Case statements for each report End Select oCriteriaForm.ShowDialog()
While this does work, the problem here is that you would have to expand the Case statement with each new canned report added to the application. Again, the above works, but we can do better.
The objective is to be able to add new records to the reports table without having to make any changes to the frmReportListing form. We wanted to have code that would logically work like this:
'Assign the form name to a string from a field or wherever. I’m hard 'coding form name here for simplicity sake Dim CriteriaFormName As String = "frmSalesReport" 'Instantiate a form based on the value in the string above Dim oCriteriaForm as New [FormName] <— This is what we want to accomplish oCriteriaForm.ShowDialog()
Just because I know someone will email me saying “I tried to put the form name in brackets like you did but it doesn’t work”. I know… The above code will not compile. It is only used to illustrate what I’m trying to accomplish. Ultimately the goal is to retrieve the form name to instantiate from somewhere and then dynamically instantiate it.
The solution we opted for implements a factory design pattern. You pass in the name of the class to instantiate to the CreateAnObject method and it returns an instance of that class.
Imports System.Reflection Public Class ObjectFactory 'Code contributed by Rod Paddock (Dash Point Software) 'www.dashpoint.com Public Shared Function CreateAnObject(ByVal ObjectName As String) As Object Dim Assem = [Assembly].GetExecutingAssembly() Dim myType As Type = Assem.GetType(ObjectName.Trim) Dim o As Object = Nothing Try o = Activator.CreateInstance(myType) Catch oEx As TargetInvocationException MessageBox.Show(oEx.ToString) End Try Return o End Function End Class
A simple Messagebox.Show() call is placed in the catch to keep things simple. How to handle an exception is not the focus of this article.
In order to use the ObjectFactory above you just pass a string in the format “assembly.classname” like this:
Dim oForm As Form = _ ObjectFactory.CreateAnObject("MyApplication.frmSalesReport") oForm.Show()
In the above code “MyApplication” is the assembly name for the project and “frmSalesReport” is the name of the report filtering criteria form being displayed. Note though that while I used the object factory to create a form, you can pass in any type to the factory and have it instantiate an object of that type for you.
In our project we simply concatenated the name of the project assembly with the form name of the selected report and passed that into the object factory like this:
'Grab the form name from the array used to populate the list box Dim ReportFormToShow As String = _ "ProjectAssemblyNameHere." + oReports(0).Item(1) 'Pass the form name into the object factory and show the form Dim oForm As ReportForm = ObjectFactory.CreateAnObject(ReportFormToShow.Trim) oForm.ShowDialog()
Go ahead and give it a shot.
1) Create a WinForm application named MyApplication.
2) Create a new class named ObjectFactory and copy the class code above into it.
3) Create a new form named frmTwo and change its height and width to distinguish it a bit.
4) Add a button to Form1 and add this code in the code behind:
5) Run the application and click on the button on Form 1. It should launch form frmTwo.
Another real world usage scenario might be capturing the name of the last form used by a user and have that form automatically displayed the next time the user opens the application. For example, consider an accounting application with forms like frmVendors, frmCustomers, frmDeposits and so on. When the user exits the application the name of the active form could be stored in a table or an XML file or anywhere else you choose to store that information. The next time the user logs on to the application the last used form name value is retrieved and then passed to the object factory to create an instance of that form. The uses of this technique are endless.
Hopefully this will help you create more flexible and dynamic applications.
A HUGE thank you goes out to my friend, software development business partner, Yoda to my Luke Skywalker, CoDe Magazine Editor, and Microsoft MVP Rod Paddock for his help with this article. I knew exactly what I wanted to accomplish and he knew exactly how to accomplish it. It’s good to have friends you can rely on for help. Thanks a ton Rod!
About Jim Duffy:
Jim Duffy is founder and president of TakeNote Technologies, an award-winning training, consulting, and software development company specializing in .NET software developer training and helping clients create business solutions with Microsoft enterprise. Jim's expertise is with Visual Studio, Visual Basic, ASP.NET, SQL Server and Visual FoxPro-to-.NET conversions. He has a BS degree in Computer and Information Systems and over 25 years of programming and training experience. He is an energetic trainer, skilled developer, and has been published in leading developer-oriented publications.
Jim is a Microsoft Regional Director, a Microsoft MVP award recipient since 2003, an INETA speaker, and is an entertaining and popular speaker at regional user groups and international developer conferences. He is also a co-host of Computers 2K9, a call-in radio show on WRBZ (AM 850), 850 The Buzz, in Raleigh, NC.