AspAlliance.com LogoASPAlliance: Articles, reviews, and samples for .NET Developers
URL:
http://aspalliance.com/articleViewer.aspx?aId=1845&pId=-1
Examining Various Silverlight Containers
page
by Brian Mains
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 36153/ 69

User Controls

The core mechanism for displaying Silverlight pages is the UserControl class.  The UserControl element appears at the top of every user control.  This root element is a prime choice for storing static resources (discussed briefly later), as well as any namespaces (Silverlight uses namespaces to point to namespaces in an assembly).

The UserControl class is a content control, which means that it has only one property for storing children: Content.  This means only one root level element can appear underneath the UserControl class, excluding complex properties (like UserControl.Resources, where Resources is a collection-based property).

Fortunately, other controls can contain a collection of children, so the user control can host a single control, which that single control can then host multiple controls.  We'll get into the specifics of this soon.  The first control that appears at the top of the user control by default is discussed in the next section.

Grids

Many may be familiar with the ASP.NET GridView or DataGrid control, a tabular control that accepts data that the control renders in a table format.  While the Grid element in Silverlight resembles the tabular structure of these controls, this control is meant more for structuring than data binding.

The Grid control is the core grid control for laying out user interfaces.  It's the control that appears by default when creating a Silverlight user control.  It's by no means the only control available, but it is a very common one.

There are a few core properties to know about when setting up this control:

·         ColumnDefinitions - Defines the columns that will appear in the grid (1, 2, or X number of columns).

·         RowDefinitions - Defines the rows that will appear in the grid (1, 2, or X number of rows).

·         Grid.Row - An attached property that specifies the row for the control to appear on.

·         Grid.Column - An attached property that specifies the column for the control to appear on.

·         Grid.RowSpan - An attached property that specifies how many rows the targeted control should span onto.

·         Grid.ColumnSpan - An attached property that specifies how many columns the targeted control should span onto.

If you aren't familiar with attached properties, the Silverlight framework (and the .NET framework for WPF) made available a feature called attached properties, which lets parent container controls attach properties to its children for the purpose of supplying additional information about something related to the control.  In the case of the Grid, attached properties let the parent know where to position the children (row/column), an important feature.

In reality, attached properties are actually method calls to a method within the Grid that lets this control know where to position its children.  All of the information related to the attached property is stored in the extended object (the controls within the Grid) and is maintained by the Silverlight framwork.

To start, a simplified grid is shown in listing 1.

Listing 1: Defining a simple Grid

<Grid>
      <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
      </Grid.RowDefinitions>
      <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
      </Grid.ColumnDefinitions>
</Grid>

This example sets up a Grid that has two rows and two columns.  Each row and column can have its width/height specified.  For instance, the RowDefinition supports the MinHeight, Height, and MaxHeight properties that specify height of a row.  These values can be left blank, or the values could be defined as a numeric value (20) or a relative value (*, 2*, etc.).  The following are valid values:

Listing 2: Specifying Varying RowDefinition objects

<Grid.RowDefinitions>
      <RowDefinition Height="*" />
      <RowDefinition Height="2*" />
</Grid.RowDefinitions>

The asterisk denotes relative widths in comparison to the other rows/columns.  The second row of the grid will be rendered double the size as the first, or at least given double the height.  However, this can change because Silverlight adjusts the sizing of its children due to how the control sizes are calculated by the Silverlight framework.  This is where the MinHeight and MaxHeight come into play.  As each control is sized within the container, Silverlight may adjust the contents, and will attempt to fit each control within the desired height.  If, for some reason, the content cannot fit within the width or height, clipping may occur.

The column definition works the same way, except it specializes in width.  The same parameters for width can be applied: a numeric value or a relative width, if any value is supplied.  To add a control to a column/row, use the following designation in Listing 3.

Listing 3: Adding a Control to the Grid

