|
Exploring Silverlight Headered Content Control...
The headered content control, a pretty simple control, consists of a header area and a content area, which the header and content can be anything.
By: Brian Mains
Date: August 11, 2009
Printer Friendly Version
Introduction
The Silverlight toolkit is an add-on to Silverlight meant to add additional Silverlight control functionality for developers. One of these
controls is the the headered content control, a pretty simple control: it consists of a header area and a content area, which the header and
content can be anything. This header/content area is a one-to-one mapping; there is only one header and one content area.
While I'm going to write about this control, I'm also going to discuss the nuances of the content-based controls, how they can be bound, and how
the entire control can be bound using an underlying data context.
The Silverlight toolkit is available at http://www.codeplex.com/Silverlight. This control is subject to change in the future, and may change
with the release of Silverlight 3.
Understanding Content Controls
If you've seen a control with the Content property in Silverlight (which there are many), then you see one of the most common types of controls
in the framework. Content controls can take a variety of values in its content property. In its simplest form, the content of the control
could be text or some other primitive value (integer, etc.). But content controls are not just limited to primitive data. Content controls
can accept any type of other Silverlight element within itself. This is done by using long-hand syntax in the fom of:
Listing 1: Long-Form Syntax
<HeaderedContentControl.Content>
<TextBlock Text="My Content" />
</HeaderedContentControl.Content>
In this way, the content property can store another Silverlight element, but it can only store a single element. This may seem like a bad idea or
even a bug, so how do you get around storing more than one element? You could use a custom control or a user control, but at its simplest form,
it could be using a panel control, as in:
Listing 2: Using a panel for content
<HeaderedContentControl.Content>
<StackPanel>
<TextBlock Text="My" />
<TextBlock Text="Content" />
</StackPanel>
</HeaderedContentControl.Content>
While content controls have only one child, panel-based controls or items-based controls, can store multiple children. So this type of element
meets this one control requirement, and the panel itself can store multiple elements. It's a two-step process of sorts. This feature isn't
unique to the HeaderedContentControl object, but is a construct in Silverlight (and is also available in WPF).
Data Binding
Silverlight controls support binding to data, whether it be a single or collection of business objects, or a DataTable. How you bind, though,
depends on the control you are using. Some controls may implement an ItemsSource property. This property comes from a special base class in
the Silverlight framework, and won't be discussed here. This type of control is more for controls that have a repeatable interface (like the
ListBox). However, controls that do not implement this property can also be bound.
By default, controls can be supplied an underlying data source via the DataContext property. This property specifies an object or collection
that acts as the relative data source, or the data that's associated with the control. What does that mean in the case of the
HeaderedContentControl? Let's take a look.
One of the ways to supply this value is via code, as in Listing 3. I like to reference my data via code, and skip the inline capabilities of
referencing data.
Listing 3: Supplying a DataContext
This.HeaderedControlInstance.DataContext = myObj;
This control now knows about the object being referred to it. So any {Binding} statements within the header and content templates come from
data supplied from the object assigned to DataContext.
However, if you tried to implement this as is based on my description of the solution, and expect it to work, it won't bind correctly
(it appears blank). This is because there is one other step that needs to happen. A generic {Binding} statement needs assigned to the
Header and Content properties; this correctly passes the underlying object supplied to the DataContext property to the header/content templates.
The final representation of the control would appear as:
Listing 4: Binding in the Header/Content Templates
<HeaderedContentControl Header="{Binding}" Content="{Binding}">
<HeaderedContentControl.HeaderTemplate>
<Label Content="{Binding Title}" />
</HeaderedContentControl.HeaderTemplate>
<HeaderedContentControl.ContentTemplate>
<Label Content="{Binding Text}" />
</HeaderedContentControl.ContentTemplate>
</HeaderedContentControl>
In Listing 4, the Headered Content Control receives an object (via the DataContext) that has a Title and Text property, which is supplied to the
Header/Content templates via the binding in the Header/Content properties. The Title and Text values in the bound object are bound to labels,
via the Binding statement. The value in the Binding declaration has to match the property in the underlying object.
So what's the difference between "{Binding}" and "{Binding Title}"? In the latter option, Title has to be the name of a property that's being
bound to some control. The binding statement without a parameter, the former option, binds the whole object to the control. So the whole
object is bound to the Header/Content of the control, so individual properties can be bound.
Items Binding
So what if you want multiple items within the Header/Content? You could use an ItemsControl, like the HeaderedItemsControl class. This control
already provides this ability with ease. However, using the HeaderedContentControl, the solution can also be to use an ItemsControl within
the content. To setup this example, suppose you had the following:
Listing 5: Example Class
public class SampleClass
{
public string Title { get; set; }
public string Text { get; set; }
public List<SampleItem> Items { get; set; }
}
Public class SampleItem
{
public string Name { get; set; }
public string Value { get; set; }
}
Using a similar setup previously for the header/content, the solution to binding a repetitive list within the HeaderedContentControl control is
to use a repetitive control (like the ListBox control) within the content.
<HeaderedContentControl Header="{Binding Title}">
<HeaderedContentControl.Content>
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding Name}" />
<Label Content="{Binding Value}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</HeaderedContentControl.Content>
</HeaderedContentControl>
In this example, the ListBox control is a way to show multiple items within the content. The source of the content comes from the Items
property in the SampleClass class. Any items in this list is bound to the ListBox, which each item gets its own instance of the provided
DataTemplate, binding each SampleItem object to the created template for the ListBoxItem.
Conclusion
The HeaderedContentControl is a simple control that provides a header and content. As we see from content-based controls, we still can
provide flexibility using these simple controls to do more advanced layouts that we may need to be accustomed to.
The HeaderedContentControl class provides a header and content to display data in, but also provide a template for each, which allows binding
to these templates via an object bound to the DataContext property.
|