During a recent WPF and Silverlight Training for a Client, I demonstrated the use of Data-bound controls provided by WPF and Silverlight. During one of the Silverlight DataForm demonstrations, one of my attendees asked me a question about the availability of DataForm control in WPF, for Data-Representation and Navigation. This question drove me to write this article. Although for this article, I have designed a WPF user control, but you can also do a Custom Control with a different implementation.
To develop this control, I have used the User Control library of WPF provided in VS2010. Reflection is used for reading public properties from the source class.
Step 1: Open VS 2010 and create a Blank solution. Rename this solution as ’WPF40_DataFormNavigation’. In this solution, add a new WPF User Control library project and rename it as ‘WPF40_DataForm_Control’.
Step 2: Rename UserControl1.xaml to ‘DataFormControl.xaml’ and change the code-behind class name as well.
Step 3: In the ‘DataFormControl.xaml’ write the following xaml code:
The Grid ‘grdView’ will contain all dynamically generated controls using Reflection. The Grid ‘grdNavigation’ contains Button for navigation purpose. The display of the UserControl is as shown below:
The button ‘Get Type’ will load the type from the consumer and based upon properties in the type, elements will be generated.
Step 4: Open ‘DataFormControl.xaml.cs’ and use the following namespaces:
Step 5: Declare the following objects and variable at the control class level:
Note: Please read comments on every object and variable declaration. The comment defines the use of every declaration.
Step 6: When you develop any user and custom control, it is suggested to define public properties for the communication between the control and its consumer. You also need to define events for the notification communication between the control and its consumer. Since we are using WPF, we need to define dependency property and routed event. At the control class level, define the following Dependency properties and routed events:
The inline comments explains the use of every dependency property and routed event.
Step 7: In the control class level, write the following method. This method is a helper method used to check the Data Type of the property from the source type.
Step 8: We now need to generate UI elements (controls) dynamically based upon the source type. In the class, write the following method:
The above method accepts PropertyInfo array and the object parameter. The method reads every property from the array and based upon the property, adds a TextBlock in the ‘grdView’ Grid element. The Text property of the TextBlock is set to the name of the property from the array. To display data from the property for e.g. if the Source property is EmpNo and the Data is 101, then for the Data display the TextBox is added. For this purpose, the above method makes a call to the ‘IsNumericOrString()’ helper method and passes the DataType of the property to it. If the DataType of the property is either Numeric or String, the TextBox will be added in the Grid otherwise for all other DataTypes e.g. DataTime and Boolean, UI elements DataPicker and CheckBox are added respectively. The Object parameter to the above method is used as a Source to the Binding for DataBinding purpose. This object is used for displaying actual data values.
Step 9: Write the following code in the Click event of the ‘Get Type’ button:
The above method raises the routed event. This event gets the ‘ItemsSource’ from the control consumer application. The Calling assembly i.e. the WPF consumer application is read by the method using ‘GetEntryAssembly()’ method of the Assembly class. The ‘ItemsSource’ send by the consumer application is read by the method and the Single type is extracted by iterating from the ItemsSource collection. E.g. The EmployeeCollection is the Source Types passed by the WPF consumer application and if this collection contains collection ‘Employees’, then ItemsSource will be EmployeeCollection and the Single TypeName will be Employee. Once the TypeName is extracted from the ItemsSource, then all public properties are read from it using GetProperties() method of the Type class. All the properties are stored in the PropertyInfo array. This array and the ItemsSource is then passed to the CreateView() method written above in Step 8.
Step 10: Write the following code in the Navigation methods of the button:
Step 11: Build the project and make sure that it is error-free. To the solution created in Step 1, add a WPF project and name it as ‘WPF40_DataFormControl’. Add a reference of the ‘WPF40_DataForm_Control’ created above.
Step 12: Use the above control in the MainWindow.Xaml as shown below:
Step 13: In this project, add a class file and name it as ‘DataClasses.cs’ and write the following classes in it:
Step 14: In the MainWindow.xaml.cs, subscribe to the TypeNameSupplied event of the control as shown below:
The ‘ItemsSource’ property of the control is Set to the EmployeeCollection. This contains collection of the Employee, which contain the Numeric property ‘EmpNo’, string property ‘EmpName’ and DataTime JoiningDate.
Step 15: Run the application and click on Get Type button, the following results will be displayed:
You can navigate through the Employee records using the navigation button. If you change the ItemsSource to the PersonCollection as shown below:
and run the application and click on Get Type button, the result will be as below:
Conclusion: With the support of Reflection, we can load any type and test it after generating the UI dynamically. However I must point out some limitations of this approach:
The above control currently relies on Numeric, String, Boolean and DateTime DataTypes.
It is not currently used for any DML operations.
No data validations are used.
The Binding code can be optimized.
The Nested collection source is not considered here but can be improved.
The entire source code of this article can be downloaded over here