<Grid ..>
      <TextBlock Text="First Name" Grid.Row="0" Grid.Column="0" />
      <TextBox Name="FN" Grid.Row="0" Grid.Column="1" />
      <TextBlock Text="Last Name" Grid.Row="1" Grid.Column="0" />
      <TextBox Name="LN" Grid.Row="1" Grid.Column="1" />
      <TextBox Name="Desc" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" />
</Grid>

This simple form uses the Grid control's attached property mechanism to add the TextBlock or TextBox elements to the grid's markup.  The first TextBlock element positions itself in row 0 and column 0.  To its right is the first name TextBox element, with the last name elements appearing in the next row.  Note that the value zero is the default for the row and column; this value doesn't need specified.  For readability, I enter zeroes into the markup.

Sometimes, a control may need to span more than one row or column.  This could be for controls like a description element, where the user needs more visibility of the control's content.  In order to allow a TextBox to span multiple columns, use the approach illustrated in Listing 3 by adding a Grid.ColumnSpan attribute.  This enables the control to flow across columns and present itself without pushing column 1's content off of the page.

As long as the form only needs one element positioned within a cell, this example works fine; the challenges come whenever the cell needs to display more than one element.  How can this be made possible?

StackPanel

Enter the StackPanel control, one of the several panels this article will discuss today.  The StackPanel element is responsible for stacking elements vertically on the screen.  However, by adjusting the orientation property, the StackPanel element can render horizontally too.  The prime function of this element is to stack its children one on top of the other.  The StackPanel doesn't wrap to the next line; this means that the content has to fit, or else clipping of content may occur.  This scenario works will when a Min/Max width are specified for its children, but even this may not prevent clipping from occuring, depending on the size of the content and its restrictions.

The StackPanel works by adding one or more children to it, as any other panel works (even in the ASP.NET work, with the Panel server control).  All of the inner children get positioned accordingly.  To solve the situation desribed above, where the Grid cannot display multiple controls in the same cell, the StackPanel can be used to save the design as shown in Listing 4.

Listing 4: Using the StackPanel

<Grid ..>
      <TextBlock Grid.Row="5" Grid.Column="0" Text="Contact Numbers" />
      <StackPanel Grid.Row="5" Grid.Column="1">
            <TextBox Name="PN1" />
            <TextBox Name="PN2" />
            <TextBox Name="PN3" />
      </StackPanel>
</Grid>

Each of the three phone number textboxes renders vertically within row 5, column 1, because of the StackPanel.  Markup becomes easier too because the StackPanel stores the row/column, not each textbox.

The StackPanel doesn't use attached properties, so implementation is very straightforward.  Because it's very easy to use, this makes for a good candidate to replace the Grid as the child of the UserControl.  In a lot of situations, I often delete the default Grid child and use a StackPanel instead.  The vertical nature of a page often makes this a prime parent choice.  However, this control wouldn't be as useful in developing forms without the next control.

WrapPanel

In the Silverlight API, there isn't any such thing as a WrapPanel, which is a shame because the WrapPanel is such a useful control in WPF.  Fortunately, the Silverlight toolkit contains the WrapPanel control in the System.Windows.Controls namespace.  Whereas the StackPanel stacks elements one on top of the other, WrapPanel will wrap content to the next line if need be.  Content that would normally be clipped gets shifted to the next line automatically, growing the WrapPanel container.  If you do not want content to wrap, plan your design carefully to avoid this situation, or use a StackPanel element.

The WrapPanel has the same design as the StackPanel; it's straightforward and easy to use, not defining any attached properties to manage.  Listing 5 has an example of using a WrapPanel for a zip code definition.

Listing 5: Setting up a WrapPanel

<Grid ..>
      <TextBlock Grid.Row="5" Grid.Column="0" Text="Zip Code" />
      <c:WrapPanel Grid.Row="5" Grid.Column="1">
            <TextBox Name="ZIPMAIN" />
            <TextBox Name="ZIPEXTENSION" />
      </c:WrapPanel>
</Grid>

The WrapPanel renders the main and extension zip code textboxes on the same line.  If, for whatever reason, the panel needs to wrap the contents because of a sizing issue, the WrapPanel may wrap the extension TextBox element to the next line.

