Posted by: Zeeshan Amjad | April 10, 2012

Dynamically loading user control


I came across a situation where I have to display different user control depends on the selection of item in tree control. While doing internet searching I found this article WPF application with dynamic user controls as page. This application loads different user control based on the button click. My application is based on the same idea, but I used tree control rather than button.

I created a simple data structure to store the department name and subjects to display in tree control and three user controls (one for each department). In my user control I didn’t do anything but just display a text message in the text block to show which user control is loaded right now.

The whole idea is picked from the above mention article to create a dictionary of string as a key and instance of user control as a value. Then inside the InitializeComponent method create all the user control object and place it in dictionary. I selected the department name as a key. Here is a code of it.

Code Snippet
Type type = this.GetType();
Assembly assembly = type.Assembly;

UserControl mathUC = (UserControl)assembly.CreateInstance(string.Format("{0}.MathUserControl",type.Namespace));
UserControl csUC = (UserControl)assembly.CreateInstance(string.Format("{0}.CSUserControl", type.Namespace));
UserControl actUC = (UserControl)assembly.CreateInstance(string.Format("{0}.AccountingUserControl", type.Namespace));

userControls.Add(dp1.Name, mathUC);
userControls.Add(dp2.Name, csUC);
userControls.Add(dp3.Name, actUC);

 

We created one group box as a container to load the user control at run time. Because we have define the course id for each course and it varies from department to department, therefore we can display the appropriate user control after looking at the id of the selected course. Here is a code to load the appropriate and set it as a content of the group box. Here is a code of SelectedItemChanged event of tree view.

Code Snippet
private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
    Subject subject = e.NewValue as Subject;

    if (subject != null)
    {
        if (subject.ID.ElementAt(0) == 'M')
        {
            container.Content = userControls["Math"];
        }
        else if (subject.ID.ElementAt(0) == 'A')
        {
            container.Content = userControls["Accounting"];
        }
        else if (subject.ID.ElementAt(0) == 'C')
        {
            container.Content = userControls["Computer Science"];
        }
        else
        {
            container.Content = null;
        }
    }            
}

 

Rest of the application is very straight forward. Here is a complete XAML code of the main window of the application.

Code Snippet
<Window x:Class="UserControls.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
        Title="MainWindow" Height="400" Width="600">
    <Grid DataContext="{Binding}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="2*"/>
        </Grid.ColumnDefinitions>
        <TreeView Margin="5" Grid.Column="0" ItemsSource="{Binding Departments}" SelectedItemChanged="TreeView_SelectedItemChanged">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Subjects}">
                    <TextBlock Text="{Binding Name}"/>
                    <HierarchicalDataTemplate.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Name}"/>
                        </DataTemplate>
                    </HierarchicalDataTemplate.ItemTemplate>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
        <GridSplitter Grid.Column="1" Width="2"
                      HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
        <GroupBox x:Name="container" Grid.Column="2" Margin="5"/>
    </Grid>
</Window>

Here is complete C# code of the program.

Code Snippet
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;

