Introduction
In one of my Silverlight projects, the requirement is to showslide show of the pictures online and the zooming funcionality
needs to implement. I created one reusable control in
Silverlight using the 'Silverlight Templated Control' available
in Silverlight tools. Here I want to share my code for the Album
which uses the XML file to show the images from the server from
multiple folders which are mapped to the album.
Using the code
The goal is to create a Album control that is reusable in aSilverlight project as below -
<grid x:name="LayoutRoot" />
<xamlalbum x:name="XAlbum" albumsrc="Album.xml" borderthickness="1" borderbrush="Black"
disablealbumselection="True" slideduration="3" height="500" width="600" imgsource="Babies" />
</grid />
To get the output like this -
Developing the control
Create a new 'Silverlihgt Class Library' project to create
the control, to build a .dll file to distribute the control.
Add new control of type 'Silverlight Templated Control' to
create our control.
This will create a class file for the code-behind of the
control and the Generic.xml under Theme folder which contains
the template of our control.
The following is template code which has the next and
previous buttons and other buttons for the slideshow and folder
selection. A slider control for zooming of the control,
MouseWheel event is also added on the image to zoom.
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SLNuke">
<Style TargetType="local:XAMLAlbum">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:XAMLAlbum">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Canvas Name="root" Width="{TemplateBinding Width}"
local:Clip.ToBounds="True"
Height="{TemplateBinding Height}" Margin="0,0,0,0" >
<Canvas.Resources>
<Storyboard x:Name="SlideShow">
<ObjectAnimationUsingKeyFrames BeginTime="00:00:00" x:Name="objectSS"
Storyboard.TargetProperty="Text"
Storyboard.TargetName="txtCurrentImg">
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<Style TargetType="Button" x:Name="AlbumButtonStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Background="White" BorderBrush="black" BorderThickness="2">
<Canvas Background="Transparent" Width="20" Height="20"
HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Canvas.Left="6" Canvas.Top="3" HorizontalAlignment="Center" VerticalAlignment="Center"
Foreground="black" FontFamily="Arial" FontWeight="Bold" FontSize="14"
Text="{TemplateBinding Content}"></TextBlock>
</Canvas>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="Button" x:Name="SlideShowButtonStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Background="White" BorderBrush="black" BorderThickness="2">
<Canvas Background="Transparent" Width="20" Height="20"
HorizontalAlignment="Center" VerticalAlignment="Center">
<Rectangle Canvas.Top="3" Canvas.Left="3" Width="14" Height="10"
Stroke="Black" StrokeThickness="1"></Rectangle>
<Line Stroke="Black" StrokeThickness="1"
X1="4" Y1="17" X2="9" Y2="13"></Line>
<Line Stroke="Black" StrokeThickness="1"
X1="16" Y1="17" X2="11" Y2="13"></Line>
</Canvas>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="Button" x:Name="SlideShowDisabledButtonStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Background="White" BorderBrush="black" BorderThickness="2">
<Canvas Background="Transparent" Width="20" Height="20"
HorizontalAlignment="Center" VerticalAlignment="Center">
<Rectangle Canvas.Top="3" Canvas.Left="3" Width="14" Height="10"
Stroke="Black" StrokeThickness="1"></Rectangle>
<Line Stroke="Black" StrokeThickness="1"
X1="4" Y1="17" X2="9" Y2="13"></Line>
<Line Stroke="Black" StrokeThickness="1"
X1="16" Y1="17" X2="11" Y2="13"></Line>
<Line Stroke="Red" StrokeThickness="1"
X1="6" Y1="4" X2="13" Y2="11"></Line>
<Ellipse Canvas.Top="4" Canvas.Left="6" Width="7"
Height="7" Stroke="Red"></Ellipse>
</Canvas>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Canvas.Resources>
<Image Canvas.Left="0" Canvas.Top="0" Stretch="Uniform"
Source="{TemplateBinding ImgSource}" Name="mainImg">
<Image.RenderTransform>
<ScaleTransform x:Name="ImgScaleTransform" ScaleX="1" ScaleY="1"></ScaleTransform>
</Image.RenderTransform>
</Image>
<TextBox Name="txtCurrentImg" Text="0" Opacity="0"></TextBox>
<Button Content=">" Style="{StaticResource AlbumButtonStyle}"
Name="btnNext" Opacity="0.1" Height="20" Width="20" >
</Button>
<Button Content="<" Style="{StaticResource AlbumButtonStyle}"
Name="btnPrev" Opacity="0.1" Height="20" Width="20">
</Button>
<Button Style="{StaticResource SlideShowDisabledButtonStyle}"
Name="btnSlideShow" Opacity="0.1" Height="20" Width="20">
</Button>
<Slider x:Name="sldrZoom" Orientation="Vertical" Opacity="0.2"
Height="100" Width="20"
Maximum="10" Minimum="1" Value="1"></Slider>
<Popup Name="ablumList" IsOpen="False">
<ListBox Name="lstAlbums" Margin="0,0,0,0" Padding="0,0,0,0">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock MinWidth="100" Text="{Binding}" Height="15"
Foreground="Black" FontSize="10"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Popup>
<Image Name="btnAlbums" Opacity="0.2" Height="30" Width="30"></Image>
</Canvas>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
which contains the albums (Folder) to be bound to the control
and the pictures file names, which will be copied to the
ClientBin of the project which uses the current control.
<?xml version="1.0" encoding="utf-8" ?>
<Albums>
<Album Name="Babies">
<Pic>baby1.jpg</Pic>
<Pic>baby2.jpg</Pic>
.....
</Album>
......
</Albums>
In the code-behind XAMLAlbum.cs load the above xml file by an
asynchronous call to server, which is referenced by the AlbumSrc
property to get the list of the Albums and the Picture names.
Here I am using GetTemplateChild() method to get the controls
from template at runtime
string[] arrPics;
public static readonly DependencyProperty AlbumSrcProperty =
DependencyProperty.Register("AlbumSrc", typeof(String), typeof(XAMLAlbum), null);
public string AlbumSrc
{
get { return (string)GetValue(AlbumSrcProperty); }
set { SetValue(AlbumSrcProperty, value); }
}
///
/// Override the apply template handler.
///
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
....
SetFirstPicToImage();
}
public int CurrentImg
{
get { return (int)GetValue(CurrentImgProperty); }
set
{
SetValue(CurrentImgProperty, value);
ShowPicture();
}
}
void SetFirstPicToImage()
{
WebClient wc = new WebClient();
wc.OpenReadCompleted += wc_OpenReadCompleted;
wc.OpenReadAsync(new Uri(AlbumSrc, UriKind.Relative));
}
XDocument doc;
private void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if (e.Error != null)
{
return;
}
using (Stream s = e.Result)
{
//load the xml file
doc = XDocument.Load(s);
//get the current folder name to show that is given by the user in the property ImgSourceProperty
strFolder = (string)GetValue(ImgSourceProperty);
var pics = from p in doc.Descendants("Album")
where (string)p.Attribute("Name") == strFolder
select p;
var pic = from p in pics.Descendants("Pic")
select (string)p;
arrPics = new string[pic.Count()];
int ind = 0;
foreach (string str in pic.ToArray())
{
arrPics[ind] = str;
ind++;
}
// this will call method ShowPicture() through the property setter - 0 to show the first picture from album
CurrentImg = 0;
//load album names to listbox
if (GetTemplateChild("lstAlbums") != null)
{
ListBox lst = ((ListBox)GetTemplateChild("lstAlbums"));
var albums = from p in doc.Descendants("Album")
select (string)p.Attribute("Name");
lst.ItemsSource = albums.ToList();
lst.SelectedIndex = 0;
Canvas root = ((Canvas)GetTemplateChild("root"));
Popup albumList = ((Popup)GetTemplateChild("ablumList"));
double left = 3;
albumList.SetValue(Canvas.LeftProperty, left);
double top = root.Height - albums.Count() * 15 - 56;
albumList.SetValue(Canvas.TopProperty, top);
albumList.MouseLeftButtonUp += new MouseButtonEventHandler(albumList_MouseLeftButtonUp);
lst.SelectionChanged += new SelectionChangedEventHandler(lst_SelectionChanged);
}
}
}
void ShowPicture()
{
if (arrPics.Length == 0) return;
if (CurrentImg < 0)
{
CurrentImg = 0;
return;
}
if (CurrentImg > arrPics.Length - 1)
{
CurrentImg = arrPics.Length - 1;
return;
}
ImageSourceConverter objImgSrc = new ImageSourceConverter();
((Image)GetTemplateChild("mainImg")).SetValue(Image.SourceProperty,
objImgSrc.ConvertFromString(strFolder + "/" + arrPics[CurrentImg]));
}
Build the project to get the .dll file.
Using the control in Silverlight applications
Create a new Silverlight project and add a reference to the
control dll. The control can be added to the page like the
following.
<UserControl xmlns:my="clr-namespace:SLNuke;assembly=SLNuke"
x:Class="SLNukeTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<Grid x:Name="LayoutRoot">
<my:XAMLAlbum ImgSource="Babies" Width="600" Height="500" x:Name="XAlbum"
SlideDuration="3" DisableAlbumSelection="True"
BorderBrush="Black" BorderThickness="1" AlbumSrc="Album.xml" ></my:XAMLAlbum>
</Grid>
</UserControl>
Create a file Album.xml with the above format and copy to
ClientBin of the project. And copy the pictures to the folders
with same name as the album names written in the xml and place
under ClientBin (Note that I copied the pictures of the albums
Babies and Flowers only Nature will not show as the pictures are
not available in the attached project file SLNukeTest).
Points of Interest
The control has the advantage to zoom and play a slideshow.
Zooming functionality is useful when the picture clarity is not
compromised when the image width and height are decreased on the
screen. The album can be set to only one folder and the property
DisableAlbumSelection can be set to true to make the control to
show only one folder pictures and the folder selection button
will be hidden.
Visit http://www.codeproject.com/KB/silverlight/Silverlight_Pic_Album.aspx to download source code.
No comments:
Post a Comment