Suppose you have three different clients and they want to use different Form/UI for a single specific task. Let’s say it’s about calculating their bills. But those Forms are developed in different Windows Form Projects and there is no way to add those forms in your project at the time of development (Quite hard to believe, but we are guessing..right?) So, how can you incorporate/open one of those Forms according to the user’s choice as a startup of Your project?
Or, let say it differently, you need to add/decide which Form to open using your configuration file without using the if/else block in your code. Then what’s the idea?
If we look deeper, let’s say, the project is highly extensible and any form (with specific type) can be added as a startup form without rebuilding your whole application solution. How can you do that?
We can do it using Dependency Injection. From Wikipedia:
"Dependency injection is a software design pattern that allows a choice of component to be made at run-time rather than compile time. This can be used, for example, as a simple way to load plugins dynamically or to choose mock objects in test environments vs. real objects in production environments. This software design pattern injects the dependent element (object or value etc) to the destination automatically by knowing the requirement of the destination. There is another pattern called Dependency Lookup, which is a regular process and reverse process to Dependency injection."
We will apply some very basics of Dependency Injection in this part.
- First we need to make the starting point of our application independent of any specific Form name or class referenced in our project.
- Then we will make our starting point to read the Form name from the configuration file, instantiate that Form and open that Form.
Lets do it using the below steps. I will describe necessary information in the relevant steps. My target audience is almost anyone who can understand the C# language, so it will be very elaborate in some cases.
I have given the download link of the project at the bottom of the post.
We will create a Blank Solution using Visual Studio 2012. Naming it as Calculator.
Delete the Form1.cs File from the Solution Explorer and remove the code from the Main method of the Program.cs class. After these tasks, the solution will look like below.
Next build the solution and make up to this state error free.
Now, we can see Calculator.MainProject solution doesn't have any Form, and we need to create another Windows Form project to use the Form from another DLL.
Lets add another project Calculator.Standard into this solution.
Now delete the Program.cs file from the Calculator.Standard project. Why are we doing this? It is because we will create a DLL which will have the Forms in it. We don't want to execute the Forms from the Calculator.Standard project. We will use the DLL to another project. In this case, the 'another project' is Calculator.MainProject project. We need also specify the Calculator.Standard project to act as normal class library project instead of Windows Forms project. Below are the steps.
Some of you might question why are we adding another project into the solution instead of creating a separate project? Well, as long as we are not adding the reference of the Calculator.Standard's DLL into the Calculator.MainProject, it makes no difference whether we make separate project or using the same solution of the two projects. I hope it is clear with you.
Next, we should rename the Form1.cs and name it as StandardForm.cs
Now we again build the solution and make sure there is no error left in the solution.
Next, the big task, add the Unity Container's DLL into the project. To do so, we should use Nuget Package. Below are the steps how can we do that.
There will be a green icon after successful installation/addition of the Unity into our project.
You will see the DLLs under the References Tree of the Calculator.MainProject.
So far, we have created a MainProject without any kinds of Windows Forms in it, a Standard project as a class library project. And we have also installed the Unity into our MainProject.
Now we will create a configuration file where we will define the necessary configuration information into the MainProject so that our Program.cs file can read those information and use those at the time of running.
Below are the steps to create and add the information into the configuration file.
We will add a '.config' file into the Calculator.MainProject.
To add the unity part into the config file, we need to know the version of the DLL of the unity we are using.
The version i got is 2.1.505.0
Now lets create a section in the App.config file like the following image. Change the boxed value according to your version value.
Then we will create the main part of the configuration file which will contain the information of the DLLs.
The tag name must be the same as the section name of the unity declaration section. This main section will contain two sub part. One is TypeAlias and the other is Container.
In the TypeAlias section we will create individual alias of the DLLs we want to use in our project.
In the Container section we will define which DLL should be called if call a class from our MainProject. We will elaborate this more shortly. So, the brief of this part is below.
And with attributes the section will looks like below.
Now we need to fill the values in the attributes.
We can see, in the typeAlias tag, there are two attributes. One is alias and the other is type. Alias is the value which we will call as a key and the type is the value of that key. In the 'key', we will add the fully qualified class name and the Assembly Information of that DLL so that we can call and instantiate that class using that DLL.
So, since we have Calculator.Standard as a class library, so we will use its DLL in our project and add the necessary information in the config file.
Fully qualified class name is "Calculator.Standard.StandardForm"
Assembly information will be seen from the following image.
And the values will be added in the config file as below.
Please check that we also added a value in the mapTo attribute inside of the type tag under types section of the container section. It is because we want to call the alias 'Standard' if the type attribute is called from the code.
For example, lets say the tag type is
This means, there will be a alias of Gateway in the typeAlias section of the config file. Suppose the Gateway alias's DLL is added into the project as reference. So, what the Unity will do is, it will check for the mapTo attribute where the type is written as Gateway. It will return the instance of the StudentGatway alias' class.
So, we should select a class which will be used in our Calculator.MainProject and we will map that class with our Calculator.Standard project's class using Unity.
Since, System.Windows.Forms.Form class is the base of every Windows Form class, we can use this in our MainProject. So the config file will be like the following.
Next we will define the names of the container and the type so that we can call those names from our code. In this case, it is CalculatorContainer and CalculatorForm as displayed in the image.
So, the total code of the config file will looks like the following.
Now our task is to create a method which will read the config file, instantiate an object which will be written in the mapTo attribute and return it to the caller. Please keep in mind that, at the development time, we don't know which Form will come at the run time. So, we should apply the Inheritance idea here. If we cast the object we got at runtime (of course it will be a Windows Form, and all Windows Form is a Form according to the Inheritance) in Form and return that object to the Main method it should open that Form successfully.
The code will be like below.
The required namespaces we will use using are below.
The above code is self explanatory. But the string values will come from according to the following picture.
So, the total code we will be using are given below.
If we press Debug at this stage, the following exception should be occurred.
Its because we don't have the Standard project's DLL inside of the Debug folder of the MainProject nor having the Windows Forms DLL as well.
So, what we do is, we should copy those DLLs and paste it in the Debug folder of the MainProject. Please keep in mind that we will not Build any project from now so that we can understand we can open a external Windows Form without rebuilding the executable file.
To copy the Windows Forms DLL, we should find it first. We can copy that using the following steps.
To copy the Calculator.StandardProject's DLL we will do the followings.
After pasting those DLLs, the Debug folder of the MainProject will looks like below.
Now we will double click on the Exe to open the external Windows Form which is pasted in the same folder. The following output should appear if we all follow exactly the same instruction.