Nov 192009
 

Unlike conventional windows Forms applications, WPF supports Nested controls. i.e. one control can reside inside other controls, something that is not possible in Windows Forms where all the controls belong to one container. Lets see how this is Possible. Many controls in WPF has a content property. The Content property is what the control displays. Now the content property is of object type, so it can pretty much accomodate anything. You can set it to a String variable, or a DateTime.

Though it is of object type, a very important distinction is made when you set the Content property of a control. If the object derives from the WPF class FrameworkElement which in turn derives from UIElement, then it is displayed in the Control, else the ToString method is called and the string representation of the object is displayed. The UIElement is the key class for graphical controls in WPF and contains the OnRender method used to get a graphical representation.

So lets see how to create nested controls. Its quite simple really especially in XAML.

<Window x:Class="WpfNestedControls.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Grid>
        <StackPanel>
            <Button Name="myButton">
                <TextBlock Name="NestedtxtBlock"
                           Text="Click Me"
                           TextAlignment="Center" >
                    <Button Name="NestedButton"
                            Content='I am nested' />
                </TextBlock>
            </Button>
        </StackPanel>
    </Grid>
</Window>

If you see the XAML code the nested text block is between the myButton tags and there is one more button inside the TextBlock. Lets see how the code works for this. I will remove the other code for the window creation so we can focus on the important part.

            Button Button1 = new Button();
            Button1.Name = "myButton";
            StackPanel1.Children.Add(Button1);
 
            TextBlock TextBlock1 = new TextBlock();
            TextBlock1.Name = "NestedtxtBlock";
            TextBlock1.Text = "Click Me";
            TextBlock1.TextAlignment = TextAlignment.Center;
            Button1.Content = TextBlock1;
 
            Button Button2 = new Button();
            Button2.Name = "NestedButton";
            Button2.Content = "I am nested";
            TextBlock1.Inlines.Add(Button2);

As you can see the nested TextBlock1 is added as the Content for the button. But the second nested button is not added to the Content property of the TextBlock because it simply doesn’t have one. What it does have is an InlineCollection – a collection of the Inline properties. Now Inline doesn’t derive from FrameworkElement or UIElement, so there is no OnRender method. This OnRender method is provided by the TextBlock. So the Inline property depends on the TextBlock Control to render itself on the screen.

Now why doesn’t TextBlock have a content property – because it doesn’t derive from the Control class, instead it derives from FrameworkElement. I understand that this class hierarchy is confusing, but its one of the keys to understanding how WPF works. More about that  in later blogs.

OurApp

Here is a small sample on how the main button click event can be used to intercept events from child controls. Such events are called RoutedEvents and they can happen in two ways – From the originating control to the root or vice versa. The originalsource property of the EventArgs contains the actual control that raised the event and the Handled property makes it stop propagating to the parent control.

       private void myButton_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("This is a click event and originated in " + ((Button)e.OriginalSource).Name );
            e.Handled = true;
        }