Tuesday, 27 May 2014

WPF Dynamic Controls

Well recently, actually specifically today I've had a requirement for the ability to dynamically add controls to a Window in a wpf project. The first thing I did naturally was to google the problem, I figure there's no point reinventing the wheel and all that, but it turns out there aren't any hard and fast guides to achieve the ability to dynamically add custom controls to a wpf project.

Anyway I decided to rectify this by creating a ListView in my WPF Window, and settings its item source to a collection of custom controls, it turns out this worked exactly as I hoped.

The Window with no dynamic controls added to it looks as below
Window with no dynamic controls added

And it looks like this with a couple of custom controls added to it

To achieve this I created a UserControl xaml form called DynamicControl, added a Label, ComboBox and TextBox as the base control to be added to the ListView, the XAML markup for this is below.
 <UserControl x:Class="DynamicControlTest.DynamicControl"  
        mc:Ignorable="d" >  
     <Label Content="Label" HorizontalAlignment="Left" VerticalAlignment="Top" RenderTransformOrigin="-0.132,0"/>  
     <ComboBox HorizontalAlignment="Left" Margin="43,0,0,0" VerticalAlignment="Top" Width="77" Height="26"/>  
     <TextBox HorizontalAlignment="Left" Height="26" Margin="135,0,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/>  

The XAML markup for the MainWindow view is very simple, it contains two buttons and a ListView, I've styled the ListView to have no borders and to hide the column headers to make it appear as if the dynamic controls are being added directly onto the Window as opposed to a control in the Window.

The xaml markup for MainWindow.xaml is below
     xmlns:local="clr-namespace:DynamicControlTest" x:Class="DynamicControlTest.MainWindow"  
     Title="MainWindow" Height="350" Width="525">  
     <ListView Name="listView" HorizontalAlignment="Stretch" Margin="10,35,10,10" VerticalAlignment="Stretch" BorderThickness="0" >  
         <Style x:Key="lvheaders" TargetType="{x:Type GridViewColumnHeader}">  
           <Setter Property="Visibility" Value="Collapsed" />  
         <GridView AllowsColumnReorder="False" ColumnHeaderContainerStyle="{StaticResource lvheaders}">  
     <Button Click="Button_Click" Content="Add Control" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="75"/>  
     <Button Click="Button_Click_1" Content="Remove Control" HorizontalAlignment="Left" Margin="407,10,0,0" VerticalAlignment="Top" Width="100"/>  

And finally the code behind that allows a user to add or remove items/controls dynamically to the window
 using System.Collections.ObjectModel;  
 using System.Windows;  
 namespace DynamicControlTest  
   /// <summary>  
   /// Interaction logic for MainWindow.xaml  
   /// </summary>  
   public partial class MainWindow : Window  
     ObservableCollection<DynamicControl> dynamicControls = new ObservableCollection<DynamicControl>();  
     public MainWindow()  
       listView.ItemsSource = dynamicControls;  
     private void Button_Click(object sender, RoutedEventArgs e)  
       dynamicControls.Add(new DynamicControl());  
     private void Button_Click_1(object sender, RoutedEventArgs e)  
       DynamicControl[] dca = new DynamicControl[listView.SelectedItems.Count];  
       if(dca != null && dca.Length > 0)  
         for (int i = 0; i < dca.Length; i++)  

And that's all there is too it. As you can see I create an ObservableCollection called dynamicControls that stores objects of the type DynamicControl. I set the ListViews ItemsSource to this ObservableCollection so that the view can track changes and reflect these on the interface. When you add a new control all you need to do is add a new DynamicControl item to the dynamicControls ObservableCollection.
I do very much the same to remove items, but as I wish to allow multiple items to be selected I create a DynamicControl array, copy the contents of the ObservableCollection to it, then I iterate over the array and call the ObservableCollection Remove function to remove each selected DynamicControl from the ObservableCollection. Hopefully this will help you in creating your own Dynamic Control lists.