MULTIMEDIA

Silverlight Recipes : Controls - Creating a Custom Layout Container (part 3)

3/4/2015 8:26:08 AM
4.1. Using the WrapPanel

Let's consider using the WrapPanel in a user interface. One straightforward option is to use it in similar fashion to a StackPanel:

<wrappanellib:WrapPanel Orientation="Horizontal">
<TextBlock Text="Child 1"/>
<TextBlock Text="Child 2"/>
<TextBlock Text="Child 3"/>
<TextBlock Text="Child 4"/>
<TextBlock Text="Child 5"/>
<TextBlock Text="Child 6"/>
<TextBlock Text="Child 7"/>
<TextBlock Text="Child 8"/>
<Button Content="Child 9" Width="60" Height="30"/>
<Button Content="Child 10" Width="70" Height="30"/>
<Button Content="Child 11" Width="60" Height="30"/>
<RadioButton Content="Child 12" Width="90" Height="30"/>
<RadioButton Content="Child 13" Width="60" Height="30"/>
<Button Content="Child 14" Width="80" Height="30"/>
<Button Content="Child 15" Width="60" Height="30"/>
</wrappanellib:WrapPanel>

This code shows the standard XAML usage pattern where all children, a mixed bag of controls in this case, are listed within the WrapPanel declaration, much in the fashion of any other layout container. You should be able to cut and paste this code in your own sample application and use it as is. Just remember to reference the assembly from the sample code, and create a namespace mapping (you have mapped the wrappanellib namespace here).

One of the interesting usages of a panel is to assist in the layout process of an ItemsControl, which is the primary base control for visually representing a collection of many items. ItemsControl exposes a property named ItemsPanel of type ItemsPanelTemplate that can be defined in terms of any type that extends Panel. A better control is the ListBox, which extends the ItemsControl and, by virtue of that, uses a panel internally for layout. Let's see how to replace the layout panel for a ListBox and control some of the panel's properties as well.

Listing 2 shows the XAML for a page with a ListBox in it, with its default panel replaced with your own WrapPanel from this recipe.

Listing 2. XAML for a ListBox using the WrapPanel for layout
<UserControl x:Class="Recipe5_9.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Recipe5_9"
xmlns:wrappanellib=
"clr-namespace:Recipe5_9;assembly=Recipe5_9.WrapPanel"
Width="585" Height="440">
<UserControl.Resources>

<local:ImagesCollection x:Key="dsImages" />

<DataTemplate x:Key="dtImageItem">
<Grid Background="#007A7575" Margin="10,10,10,10" >

<Rectangle Fill="#FF7A7575" Stroke="#FF000000"
RadiusX="5" RadiusY="5"/>
<Image Margin="10,10,10,10" Width="50" Height="50"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Source="{Binding ImageFromResource}"/>

</Grid>
</DataTemplate>

<Style TargetType="ListBox" x:Key="STYLE_WrapPanelListBox">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<wrappanellib:WrapPanel Orientation="{Binding CurrentOrientation}"
Width="600" Height="600"/>
</ItemsPanelTemplate>
</Setter.Value>

</Setter>
</Style>

</UserControl.Resources>

<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox x:Name="lbxWrapPanelTest" Grid.Row="0"
ItemTemplate="{StaticResource dtImageItem}"
ItemsSource="{StaticResource dsImages}"
Style="{StaticResource STYLE_WrapPanelListBox}">
</ListBox>
<StackPanel Orientation="Horizontal" Grid.Row="1">
<RadioButton Content="Horizontal Arrangement" Margin="0,0,20,0"
GroupName="OrientationChoice" x:Name="rbtnHorizontal"
Checked="rbtnHorizontal_Checked" IsChecked="True"/>
<RadioButton Content="Vertical Arrangement" Margin="0,0,0,0"
GroupName="OrientationChoice" x:Name="rbtnVertical"
Checked="rbtnVertical_Checked"/>
</StackPanel>
</Grid>

</UserControl>


The first thing to note is the ItemsPanelTemplate definition for the ListBox. The internal implementation of the ListBox in the framework uses a StackPanel as the panel, but you redefine it to use your own WrapPanel and set it as the value of the ItemsPanel property in a style targeting a ListBox. You then apply the style to the ListBox lbxWrapPanelTest. We will come back to this definition in a moment.

Also notice that lbxWrapPanelTest gets its data from a data source named dsImages pointing to a collection named ImagesCollection. The ItemTemplate is set to a data template dtImageItem that displays some images contained in dsImages; each image is encapsulated in a type named CustomImageSource.

Listing 3 shows the code for CustomImageSource and ImagesCollection.

Listing 3. Code for CustomImageSource and ImagesCollection types
using System.Windows.Media.Imaging;
using System.Reflection;
using System.Collections.Generic;

namespace Recipe5_9
{
public class CustomImageSource


{
public string ImageName { get; set; }
private BitmapImage _bitmapImage;
public BitmapImage ImageFromResource
{
get
{
if (_bitmapImage == null)
{
_bitmapImage = new BitmapImage();
_bitmapImage.SetSource(
this.GetType().Assembly.GetManifestResourceStream(ImageName));
}

return _bitmapImage;
}
}
}
public class ImagesCollection : List<CustomImageSource>
{
public ImagesCollection()
{
Assembly thisAssembly = this.GetType().Assembly;
List<string> ImageNames =
new List<string>(thisAssembly.GetManifestResourceNames());

foreach (string Name in ImageNames)
{
if (Name.Contains(".png"))
this.Add(new CustomImageSource { ImageName = Name });
}
}
}
}


