1. Positioning In A Specific Grid Cell

  In the previous example, the location of the TextBlock was fixed by means of setting its Alignment and Margin properties. The Alignments located it up in the top left corner of the Grid and the Margins provided a buffer to the left and top between the control itself and its container, the Grid.


  For the next part of the article I will create a Button control at run time. Much of the code logic for the control creation is unchanged from that we used for the previous TextBlock, although of course there are slight differences in the properties that are available. For example, a Button has Content, not Text.

  Here are the first few lines of code in the CreateButton procedure:

Code Copy
Private Sub CreateButton()
        ' Create a new control instance
        Dim MyButton As New Button()
        '  Set its Height and Width
        MyButton.Height = 30
        MyButton.Width = 80

        '  Assign some text content
        MyButton.Content = "Hello There!"

  In order to have a choice of cells in the grid, you first need to create these. By far the easiest way is to go the xaml code and design split window and click on the grid to create the rows and columns. (We could do it in code, but it's just not necessary in this scenario). For the demonstration create 3 rows and 3 columns. The widths and heights are not critical, but columns should be minimum width of 80 to enable the button to fit inside it .

  Once you have the cells, you can assign the location of this run time button to any of them. The way we do this is by using the SetValue method. As you probably know, child controls in a grid have access to the Row and Column Attached Properties and it these properties that we set via SetValue. Here's the code for the sample:

Code Copy
'  To place it in a particular Row and Column, use SetValue
        '  on the Attached Property:
        MyButton.SetValue(Grid.RowProperty, 1)
        MyButton.SetValue(Grid.ColumnProperty, 2)

   If you don't add further code relating to the control's location it will by default be placed centrally in the grid cell. This is because the default value of the HorizontalAlignment and VerticalAlignment properties is Center.

However, if you do want finer grained control of the location, feel free to use the Alignment properties and the Margin property in addition. For example:

Code Copy
'  Location *within the Grid Cell* is still controlled by its Alignment
'  and Margin properties

        MyButton.VerticalAlignment = Windows.VerticalAlignment.Top
        MyButton.Margin = New Thickness(0, 10, 0, 0)

   This will produce the following output:

   If you are wondering about the dashed lines in that screen shot, they are created for demonstration purposes so you can see that the cell placement is working as it should. The code to make the lines visible is:

Code Copy
   MainGrid.ShowGridLines = True

  Next we need to wire up an event handler for the Button's Click event. Similar to the code we used for the TextBlock, but note the change to the delegate used; from MouseButtonEventHandler to RoutedEventHandler

Code Copy
'  Add Event handler for Button Click
        MyButton.AddHandler(Button.ClickEvent, _
         New RoutedEventHandler(AddressOf Button_Click))

  Finally, add the button to the Grid as before:

Code Copy
  '  Add the control instance to the Grid
        MainGrid.Children.Add(MyButton)

   As the code explanation is a bit fractured, I've reproduced the complete procedure below:

Code Copy
    Private Sub CreateButton()
        ' Create a new control instance
        Dim MyButton As New Button()
        '  Set its Height and Width
        MyButton.Height = 30
        MyButton.Width = 80

        '  Assign some text content
        MyButton.Content = "Hello There!"

        '  To place it in a particular Row and Column, use SetValue
        '  on the Attached Property:
        MyButton.SetValue(Grid.RowProperty, 1)
        MyButton.SetValue(Grid.ColumnProperty, 2)

        '  Location *within the Grid Cell* is still controlled by its Alignment
        '  and Margin properties
        ' MyButton.HorizontalAlignment = Windows.HorizontalAlignment.Left
        MyButton.VerticalAlignment = Windows.VerticalAlignment.Top
        MyButton.Margin = New Thickness(0, 10, 0, 0)

        '  Add Event handler for Button Click
        MyButton.AddHandler(Button.ClickEvent, _
         New RoutedEventHandler(AddressOf Button_Click))

        '  This also works:
        '  AddHandler MyButton.Click, AddressOf Button_Click

        '  For demonstration purposes, show the grid lines:
        MainGrid.ShowGridLines = True

        '  Add the control instance to the Grid
        MainGrid.Children.Add(MyButton)

    End Sub

  Don't forget to add the event handler:

Code Copy
    Private Sub Button_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs)
        Dim Msg As String = "We have a Click!"
        Dim Caption As String = "Houston: "
        MessageBox.Show(Msg, Caption)
    End Sub

   And in my example in the screen shot, I fire the control creation code from a Button named Button1:

Code Copy
    Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
        CreateButton()

    End Sub

  Docking in a DockPanel

  Going back to my original task, the requirement wasn't to place the new control inside a grid cell, but in fact to dock it to a DockPanel. To make life more interesting, this DockPanel itself was a part of a UserControl.

  The code needed to place a dynamically created control in a DockPanel and dock it to a particular docking location is similar to the code we used for grid cell placement:

Code Copy
    Private Sub ButtonDocked()
        '  Ensure LastChildFill correctly set to allow docking
        Me.DockPanel1.LastChildFill = False

        ' Create a new control instance
        Dim DockedButton As New Button()
        '  Set its Height and Width
        DockedButton.Height = 35
        DockedButton.Width = 120

        '  Assign some text content
        DockedButton.Content = "I'm Bottom Docked!"

        '  To Dock it in a DockPanel, use SetValue
        '  on the Attached Property:
        DockedButton.SetValue(DockPanel.DockProperty, Dock.Bottom)

        '  Add Event handler for Button Click
        DockedButton.AddHandler(Button.ClickEvent, _
         New RoutedEventHandler(AddressOf Button_Click))

        '  This also works:
        '  AddHandler DockedButton.Click, AddressOf Button_Click

        '  Add the control instance to the DockPanel
        DockPanel1.Children.Add(DockedButton)

    End Sub

  Try running that and you will see that it places the Button in the bottom of the DockPanel.

If you run it multiple times, a new instance of the button will be added each time and because of the docking mechanism, each will be placed on top of the other .. at least until the DockPanel runs out of space.

  As it turns out, the fact that we want to handle the elements in a UserControl causes no problems at all. Just ensure that you assign a value to the Name property of the DockPanel when creating the UserControl and you can access the panel just as we have done with the grid in the previous examples. Of course you will need to qualify the full name of the UserControl together with the DockPanel's name. So if you have a UserControl named MyUC1 in a Window and that UserControl contains a DockPanel named DockPanel1 then the syntax to access the panel will be:
MyUC1.DockPanel1....

  So that solved the original question - how to create a new control dynamically at run time and dock it to a specific docking location inside a DockPanel. But, you know how it is, along the way several more questions occurred to me and my geeky soul couldn't leave things alone until I had answered these too. What happens if you want more than just text as the Button's content? What if you want to place multiple copies of the same dynamic control in different containers? (Not as easy as you'd expect) What if you want to recycle the dynamic control, changing just some elements and then re-using it? I'll be looking into these in Part 2 of this article.