namespace UserControls
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private University uni = new University();
        private Dictionary<string, UserControl> userControls = new Dictionary<string, UserControl>();

        public MainWindow()
        {
            InitializeComponent();

            Department dp1 = new Department();
            dp1.Name = "Math";
            dp1.Subjects.Add(new Subject() { ID = "M101", Name = "PreCalculus" });
            dp1.Subjects.Add(new Subject() { ID = "M210", Name = "Calculus 1" });
            dp1.Subjects.Add(new Subject() { ID = "M211", Name = "Calculus 2" });
            dp1.Subjects.Add(new Subject() { ID = "M212", Name = "Calculus 3" });
            dp1.Subjects.Add(new Subject() { ID = "M213", Name = "Differential Equations" });
            dp1.Subjects.Add(new Subject() { ID = "M218", Name = "Linear Algebra" });

            Department dp2 = new Department();
            dp2.Name = "Computer Science";
            dp2.Subjects.Add(new Subject() { ID = "CS101", Name = "Introduction to Comuter" });
            dp2.Subjects.Add(new Subject() { ID = "CS201", Name = "Comuter Science 1" });
            dp2.Subjects.Add(new Subject() { ID = "CS202", Name = "Comuter Science 2" });
            dp2.Subjects.Add(new Subject() { ID = "CS210", Name = "Data Structure" });
            dp2.Subjects.Add(new Subject() { ID = "CS211", Name = "Database Management" });

            Department dp3 = new Department();
            dp3.Name = "Accounting";
            dp3.Subjects.Add(new Subject() { ID = "ACT101", Name = "Accounting 1" });
            dp3.Subjects.Add(new Subject() { ID = "ACT102", Name = "Accounting 2" });
            dp3.Subjects.Add(new Subject() { ID = "ACT201", Name = "Accounting 3" });
            dp3.Subjects.Add(new Subject() { ID = "ACT202", Name = "Accounting 4" });
            dp3.Subjects.Add(new Subject() { ID = "ACT203", Name = "Cost Accounting" });
            dp3.Subjects.Add(new Subject() { ID = "ACT214", Name = "Auditing" });

            uni.Departments.Add(dp1);
            uni.Departments.Add(dp2);
            uni.Departments.Add(dp3);

            Type type = this.GetType();
            Assembly assembly = type.Assembly;

            UserControl mathUC = (UserControl)assembly.CreateInstance(string.Format("{0}.MathUserControl",type.Namespace));
            UserControl csUC = (UserControl)assembly.CreateInstance(string.Format("{0}.CSUserControl", type.Namespace));
            UserControl actUC = (UserControl)assembly.CreateInstance(string.Format("{0}.AccountingUserControl", type.Namespace));

            userControls.Add(dp1.Name, mathUC);
            userControls.Add(dp2.Name, csUC);
            userControls.Add(dp3.Name, actUC);

            DataContext = uni;
        }

        private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            Subject subject = e.NewValue as Subject;

            if (subject != null)
            {
                if (subject.ID.ElementAt(0) == 'M')
                {
                    container.Content = userControls["Math"];
                }
                else if (subject.ID.ElementAt(0) == 'A')
                {
                    container.Content = userControls["Accounting"];
                }
                else if (subject.ID.ElementAt(0) == 'C')
                {
                    container.Content = userControls["Computer Science"];
                }
                else
                {
                    container.Content = null;
                }
            }            
        }
    }

    public class Subject
    {
        public string ID
        { get; set; }

        public string Name
        { get; set; }
    }

    public class Department
    {
        public Department()
        {
            this.Subjects = new ObservableCollection<Subject>();

        }
        public string Name
        { get; set; }

        public ObservableCollection<Subject> Subjects
        { get; set; }
    }

    public class University
    {
        public University()
        {
            this.Departments = new ObservableCollection<Department>();
        }

        public ObservableCollection<Department> Departments
        { get; set; }
    }
}

 

Here is a XAML for one user control. All of the user controls are very similar.

Code Snippet
<UserControl x:Class="UserControls.CSUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006&quot;
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008&quot;
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <TextBlock Margin="5">CS User Control</TextBlock>
    </Grid>
</UserControl>

 

And here is C# code of the same user control.

Code Snippet
using System.Windows.Controls;

namespace UserControls
{
    /// <summary>
    /// Interaction logic for CSUserControl.xaml
    /// </summary>
    public partial class CSUserControl : UserControl
    {
        public CSUserControl()
        {
            InitializeComponent();
        }
    }
}

 

This is the output of the program if we select some math subject.

UserControl_Output1

And here is the output if we select any subject from Computer Science.

UserControl_Output2


Responses

  1. We also have something like this in one of our application. But what we have taken the approach of binding the user control’s visibility property to the current selection in the list. Of course here all the user control’s are loaded but we don’t need to manage them in dictionary.

    • Thanks for visiting my blog and giving me another idea. I will try that one too in my other program.

      Regards
      Zeeshan Amjad

  2. nice article thankx for sharing

    • Thanks to like it.

      Regards
      Zeeshan Amjad

  3. Can we you do the same thing without using the code behind? I’m trying to load the usercontrol dynamically using the MVVM pattern. I have created a property like this:

    private UserControl _userControl;
    public UserControl DynamicScreen
    {
    get
    {
    return _userControl;
    }

    set
    {
    _userControl = value;
    RaisePropertyChanged(MethodBase.GetCurrentMethod().GetPropertyName());
    }
    }

    This is property is getting set every time the user select a different node on the treeview, I use reflection to find the correct usercontrol. I would like to bind this property to ContentControl on the front end but it is not working. Would you please assist.

    I would like to achieve this:

    • Someone suggested me one more good idea and that is load all the control, but set their visibility using MVVM when you want to display or hide any control. I haven’t tried that idea yet, but it looks like a good idea to use MVVM with user control. The only disadvantage I can think of is that it takes more memory, because all the controls are loaded in the memory all the time.

      Regards
      Zeeshan Amjad

  4. I like the article, but the design make the scources almost impossible to read. The whole article has about 600pixel width minus the sidbar. So I am looking at two bold blue empty spaces left an right, and lots of code snippets with horizontal scrollbars about 350pixle width in the middle….

  5. […] complicated for me to follow based on my very simple task. The examples I've looked at are here, here, and […]


Leave a comment

Categories