The images used in this sample are embedded as resources in the project assembly. ImagesCollection uses GetManifestResourceNames() to get a collection of the string names of all the embedded resources. It then iterates over the collection of resource names and uses GetManifestResourceStream() to acquire each resource as a stream. It creates a new CustomImageSource for each one ending with the .png extension indicating an image resource, and the CustomImageSource type constructor loads the image.

Let's take another look at that ItemsPanelTemplate definition. Once the ItemsPanel property is set on the ListBoxItem, the panel instance that is created internally by the ListBox is not made available to your application code in any way. However, there may be a need to access properties on the underlying panel from application code. An example could be the need to change your WrapPanel's Orientation property to influence the ListBox's layout. However, since the panel is not directly exposed, you need to take a slightly indirect approach to this.

Inside the ItemsPanelTemplate declaration, the WrapPanel has full access to the DataContext of its parent ListBox lbxWrapPanelTest. This gives you a way to bind a property exposed by the panel to application data, as long as that data is made available through the ListBox's DataContext. As shown in Listing 1, you bind the WrapPanel.Orientation property to the CurrentOrientation property of some data item. Further, you have two RadioButtons on the page with Checked event handlers defined in the codebehind. Listing 4 shows the codebehind for the page.

Listing 4 . Codebehind for the MainPage hosting the ListBox
using System.Windows.Controls;
using System.ComponentModel;

namespace Recipe5_9
{
public partial class MainPage : UserControl
{
ListBoxPanelOrientation CurrentLbxOrientation =
new ListBoxPanelOrientation { CurrentOrientation = Orientation.Horizontal };
public MainPage()
{
InitializeComponent();
lbxWrapPanelTest.DataContext = CurrentLbxOrientation;
}

private void rbtnHorizontal_Checked(object sender,
System.Windows.RoutedEventArgs e)
{
CurrentLbxOrientation.CurrentOrientation = Orientation.Horizontal;
}

private void rbtnVertical_Checked(object sender,
System.Windows.RoutedEventArgs e)
{
CurrentLbxOrientation.CurrentOrientation = Orientation.Vertical;
}
}
public class ListBoxPanelOrientation : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;

private Orientation _Current;
public Orientation CurrentOrientation
{
get { return _Current; }


set
{
_Current = value;
if (PropertyChanged != null)
PropertyChanged(this,
new PropertyChangedEventArgs("CurrentOrientation"));
}
}
}
}

The ListBoxPanelOrientation type exposes the CurrentOrientation property enabled with property change notification. You construct and initialize an instance of ListBoxPanelOrientation, and set it to the ListBox's DataContext. This causes the internal WrapPanel instance to adopt this orientation through the binding discussed earlier. In the Checked event handlers of the RadioButtons, you change the CurrentOrientation value, which causes the ListBox to change its orientation dynamically, again because of the property change notification flowing back to the WrapPanel through the binding.

Figure 1 shows the ListBox and the contained WrapPanel in action.

Figure 1. A ListBox using the WrapPanel with different orientations


Other  
 
Most View
Microsoft SharePoint 2010 Web Applications : Presentation Layer Overview - Ribbon (part 1)
The Cyber-athletic Revolution – E-sports’ Era (Part 1)
Windows Server 2003 : Implementing Software Restriction Policies (part 4) - Implementing Software Restriction Policies - Creating a Path Rule, Designating File Types
Sql Server 2012 : Hierarchical Data and the Relational Database - Populating the Hierarchy (part 1)
Two Is Better Than One - WD My Cloud Mirror
Programming ASP.NET 3.5 : Data Source-Based Data Binding (part 3) - List Controls
Windows 8 : Configuring networking (part 5) - Managing network settings - Understanding the dual TCP/IP stack in Windows 8, Configuring name resolution
Nikon Coolpix A – An Appealing Camera For Sharp Images (Part 2)
Canon PowerShot SX240 HS - A Powerful Perfection
LG Intuition Review - Skirts The Line Between Smartphone And Tablet (Part 2)
Popular Tags
Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Indesign Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe After Effects Adobe Photoshop Adobe Fireworks Adobe Flash Catalyst Corel Painter X CorelDRAW X5 CorelDraw 10 QuarkXPress 8 windows Phone 7 windows Phone 8 BlackBerry Android Ipad Iphone iOS
Top 10
Review : Acer Aspire R13
Review : Microsoft Lumia 535
Review : Olympus OM-D E-M5 Mark II
TomTom Runner + MultiSport Cardio
Timex Ironman Run Trainer 2.0
Suunto Ambit3 Peak Sapphire HR
Polar M400
Garmin Forerunner 920XT
Sharepoint 2013 : Content Model and Managed Metadata - Publishing, Un-publishing, and Republishing
Sharepoint 2013 : Content Model and Managed Metadata - Content Type Hubs