HeaderedContentControl

The HeaderedContentControl is a new control added to the Silverlight toolkit.  The control provides a header and a footer that content can be supplied to, whether static text or an individualized control makeup.  The control could appear like in Listing 6.

Listing 6: HeaderedControlControl

<HeaderedContentControl Header="My Header Text">
      <HeaderedContentControl.Content>
            <Button Content="First" />
            <Button Content="Second" />
            <Button Content="Third" />
      </HeaderedContentControl.Content>
</HeaderedContentControl>

Here we have a headered control that has a static header stating the "My Header Text" text.  Silverlight (and likewise WPF) support defining properties as children in this regard.  While content is a property of HeaderedContentControl and can be defined inline like the header, adding complex content to a control (like child elements) requires defining the content in this regard.

What appears is the My Header Text above the three buttons.  The buttons appear in a horizontal fashion, using this default setup.  Alternatively, using a StackPanel would have caused a vertical orientation (unless the Orientation property was explicitly set to another value).

The HeaderedContentControl can supply any type of control for the header or content regions.  For instance, adding a textbox to the header simply requires the header property in its template form and adding a control to it in the form of:

Listing 7: Adding a Textbox to the Header

<HeaderedContentControl>
     <HeaderedContentControl.Header>
         <TextBox Test="Text Entering Header" />
     </HeaderedContentControl.Header>
 </HeaderedContentControl>

Now that we have a Textbox in the header, the user can enter text and this text content can be submitted wherever it needs to go.  This control supplies a couple of options for supplying headers or content.  First is the Header and Content properties; it also supplies a HeaderTemplate and ContentTemplate, useful for overriding the default templates of the control.  While the Header and Content properties supply the content, the template overrides the entire template.  Overriding the template may not have much of a visible effect in this scenario, but it does in most controls that you'll use.

A custom template for a control uses a ControlTemplate object to provide the interface that will override the default.  If you override the default template, you have to handle all of the interactions that control performed.  This isn't a daunting task for the HeaderedContentControl, but may be for other controls like the Button control.

Resources

The Silverlight framework (as well as WPF) provides a resource location to store objects within it.  Resources are available at the application level, user control level, and pretty much every-other-control-level that it's very handy to create a reusable template through this scenario, such as:

Listing 8: Reusing control templates

 
<UserControl.Resources>
    <ControlTemplate x:Key="MyCustomTemplate">
         <Border BorderBrush="Beige" BorderThickness="2">
            <TextBlock Margin="20" Text="My templated content via resources" />
        </Border>
     </ControlTemplate>
 </UserControl.Resources>
.
.
<HeaderedContentControl HeaderTemplate="{StaticResource MyCustomTemplate}" />

Any control can override the default template by providing a custom template using the StaticResource keyword.  This keyword designates a resource stored in the UserControl's resources collection, and will dynamically supply the template at runtime.

Panels in Other Controls

Some controls may support defining a custom panel in its template.  For instance, the ListBox supports setting up the panel you'd like to use as the panel of choice for the ListBox.  To define the listbox template, use the following approach:

<ListBox>
      <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                  <c:WrapPanel />
            </ItemsPanelTemplate>
      </ListBox.ItemsPanel>
</ListBox>

The WrapPanel element replaces the default panel for the ListBox; this means each item uses the WrapPanel means for rendering list box items.  If the number of items overflows, the WrapPanel expands to encompass the maximum height of the elements within the row.

Note that you cannot necessarily access the panel being used to wrap the items in the ListBox.  As was recommended by another poster on the silverlight.net forums, the Loaded event for the ListBox passes the instance of the panel via the sender property.  This is the only way to access the underlying panel, if you desire to.  In most cases, you won't need to do so.

Conclusion

Silverlight provides a lot of flexible options for displaying content in the UI, and we looked at a brief overview of some of the common containers in the framework.



©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-03-28 11:18:20 AM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search