We want to take advantage of Silverlight 4 features to work with rich media and perform animations. In this chapter, we will cover many topics related to retrieving digital assets from SharePoint libraries through the SharePoint Silverlight Client Object Model and consuming them in a Silverlight RIA. We will:
So far, we have been able to interact with data from the SharePoint Server through the SharePoint Silverlight Client Object Model and WCF Data Services. Sometimes, we need to share, manage, and consume rich media, related to data.
SharePoint 2010 improves rich media management by introducing asset libraries and we can take advantage of this new feature by consuming it in a Silverlight RIA through the SharePoint Silverlight Client OM. Silverlight 4 offers outstanding features to create amazing User eXperiences (UX) when combining rich media with effects and animations.
SharePoint Server 2010 introduced a new asset library specially designed for managing and sharing digital assets and rich media files such as images, audio, and video. It is possible to combine workflows, routing, rules, and policies with asset libraries. However, in this case, we will focus on creating simple asset libraries to allow us to store images, videos, and audio files and we will consume them through the SharePoint Silverlight Client Object Model.
We will combine a new SharePoint Visual Web Part with a Silverlight RIA to allow users to select their desired asset library and to browse its images and videos with interactive animations and dazzling effects. The SharePoint Visual Web Part will display a drop-down list with the available asset libraries that store images, videos, or audio files and when the user selects one of them, the Silverlight RIA will use the capabilities offered by the Client OM to retrieve and display the digital assets. This way, with this new composite Web Part, it is going to be possible to create a new asset library and to upload the necessary images and videos to display, and the desired background music as an audio file. The Web Part will allow a user to interact with any asset library.
First, follow these steps to create two asset libraries in a SharePoint site:
BeginnersGuides
in the Name textbox. Beginner's Guides
in Description and select Yes in Display this list on the Quick Launch? Cookbooks
as the Name and Description for this new asset library.Follow these steps to prepare and add images, videos, and audio files to the previously created asset libraries.
BeginnersGuides
and Cookbooks
. Add many JPG and/or PNG images to these folders. Add a WMV (Windows Media Video) video file and an MP3 audio file to both folders. The following screenshot shows an example of the contents of the BeginnersGuides
folder with 17 JPG images, a WMV video file, and an MP3 audio file: BeginnersGuides
asset library in the Quick Launch for the SharePoint site. BeginnersGuides
folder. Select all the files within the folder and drag-and-drop them in the Drag files and folder here panel within the Upload Document dialog box. All the file names will appear in the panel. Title, Keywords, Comments, Author
, and Copyright
. You can use this dialog box to set the values for these properties in order to organize the contents of the asset library. Then, click on Save. Cookbooks
. Remember to upload the files stored in the Cookbooks
folder.We added images, videos and audio files to the two asset libraries, BeginnerGuides
and Cookbooks
, in the SharePoint site. Now, we can browse the asset libraries' structure and then create an interactive Silverlight RIA capable of consuming the uploaded digital assets.
Once we have created the two asset libraries in SharePoint, we can use Server Explorer in Visual Studio to analyze the new asset libraries' structures.
This time, we are going to create a new solution in Visual Studio that will include two new projects:
SPAssetsBrowserWebPart
SLAssetsBrowser
SharePoint is built on top of ASP.NET, and therefore, a Visual Web Part inherits key features from the ASP.NET Web Part architecture. The Visual Web Part will display the available asset libraries with videos, pictures, and/or audio files in a SharePoint site and it will send the selected asset library as a parameter to the Silverlight host control that will render the Silverlight application. We will take advantage of one of the new project templates in Visual Studio 2010, the Visual Web Part project template, which enables us to visually design a Web Part that can be deployed to SharePoint. The necessary steps to display a Silverlight application within the Visual Web Part are a bit complex but the flexibility offered by this combination is worth the effort.
Follow these steps to create the new Visual Web Part that accesses the available asset libraries in a SharePoint site:
AssetsBrowser
as the project's name and click OK. Visual Studio will create a blank solution with no projects. SPAssetsBrowserWebPart
as the project's name, and click OK. The SharePoint Customization Wizard dialog box will appear. SPAssetsBrowserWebPart
empty SharePoint 2010 Visual Web Part project will be added to the solution. The code editor will open the source code for the VisualWebPart1UserControl.ascx UserControl
(System.Web.UI.UserControl
). This UserControl
defines the UI for the Visual Web Part and it has a code-behind file, VisualWebPart1UserControl.ascx.cs
. VisualWebPart1
folder in Solution Explorer and select Delete in the context menu. Click OK in the confirmation dialog box. SPAssetsBrowserWebPart
, and select Add | New Item… in the context menu. Expand Visual C# and then expand SharePoint and then select 2010 under Installed Templates in the New Item dialog box. Then, select Visual Web Part, enter AssetsBrowserWebPart
in Name, and click OK. The code editor will open the source code for the AssetsBrowserWebPart.ascx UserControl
(System.Web.UI.UserControl
). Its new code-behind file is AssetsBrowserWebPartUserControl.ascx.cs
. Renaming a Visual Web Part can be a very complex process and therefore, it is easier to delete the default VisualWebPart1
folder and add a new Visual Web Part item with the desired name. This way, Visual Studio will create the container folder and all its related files with the new name. AssetsBrowserWebPart.ascx UserControl
and use the Toolbox to drag-and-drop the following server controls. The names for the server controls are assigned in the ID
property in the Properties window. AssetsBrowserWebPart.ascx UserControl, AssetsBrowserWebPartUserControl.ascx.cs
and add the following using
statements.using Microsoft.SharePoint; Visual Web Partcreatingusing Microsoft.SharePoint.WebControls;
AssetsBrowserWebPartUserControl
partial class.public string SelectedList { get; private set; }
Page_Load
event. This code will run at the server when a user requests the Visual Web Part for the first time and each time a postback occurs. Thus, it is necessary to run different code when it is a postback by checking the Boolean value of the IsPostBack
property. When the code runs for the first time (IsPostBack == false
), it will add the titles for the lists of SPBaseType.DocumentLibrary
type with at least one item (libraryList.RootFolder.ItemCount> 0)
and with content types of Picture, Image, Audio
, or Video
.if (!IsPostBack) { var _context = SPContext.Current; var documentLibraries = _context.Web.GetListsOfType(SPBaseType.DocumentLibrary); foreach (SPList libraryList in documentLibraries) { if ((libraryList.RootFolder.ItemCount > 0) && ((libraryList.ContentTypes[0].Name == "Picture") || (libraryList.ContentTypes[0].Name == "Image") || (libraryList.ContentTypes[0].Name == "Audio") || (libraryList.ContentTypes[0].Name == "Video"))) { // The list has at least 1 element cboDocumentLibraries.Items.Add( new ListItem(libraryList.Title)); } } // Select the first item in the dropdown list cboDocumentLibraries.SelectedIndex = 0; } SelectedList = cboDocumentLibraries.SelectedValue;
AssetsBrowserWebPartUserControl.ascx
and define a SelectedIndexChanged
event handler for the cboDocumentLibraries DropDownList
and add the following code in it. This way, when the user selects a different item in the drop-down list, the SelectedList
property will hold the name for the new list that has been selected.SelectedList = cboDocumentLibraries.SelectedValue;
AssetsBrowserWebPart.cs
code file within the AssetsBrowserWebPart
folder. This file defines the AssetsBrowserWebPart
class as a subclass of WebPart
. Its original code defines a path for the UserControl, AssetsBrowserWebPartUserControl.ascx
, that this WebPart
subclass will load and add to the Controls ControlCollection
. This way, the WebPart
renders the UserControl
. The following lines show the original code for this file.using System;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
namespace SPAssetsBrowserWebPart.AssetsBrowserWebPart
{
[ToolboxItemAttribute(false)]
public class AssetsBrowserWebPart : WebPart
{
// Visual Studio might automatically update this path when you change the Visual Web Part project item.
private const string _ascxPath = @"~/_CONTROLTEMPLATES/SPAssetsBrowserWebPart/AssetsBrowserWebPart/AssetsBrowserWebPartUserControl.ascx";
protected override void CreateChildControls()
{
Control control = Page.LoadControl(_ascxPath);
Controls.Add(control);
}
}
}
AssetsBrowserWebPart
class. This variable will hold a reference to the Control
instance cast as AssetsBrowserWebPartUserControl
. This way, it will be possible to access the value for the SelectedList
public property to send it as a parameter to the Silverlight host control in the OnPreRender
method.private AssetsBrowserWebPartUserControl _control;
CreateChildControls
method to save the reference to the AssetsBrowserWebPartUserControl
instance. OnPreRender
event to add the Silverlight host control that will load and display the Silverlight RIA and it will send the selected asset library title as a parameter. The highlighted lines define the .xap
file location and the parameter called Name
.protected override void OnPreRender(EventArgs e)
eventOnPreRender{
var name = _control.SelectedList;
string webUrl = SPContext.Current.Web.Url;
string renderHost = @"<div id='silverlightControlHost'>
<object data='data:application/x-silverlight-2,' type='application/x-silverlight-2' width='100%' height='100%'>
<param name='source' value='/_catalogs/wp/SLAssetsBrowser.xap'/>
<param name='background' value='white' />
<param name='minRuntimeVersion' value='4.0.50303.0' />
<param name='autoUpgrade' value='true' />
<param name='initParams' value='Name=" + name.Trim() + @"' />
<a href='http://go.microsoft.com/fwlink/?LinkID=149156&v=4.0.50303.0' style='text-decoration:none'>
<img src='http://go.microsoft.com/fwlink/?LinkId=161376' alt='Get Microsoft Silverlight' style='border-style:none'/>
</a>
</object><iframe id='_sl_historyFrame' style='visibility:hidden;height:0px;width:0px;border:0px'></iframe></div>";
LiteralControl host = new LiteralControl(renderHost);
Controls.Add(host);
base.OnPreRender(e);
}
The values for the renderHost
string define a Silverlight control host. You can check the test page generated by Visual Studio for the Silverlight application to find the most up to date definition.
Once you have built your application, click on the Show All Files button in Solution Explorer. Then, expand the BinDebug
folder for your Silverlight project. You will find many folders and files; open the HTML file that ends with TestPage.html
, in our example, SLAssetsBrowserTestPage.html
. You can copy from<div id="silverlightControlHost">
to</div>
and you can assign this value to renderHost
to create a Silverlight host control. However, you have to change the following line that defines the path for the .xap
file:
<param name="source" value="SLAssetsBrowser.xap"/>
It has to be replaced with the path for the .xap
file inside the SharePoint _catalogs/wp
folder.
<param name='source' value='/_catalogs/wp/SLAssetsBrowser.xap'/>
In this case, then, it was necessary to add a parameter after the last param name
, because we want to send a specific value to the Silverlight RIA.
Follow these steps to create the new Silverlight RIA that loads the images, videos, and audio from the asset library selected in the Visual Web Part that renders this application and sends the selected name as a parameter:
SLAssetsBrowser
as the project's name, choose Add to Solution in the Solution drop-down list, and click OK.Microsoft.SharePoint.Client.Silverlight.dll
Microsoft.SharePoint.Client.Silverlight.Runtime.dll
App.xaml.cs
and add the following using
statement:using Microsoft.SharePoint.Client;
StartUp
event handler with the following lines. The code stores the value for the Name
parameter, specified by the Visual Web Part in the string that creates the Silverlight host control, in the parameterName
local variable. Then, it creates a new instance of MainPage
sending this value as a parameter to the constructor.private void Application_Startup(object sender, StartupEventArgs e)
{
string parameterName = e.InitParams["Name"];
this.RootVisual = new MainPage(parameterName);
ApplicationContext.Init(e.InitParams,
System.Threading.SynchronizationContext.Current);
}
C:Program Files (x86)Microsoft SDKsSilverlightv4.0ToolkitApr10Bin
in 64-bit Windows versions, and at C:Program FilesMicrosoft SDKsSilverlightv4.0ToolkitApr10Bin
in 32-bit Windows versions. System.Windows.Controls.Toolkit.dll
. Remember that it is located in the aforementioned Bin
sub-folder. This way, we will have access to the WrapPanel
control. MainPage.xaml
and activate the Toolbox. Right-click on the All Sivlerlight Controls header and select Choose Items... in the context menu. The Choose Toolbox Items dialog box will appear with the Silverlight Components tab activated. Make sure that the checkbox located at the left of the WrapPanel item in the Name column is checked. This way, the Toolbox will display the WrapPanel
control and you will be able to add it by dragging and dropping it to the desired location within the design view. Grid, 800
and 600
, and add the following controls. The following lines show the XAML that defines all the controls and some effects for the lblLibraryName Label
and the wrapPanel WrapPanel
.<UserControl x:Class="SLAssetsBrowser.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:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="600" d:DesignWidth="800" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"> <Grid x:Name="LayoutRoot" Loaded="LayoutRoot_Loaded" Width="Auto" Height="Auto"> <sdk:Label Height="28" HorizontalAlignment="Left" Margin="12,12,0,0" Name="lblLibraryName" VerticalAlignment="Top" Width="776" FontSize="20" FontWeight="Bold" > <sdk:Label.Effect> <DropShadowEffect ShadowDepth="5" Color="Orange" /> </sdk:Label.Effect> </sdk:Label> <ProgressBar Height="22" HorizontalAlignment="Left" Margin="12,554,0,0" Name="pgbLoadingStatus" VerticalAlignment="Top" Width="776" /> <sdk:Label Height="22" HorizontalAlignment="Left" Margin="12,534,0,0" Name="lblStatus" VerticalAlignment="Top" Width="776" Content="Status" /> <ScrollViewer Height="487" HorizontalAlignment="Left" Margin="12,41,0,0" Name="scrollViewer" VerticalAlignment="Top" Width="776"> <toolkit:WrapPanel Name="wrapPanel" Width="Auto" Height="Auto" RenderTransformOrigin="0.497,0.493"> <toolkit:WrapPanel.Effect> <DropShadowEffect ShadowDepth="10"/> </toolkit:WrapPanel.Effect> </toolkit:WrapPanel> </ScrollViewer> </Grid> </UserControl>
MainPage.xaml
and selecting Open in Expression Blend… in the context menu. This way, you will be able to work with the additional effects offered by this tool.There are many open source projects that provide additional effects that you can use in your RIAs, such as Silverlight.FX, http://projects.nikhilk.net/SilverlightFX.
MainPage.xaml.cs
. Now, it is necessary to add a using
statement to include the Microsoft.SharePoint.Client
namespace, as we want to work with the SharePoint Silverlight Client OM. We also have to work with the BitmapImage
class, included in System.Windows.Media.Imaging
.Add the following lines of code:
using Microsoft.SharePoint.Client; using SP = Microsoft.SharePoint.Client; using System.Windows.Media.Imaging;
private ClientContext _context; private SP.List _documents; private string _assetLibraryName; private int _maxImageWidth = 150; private int _imageMargin = 5; // The background music can be added just once private bool _backgroundMusicAdded = false; // The current document to load private int _documentToLoad;
MainPage
constructor with this new constructor that receives the asset library name as a parameter, assigns its value to the _assetLibraryName
private variable, and displays it in the lblLibraryName Label
.public MainPage(string assetLibraryName) { InitializeComponent(); _assetLibraryName = assetLibraryName; lblLibraryName.Content = assetLibraryName; }
Audio, Video
, and Picture
. Add the following code to define an enumeration and a method that returns the media file type according to the received file name's extension:private enum MediaFileType { Audio, Video, Picture } private MediaFileType GetMediaFileType(string fileName) { switch (System.IO.Path.GetExtension(fileName).ToUpper()) { // It isn't necessary to add break; // after each line because the code // exits with the return statement case ".JPG": return MediaFileType.Picture; case ".JPEG": return MediaFileType.Picture; case ".GIF": return MediaFileType.Picture; case ".WMA": return MediaFileType.Audio; case ".MP3": return MediaFileType.Audio; case ".AAC": return MediaFileType.Audio; case ".WMV": return MediaFileType.Video; case ".MP4": return MediaFileType.Video; default: return MediaFileType.Picture; } }
private void imageButton_MouseRightButtonDown(object sender, MouseButtonEventArgs e) { // This ensures that Silverlight won't show up // the default Silverlight context menu e.Handled = true; var hlButton = (sender as HyperlinkButton); var image = hlButton.Content as Image; // Add a doubleAnimation for a MaxWidth animation var doubleAnimMaxWidth = new DoubleAnimation(); doubleAnimMaxWidth.Duration = new Duration(TimeSpan.FromSeconds(6)); doubleAnimMaxWidth.From = image.ActualWidth; doubleAnimMaxWidth.To = scrollViewer.ActualWidth - (_imageMargin * 2); doubleAnimMaxWidth.FillBehavior = FillBehavior.HoldEnd; // Create a new Storyboard to handle the MaxWidth animation var storyboardMaxWidth = new Storyboard(); storyboardMaxWidth.Children.Add(doubleAnimMaxWidth); Storyboard.SetTarget(doubleAnimMaxWidth, image); Storyboard.SetTargetProperty(doubleAnimMaxWidth, new PropertyPath("MaxWidth")); storyboardMaxWidth.AutoReverse = true; storyboardMaxWidth.RepeatBehavior = new RepeatBehavior(1); // Add a doubleAnimation for a MaxHeight animation var doubleAnimMaxHeight = new DoubleAnimation(); doubleAnimMaxHeight.Duration = new Duration(TimeSpan.FromSeconds(6)); doubleAnimMaxHeight.From = image.ActualHeight; doubleAnimMaxHeight.To = scrollViewer.ActualHeight - (_imageMargin * 2); doubleAnimMaxHeight.FillBehavior = FillBehavior.HoldEnd; // Create a new Storyboard to handle the MaxHeight animation var storyboardMaxHeight = new Storyboard(); storyboardMaxHeight.Children.Add(doubleAnimMaxHeight); Storyboard.SetTarget(doubleAnimMaxHeight, image); Storyboard.SetTargetProperty(doubleAnimMaxHeight, new PropertyPath("MaxHeight")); storyboardMaxHeight.AutoReverse = true; storyboardMaxHeight.RepeatBehavior = new RepeatBehavior(1); // Start the previously defined storyboards storyboardMaxWidth.Begin(); storyboardMaxHeight.Begin(); } private void videoButton_MouseRightButtonDown(object sender, MouseButtonEventArgs e) { // This ensures that Silverlight won't show up // the default Silverlight context menu e.Handled = true; var hlb = (sender as HyperlinkButton); var element = hlb.Content as MediaElement; // Add a doubleAnimation for a MaxWidth animation var doubleAnimMaxWidth = new DoubleAnimation(); doubleAnimMaxWidth.Duration = new Duration(TimeSpan.FromSeconds(9)); doubleAnimMaxWidth.From = element.ActualWidth; doubleAnimMaxWidth.To = scrollViewer.ActualWidth - (_imageMargin * 2); doubleAnimMaxWidth.FillBehavior = FillBehavior.HoldEnd; // Create a new Storyboard to handle the MaxWidth animation var storyboardMaxWidth = new Storyboard(); storyboardMaxWidth.Children.Add(doubleAnimMaxWidth); Storyboard.SetTarget(doubleAnimMaxWidth, element); Storyboard.SetTargetProperty(doubleAnimMaxWidth, new PropertyPath("MaxWidth")); storyboardMaxWidth.AutoReverse = true; storyboardMaxWidth.RepeatBehavior = new RepeatBehavior(1); // Add a doubleAnimation for a MaxHeight animation var doubleAnimMaxHeight = new DoubleAnimation(); doubleAnimMaxHeight.Duration = new Duration(TimeSpan.FromSeconds(9)); doubleAnimMaxHeight.From = element.ActualHeight; doubleAnimMaxHeight.To = scrollViewer.ActualHeight - (_imageMargin * 2); doubleAnimMaxHeight.FillBehavior = FillBehavior.HoldEnd; // Create a new Storyboard to handle the MaxHeight animation var storyboardMaxHeight = new Storyboard(); storyboardMaxHeight.Children.Add(doubleAnimMaxHeight); Storyboard.SetTarget(doubleAnimMaxHeight, element); Storyboard.SetTargetProperty(doubleAnimMaxHeight, new PropertyPath("MaxHeight")); storyboardMaxHeight.AutoReverse = true; storyboardMaxHeight.RepeatBehavior = new RepeatBehavior(1); // Start the previously defined storyboards storyboardMaxWidth.Begin(); storyboardMaxHeight.Begin(); }
private void media_MediaEnded(object sender, RoutedEventArgs e) { var media = (sender as MediaElement); // It is necessary to stop it or to set its Position to TimeSpan.Zero media.Stop(); // Play again media.Play(); }
HyperlinkButton
to the wrapPanel WrapPanel
with an image and a video:private HyperlinkButton AddImage(string url) { var image = new Image(); image.MaxWidth = _maxImageWidth; image.Stretch = Stretch.Uniform; var bitmapImage = new BitmapImage(new Uri(url, UriKind.Absolute)); image.Source = bitmapImage; var imageButton = new HyperlinkButton(); imageButton.Visibility = System.Windows.Visibility.Collapsed; imageButton.Margin = new Thickness(_imageMargin); imageButton.Content = image; imageButton.NavigateUri = new Uri(url); imageButton.MouseRightButtonDown += new MouseButtonEventHandler(imageButton_MouseRightButtonDown); imageButton.TargetName = "_blank"; imageButton.Cursor = Cursors.Hand; // Add the new Hyperlink button with the image // to the WrapPanel wrapPanel wrapPanel.Children.Add(imageButton); return imageButton; } private HyperlinkButton AddVideo(string url) { MediaElement media = new MediaElement(); media.MaxWidth = (_maxImageWidth * 3); media.Stretch = Stretch.UniformToFill; media.Source = new Uri(url, UriKind.Absolute); media.AutoPlay = true; media.MediaEnded += new RoutedEventHandler(media_MediaEnded); var videoButton = new HyperlinkButton(); videoButton.Visibility = System.Windows.Visibility.Collapsed; videoButton.Margin = new Thickness(_imageMargin); videoButton.Content = media; videoButton.NavigateUri = new Uri(url); videoButton.MouseRightButtonDown += new MouseButtonEventHandler(videoButton_MouseRightButtonDown); videoButton.TargetName = "_blank"; videoButton.Cursor = Cursors.Hand; // Add the new Hyperlink button with the video // to the WrapPanel wrapPanel wrapPanel.Children.Add(videoButton); return videoButton; }
HyperlinkButton
that displays an image or a video received as a parameter:private void AddImageVideoAnimation(HyperlinkButton hlButton) { // Add a projection to the button var projection = new PlaneProjection(); hlButton.Projection = projection; // Add a doubleAnimation for a Projection's RotationZ animation var doubleAnimProjectionZ = new DoubleAnimation(); doubleAnimProjectionZ.Duration = new Duration(TimeSpan.FromSeconds(5)); doubleAnimProjectionZ.From = 0.0; doubleAnimProjectionZ.To = 360.0; doubleAnimProjectionZ.FillBehavior = FillBehavior.HoldEnd; // Create a new Storyboard to handle the Projection's RotationZ animation var storyboardProjectionZ = new Storyboard(); storyboardProjectionZ.Children.Add(doubleAnimProjectionZ); Storyboard.SetTarget(doubleAnimProjectionZ, projection); Storyboard.SetTargetProperty(doubleAnimProjectionZ, new PropertyPath("RotationZ")); // Add a doubleAnimation for a Projection's RotationY animation var doubleAnimProjectionY = new DoubleAnimation(); doubleAnimProjectionY.Duration = new Duration(TimeSpan.FromSeconds(3)); doubleAnimProjectionY.From = -45.0; doubleAnimProjectionY.To = 45.0; doubleAnimProjectionY.FillBehavior = FillBehavior.HoldEnd; doubleAnimProjectionY.RepeatBehavior = RepeatBehavior.Forever; doubleAnimProjectionY.AutoReverse = true; // Create a new Storyboard to handle the Projection's RotationY animation var storyboardProjectionY = new Storyboard(); storyboardProjectionY.Children.Add(doubleAnimProjectionY); Storyboard.SetTarget(doubleAnimProjectionY, projection); Storyboard.SetTargetProperty(doubleAnimProjectionY, new PropertyPath("RotationY")); // Add a doubleAnimation for an Opacity animation var doubleAnimOpacity = new DoubleAnimation(); doubleAnimOpacity.Duration = new Duration(TimeSpan.FromSeconds(5)); doubleAnimOpacity.From = 0.0; doubleAnimOpacity.To = 1.0; doubleAnimOpacity.FillBehavior = FillBehavior.HoldEnd; // Create a new Storyboard to handle the Opacity animation var storyboardOpacity = new Storyboard(); storyboardOpacity.Children.Add(doubleAnimOpacity); Storyboard.SetTarget(doubleAnimOpacity, hlButton); Storyboard.SetTargetProperty(doubleAnimOpacity, new PropertyPath("Opacity")); // Start the previously defined storyboards storyboardProjectionZ.Begin(); storyboardOpacity.Begin(); storyboardProjectionY.Begin(); hlButton.Visibility = System.Windows.Visibility.Visible; }
private void AddBackgroundMusic(string url) { if (_backgroundMusicAdded) { // Background music already loaded return; } _backgroundMusicAdded = true; MediaElement backgroundMusic = new MediaElement(); LayoutRoot.Children.Add(backgroundMusic); backgroundMusic.Volume = 0.8; backgroundMusic.Source = new Uri(url); backgroundMusic.Play(); }
_assetLibraryName
, request its files, and process each picture, video, and audio file to add it to the wrapPanel WrapPanel
. These methods will run in the UI thread. Replace"http://gaston-pc"
with the SharePoint website's URL.private void Connect() { // Runs in the UI Thread lblStatus.Content = "Started"; // Replace http://gaston-pc with // your SharePoint 2010 Server URL and Site _context = new SP.ClientContext(new Uri("http://gaston-pc", UriKind.Absolute)); _context.Load(_context.Web); _context.ExecuteQueryAsync(OnConnectSucceeded, null); } private void ConnectLists() { // Runs in the UI Thread lblStatus.Content = "Web Connected. Connecting to Lists..."; _context.Load(_context.Web.Lists); _context.ExecuteQueryAsync(OnConnectListsSucceeded, null); } private void GetListData() { // Runs in the UI Thread lblStatus.Content = "Lists Connected. Getting List data..."; _documents = _context.Web.Lists.GetByTitle(_assetLibraryName); _context.Load(_documents); _context.Load(_documents.RootFolder); // Request the files _context.Load(_documents.RootFolder.Files); _context.ExecuteQueryAsync(OnGetListDataSucceeded, null); } private void LoadItems() { // Runs in the UI Thread lblStatus.Content = String.Format("Loading {0} items...", _documents.RootFolder.Files.Count); pgbLoadingStatus.Maximum = _documents.RootFolder.Files.Count; pgbLoadingStatus.Value = 0; _documentToLoad = 0; // Clear the WrapPanel children wrapPanel.Children.Clear(); foreach (File file in _documents.RootFolder.Files) { _context.Load(file); _context.ExecuteQueryAsync( OnLoadItemsSucceeded, OnLoadItemsFailed); } } private void ShowItem() { // Runs in the UI Thread lblStatus.Content = String.Format("Processing item # {0}", _documentToLoad); string fileName = _documents.RootFolder.Files[_documentToLoad].Name; string Url = _context.Url + _documents.RootFolder.Files [_documentToLoad].ServerRelativeUrl; switch (GetMediaFileType(fileName)) { case MediaFileType.Audio: AddBackgroundMusic(Url); break; case MediaFileType.Picture: var imageButton = AddImage(Url); AddImageVideoAnimation(imageButton); break; case MediaFileType.Video: var videoButton = AddVideo(Url); AddImageVideoAnimation(videoButton); break; } // Update the progress bar pgbLoadingStatus.Value++; _documentToLoad++; if (_documentToLoad >= _documents.RootFolder.Files.Count) { // All documents loaded lblStatus.Content = "Displaying animations for all the documents."; } }
private void ShowErrorInformation(ClientRequestFailedEventArgs args) { MessageBox.Show("Request failed. " + args.Message + " " + args.StackTrace + " " + args.ErrorDetails + " " + args.ErrorValue); } private void OnConnectSucceeded(Object sender, SP.ClientRequestSucceededEventArgs args) { // This callback isn't called on the UI thread Dispatcher.BeginInvoke(ConnectLists); } private void OnConnectListsSucceeded(Object sender, SP.ClientRequestSucceededEventArgs args) { // This callback isn't called on the UI thread Dispatcher.BeginInvoke(GetListData); } private void OnGetListDataSucceeded(Object sender, SP.ClientRequestSucceededEventArgs args) { // This callback isn't called on the UI thread Dispatcher.BeginInvoke(LoadItems); } private void OnLoadItemsFailed(Object sender, SP.ClientRequestFailedEventArgs args) { // This callback isn't called on the UI thread // Invoke a delegate and send the args instance as a parameter Dispatcher.BeginInvoke(() => ShowErrorInformation(args)); } private void OnLoadItemsSucceeded(Object sender, SP.ClientRequestSucceededEventArgs args) { // This callback isn't called on the UI thread Dispatcher.BeginInvoke(ShowItem); }
LayoutRoot_Loaded
event:We created a new Silverlight RIA that receives an asset library name as a parameter from the Visual Web Part that renders this application. When the user selects an asset library from a drop-down list in the Visual Web Part, the Silverlight RIA will load the images, videos, and audio from the chosen asset library. We added the necessary code to create an application that displays the images and videos with many animations and effects.
Follow these steps to link the previously created Visual Web Part, AssetsBrowserWebPart
, with this new Silverlight RIA, SLAssetsBrowser
. This way, the Silverlight RIA will be part of the package that contains the Visual Web Part.
AssetsBrowserWebPart
, in the Solution Explorer. AssetsBrowserWebPart
and select Properties in the context menu that appears. You will see the values for its properties in the Properties panel. SPAssetsBrowserWebPart
, will appear as a new member. SLAssetsBrowser
, in the Project Name drop-down list. Element File
in the Deployment Type drop-down list. The following value will appear in Deployment Location, {SharePointRoot}TemplateFeatures{FeatureName}AssetsBrowserWebPart
. The following screenshot shows the dialog box with the explained values: SLTasksViewer
. However, it is still necessary to add a line to the Elements.xml
file to make the Silverlight RIA be part of the Visual Web Part. Elements.xml
file. The following lines are the initial contents of this XML file. They describe the elements that compose this SharePoint 2010 Visual Web Part.<?xml version="1.0" encoding="utf-8"?> <Elements xmlns="http://schemas.microsoft.com/sharepoint/" > <Module Name="AssetsBrowserWebPart" List="113" Url="_catalogs/wp"> <File Path="AssetsBrowserWebPartAssetsBrowserWebPart.webpart" Url="AssetsBrowserWebPart.webpart" Type="GhostableInLibrary" > <Property Name="Group" Value="Custom" /> </File> </Module> </Elements>
</Module>
. The new contents of this XML file will include a reference to the linked Silverlight project .xap
file, SLAssetsBrowser.xap
. This is a new element for this SharePoint 2010 Visual Web Part. During the deployment process, the SLAssetsBrowser.xap
file will be located in the AssetsBrowserWebPart
folder in the SharePoint package file, also known as the WSP package, because it has a .wsp
extension. Thus, the WSP package will also deploy the Silverlight application to the SharePoint server.<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/" >
<Module Name="AssetsBrowserWebPart" List="113" Url="_catalogs/wp">
<File Path="AssetsBrowserWebPartAssetsBrowserWebPart.webpart" Url="AssetsBrowserWebPart.webpart" Type="GhostableInLibrary" >
<Property Name="Group" Value="Custom" />
</File>
<!-- Added -->
<File Path="AssetsBrowserWebPartSLAssetsBrowser.xap" Url="SLAssetsBrowser.xap" />
<!-- EOF Added -->
</Module>
</Elements>
SPAssetsBrowserWebPart
. This way, the solution is going to start with the SharePoint project and not with the Silverlight application. This is very important because it will allow us to debug the Silverlight application when it runs in a SharePoint site. Then, click OK. Feature1.feature
. Visual Studio will display a new panel with the feature title, description, scope, and its items. The feature will include three files in the Items in the feature list, AssetsBrowserWebPart, AssetsBrowsersWebPartUserControl.ascx
and Elements.xml
.Now that the WSP package has been deployed to the SharePoint site, follow these steps to create a new web page and add the Visual Web Part that includes and renders the Silverlight RIA. In this case, it isn't necessary to upload the .xap
file, because it was already deployed with the WSP package.
AssetsBrowser
and click on Create. SharePoint will display the editing tools for the new page. AssetsBrowserWebPart
, in Web Parts. Assets Browser
in Title. 700
in Pixels.The following screenshot shows this value for the label and the Silverlight RIA displaying the images and videos for the chosen asset library.
AssetsBrowserWebPartUserControl.ascx, AssetsBrowserWebPartUserControl.ascx.cs
. Insert a breakpoint in the first line of the Page_Load
event handler, if (!IsPostBack)
. Insert another breakpoint in the line of code of the cboDocumentLibraries_SelectedIndexChanged
event handler, SelectedList = cboDocumentLibraries.SelectedValue
;. AssetsBrowserWebPart.cs
and insert a breakpoint in the first line of the OnPreRender
event handler. Page_Load
event handler in the code-behind file, AssetsBrowserWebPartUserControl.ascx.cs
. IsPostBack
and it will be false
, because it is the first time that the Visual Web Part is rendered. Thus, the method will run the code to add the titles of the document libraries that have pictures, images, audio, or video files. The first item for the cboDocumentLibraries DropDownList
will be selected as the default library and the SelectedList
property is going to save the selected title. Run the code step-by-step to understand the execution flow. OnPreRender
event handler, in AssetsBrowserWebPart.cs
. The renderHost
string will include a line that defines the value for the Name
parameter. This parameter will specify a string with the value stored in the SelectedList
public property. In this method, the code defines a new LiteralControl
instance initialized with the renderHost
string and adds it to the Controls ControlCollection
. Page_Load
event handler in the code-behind file, AssetsBrowserWebPartUserControl.ascx.cs
. IsPostBack
and it will be true
because it is a postback for the UserControl
. Thus, the method won't run the code to add the titles of the document libraries to the drop-down list. It will just run the line that sets the SelectedList
property to the selected title. Run the code step-by-step to understand the execution flow. OnPreRender
event handler, in AssetsBrowserWebPart.cs
. The renderHost
string will include a line that defines the new value for the Name
parameter, held in the previously explained SelectedList
public property. This way, the new LiteralControl
instance will add a Silverlight RIA with a different parameter value.We added the SharePoint Visual Web Part to a new Web page in the SharePoint Site Collection. Then, we used Visual Studio to debug the Visual Web Part and we learned how Visual Web Parts renders a Silverlight RIA with parameters. We inserted many breakpoints to analyze the postback performed by the UserControl
within the Visual Web Part.
The Silverlight RIA displays a WrapPanel
control, wrapPanel
, within a ScrollViewer, scrollViewer
. The WrapPanel
control works as a container and it locates its child elements in sequential positions from left to right, in columns, when its Orientation
property is set to Horizontal
. At the edge of the containing box, it breaks the content to the next row and therefore, it simplifies the organization of HyperlinkButton
controls.
As we don't know the number of rows that will be necessary to display all the pictures and videos in the WrapPanel
control, the ScrollViewer
control defines a scrollable viewport. When the content of the WrapPanel
is not entirely visible, the ScrollViewer
will display scrollbars to allow the user to move the content area that is visible. The visible content is known as, viewport and all of the content included in the ScrollViewer
is known as the extent.
The XAML markup in MainPage.xaml
defines a DropShadowEffect
for the WrapPanel
control, with its ShadowDepth
property set to 10
. This way, all the HyperlinkButton
controls added as wrapPanel's
children will inherit this effect and will drop a shadow with a depth of 10
pixels.
<ScrollViewer Height="487" HorizontalAlignment="Left" Margin="12,41,0,0" Name="scrollViewer" VerticalAlignment="Top" Width="776">
<toolkit:WrapPanel Name="wrapPanel" Width="Auto" Height="Auto" RenderTransformOrigin="0.497,0.493">
<toolkit:WrapPanel.Effect>
<DropShadowEffect ShadowDepth="10"/>
</toolkit:WrapPanel.Effect>
</toolkit:WrapPanel>
</ScrollViewer>
The GetListData
method requests the asset library, a special list, specified in _assetLibraryName
, and loads it, its RootFolder Folder
, and its RootFolder.Files FileCollection.
_documents = _context.Web.Lists.GetByTitle(_assetLibraryName); _context.Load(_documents); _context.Load(_documents.RootFolder); _context.Load(_documents.RootFolder.Files);
After the successful asynchronous execution of the queries, the LoadItems
method clears the children for the wrapPanel WrapPanel
. Then, it runs an asynchronous query to load each File
in the asset library, _documents, RootFolder.Files FileCollection
.
wrapPanel.Children.Clear(); foreach (File file in _documents.RootFolder.Files) { _context.Load(file); _context.ExecuteQueryAsync( OnLoadItemsSucceeded, OnLoadItemsFailed); }
Each successful asynchronous query will schedule the ShowItem
method to run in the UI thread. The first time this method is called, _documentToLoad
is set to 0
and the code in this method will increment _documentToLoad
each time it finishes processing a file. The method retrieves the file name, stored in the Name
property for the File
instance to determine the media file type and saves it in the local fileName
string. Then, it computes an absolute Url
to access the file, _context.Url
concatenated with the ServerRelativeUrl
property for the File
instance, and saves it in the local Url
string.
string fileName = _documents.RootFolder.Files[_documentToLoad].Name; string Url = _context.Url + _documents.RootFolder.Files[_documentToLoad].ServerRelativeUrl;
A switch
statement considers the results of the GetMediaFileType
method that receives the fileName
string as a parameter. As previously explained this method determines the media file type according to the extension and returns a MediaFileType
as a result.
If the file type is MediaFileType.Picture
, the method calls the AddImage
method with the Url
string as a parameter and it saves the HyperlinkButton
instance returned by this method in imageButton
. Then, it calls the AddImageVideoAnimation
with imageButton
as a parameter.
case MediaFileType.Picture: var imageButton = AddImage(Url); AddImageVideoAnimation(imageButton); break;
The AddImage
method creates a new Image
instance, sets values for its MaxWidth
and Stretch
properties, creates a BitmapImage, bitmapImage
, with the absolute Uri
from the URL received as a parameter, url
, and assigns bitmapImage
to the image.Source
property.
var image = new Image(); image.MaxWidth = _maxImageWidth; image.Stretch = Stretch.Uniform; var bitmapImage = new BitmapImage(new Uri(url, UriKind.Absolute)); image.Source = bitmapImage;
Then, the code creates a new invisible HyperlinkButton, imageButton
, and sets its Content
property to the previously created Image
instance, image
. When imageButton
becomes visible, it will show the bitmap image. The NavigateUri
property for imageButton
is set to a new Uri
from the URL received as a parameter, url
. The TargetName
property is set to _blank
, and therefore, when the user clicks the HyperlinkButton,
the Web browser will open a new window and will display the image from the URL.
The code attaches an event handler to the MouseRightButtonDown
event that occurs when the user clicks the right mouse button and the mouse pointer is over the Hyperlinkbutton
. It assigns a new MouseButtonEventHandler
that will fire the imageButton_MouseRightButtonDown
method. This method runs an animation for the Hyperlinkbutton.
var imageButton = new HyperlinkButton(); imageButton.Visibility = System.Windows.Visibility.Collapsed; imageButton.Margin = new Thickness(_imageMargin); imageButton.Content = image; imageButton.NavigateUri = new Uri(url); imageButton.MouseRightButtonDown += new MouseButtonEventHandler(imageButton_MouseRightButtonDown); imageButton.TargetName = "_blank"; imageButton.Cursor = Cursors.Hand;
Finally, it is necessary to add the HyperlinkButton
as a child to the wrapPanel WrapPanel
and return the instance. As previously explained, wrapPanel
will take care of organizing the layout of all the HyperlinkButton
instances added as children.
wrapPanel.Children.Add(imageButton); return imageButton;
At this point, the HyperlinkButton
is invisible, because its Visibility
property was set to System.Windows.Visibility.Collapsed
. However, when the AddImage
method returns, the AddImageVideoAnimation
receives the HyperlinkButton
control as a parameter, hlButton
, and brings life to the image that it displays.
Firstly, it adds a PlaneProjection
instance to the HyperlinkButton, hlButton
, by setting its Projection
property to a new PlaneProjection
instance, projection. PlaneProjection
is a subclass of the Projection
class. The latter allows describing how to project a 2D object in the 3D space by using perspective transforms. Then, the code will run an animation with the values that define the perspective transform for hlButton
.
var projection = new PlaneProjection(); hlButton.Projection = projection;
The RotationX, RotationY
, and RotationZ
properties for a PlaneProjection
instance specify the number of degrees to rotate the HyperlinkButton
in the space. The LocalOffsetX
and LocalOffsetY
properties specify the distance the HyperlinkButton
is translated along each axis of the HyperlinkButton's
plane.
Then, the code defines three DoubleAnimation
(System.Windows.Media.Animation.DolubleAnimation
) instances and adds them as children of their corresponding Storyboard
(System.Windows.Media.Animation.Storyboard
) instances. A DoubleAnimation
instance allows us to animate the value of a Double
property between two target values specified by their From
and To
properties. It uses a linear interpolation over a specified duration, specified by the Duration
property. Each Storyboard
instance defines a container timeline that provides object and property targeting information for its child DoubleAnimation
instance. The code creates the DoubleAnimation
and Storyboard
instances summarized in the following table:
DoubleAnimation instance |
Storyboard instance |
Animates |
From |
To |
Duration (seconds) |
---|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The following lines create the doubleAnimProjectionZ DoubleAnimation
and set its properties. The FillBehavior
property is set to FillBehavior.HoldEnd
to specify that the animation must hold its value after it reaches the end of its active period. This way, the target property for this animation will remain at its end value after the animation ends and it won't revert to its non-animated value.
var doubleAnimProjectionZ = new DoubleAnimation(); doubleAnimProjectionZ.Duration = new Duration(TimeSpan.FromSeconds(5)); doubleAnimProjectionZ.From = 0.0; doubleAnimProjectionZ.To = 360.0; doubleAnimProjectionZ.FillBehavior = FillBehavior.HoldEnd;
The next lines create the Storyboard
instance and add doubleAnimProjectionZ
as a child. Then, it is necessary to set the target object and the target property by calling the static methods Storyboard.SetTarget
and Storyboard.SetTargetProperty
with doubleAnimProjectionZ
as its first parameter.
var storyboardProjectionZ = new Storyboard(); storyboardProjectionZ.Children.Add(doubleAnimProjectionZ); Storyboard.SetTarget(doubleAnimProjectionZ, projection); Storyboard.SetTargetProperty(doubleAnimProjectionZ, new PropertyPath("RotationZ"));
The animations defined in doubleAnimProjectionZ
and doubleAnimOpacity
will run just one. However, doubleAnimProjectionY
will run forever and it will auto reverse its execution, because its RepeatBehavior
is set to RepeatBehavior.Forever
and Autoreverse
to true
. Once it reaches the value specified by To
for projection.RotationY
, it will start a new animation from this value to the value specified by From
, in the reverse direction.
doubleAnimProjectionY.RepeatBehavior = RepeatBehavior.Forever; doubleAnimProjectionY.AutoReverse = true;
Once the method defines all the properties for the DoubleAnimation
and Storyboard
instances, it applies the animations associated with each Storyboard to their targets and initiates them by calling the Begin
method.
storyboardProjectionZ.Begin(); storyboardOpacity.Begin(); storyboardProjectionY.Begin(); hlButton.Visibility = System.Windows.Visibility.Visible;
It is also possible for a single Storyboard
instance to have many DoubleAnimation
or other Animation
subclasses as children. In this case, we used an independent Storyboard
instance for each animation, because we want to have full control over each one to allow us to start and/or stop each animation to experience different alternatives for the UX in the future. However, if we just need to start all the animations at the same time, we can create a single Storyboard
instance, add all the DoubleAnimation
instances as their children, set the Target
and TargetProperty
for each DoubleAnimation
, and call the Begin
method.
When you open the page that contains the Visual Web Part, the Silverlight RIA will display all the hyperlink buttons that display images and videos with dazzling movements. doubleAnimProjectionY
will run forever. The following screenshot shows one of the frames for the animations:
When you click on a dancing image, an animated Hyperlink
button, the Web browser will open a new window with the image displayed with its full size. When you right-click on a dancing image, the code in the imageButton_MouseRightButtonDown
method will run and the image will go on performing the same animation but it will also grow and then stretch. The container WrapPanel
will make sure that the different elements displayed reorganize as the hyperlink button grows and stretches. The following picture shows one of the frames for the animation.
The imageButton_MouseRightButtonDown
method receives two parameters, object sender
and MouseButtonEventArgs e
. The first line sets the Handled
property for e
to true
. This way, it ensures that Silverlight won't show the default Silverlight context menu that appears when the user right-clicks within the Silverlight application area.
e.Handled = true;
As we attached this method as an event handler for the MouseRightButtonDown
event for a HyperlinkButton, sender
can be cast to HyperlinkButton, hlb
and we can access its Content
property to access its associated Image
and store its reference in image
.
var hlButton = (sender as HyperlinkButton); var image = hlButton.Content as Image;
Then, the code defines two DoubleAnimation
instances and adds them as children of their corresponding Storyboard
instances. The code creates the DoubleAnimation
and Storyboard
instances summarized in the following table.
DoubleAnimation instance |
Storyboard instance |
Animates |
From |
To |
Duration (seconds) |
---|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Both DoubleAnimation
instances have their AutoReverse
property set to true
and RepeatBehavior
set to RepeatBehavior(1)
. This means that the image will grow and then it will auto-reverse the animation to stretch to its original width and height.
Once the method defines all the properties for the DoubleAnimation
and Storyboard
instances, it applies the animations associated with each Storyboard
to their targets and initiates them by calling the Begin
method.
storyboardMaxWidth.Begin(); storyboardMaxHeight.Begin();
You can right-click on many images and the animation will run for all these images. The following screenshot shows one of the frames for the animation.
When the file type is MediaFileType.Video
, the ShowItem
method calls the AddVideo
method with the Url
string as a parameter and it saves the HyperlinkButton
instance returned by this method in videoButton
. Then, it calls the AddImageVideoAnimation
with videoButton
as a parameter.
case MediaFileType.Video: var videoButton = AddVideo(Url); AddImageVideoAnimation(videoButton); break;
The AddVideo
method creates a new MediaElement
instance, media
, and sets values for its MaxWidth
and Stretch
properties. Then, it assigns the absolute Uri
from the URL received as a parameter, url
, to the media.Source
property.
MediaElement media = new MediaElement(); media.MaxWidth = (_maxImageWidth * 3); media.Stretch = Stretch.UniformToFill; media.Source = new Uri(url, UriKind.Absolute);
Then, the code sets the AutoPlay
property to true
to automatically start the playback of the video specified in the Source
property. The code attaches an event handler to the MediaEnded
event that occurs when the video finishes. It assigns a new RoutedEventHandler
that will fire the media_MediaEnded
method. This method plays the video again from the beginning and therefore, the video is going to play forever while the Silverlight RIA performs all the animations.
media.AutoPlay = true; media.MediaEnded += new RoutedEventHandler(media_MediaEnded);
Then, the code creates a new invisible HyperlinkButton, videoButton
, and sets its Content
property to the previously created MediaElement
instance, media
. When videoButton
becomes visible, it will show the video being reproduced. The NavigateUri
property for videoButton
is set to a new Uri
from the URL received as a parameter, url
. The TargetName
property is set to _blank
and therefore, when the user clicks the HyperlinkButton
, the Web browser will open the video from the URL in the default player associated with the file extension.
The code attaches an event handler to the MouseRightButtonDown
event that occurs when the user clicks the right mouse button and the mouse pointer is over the Hyperlinkbutton
. It assigns a new MouseButtonEventHandler
that will fire the videoButton_MouseRightButtonDown
method. This method runs the previously explained animation for the Hyperlinkbutton
. This animation is very similar to the one explained for the imageButton_MouseRightButtonDown
method.
var videoButton = new HyperlinkButton(); videoButton.Visibility = System.Windows.Visibility.Collapsed; videoButton.Margin = new Thickness(_imageMargin); videoButton.Content = media; videoButton.NavigateUri = new Uri(url); videoButton.MouseRightButtonDown += new MouseButtonEventHandler(videoButton_MouseRightButtonDown); videoButton.TargetName = "_blank"; videoButton.Cursor = Cursors.Hand;
Finally, it is necessary to add the HyperlinkButton
as a child to the wrapPanel WrapPanel
and return the instance.
wrapPanel.Children.Add(videoButton); return videoButton;
At this point, the HyperlinkButton
is invisible, because its Visibility
property was set to System.Windows.Visibility.Collapsed
. However, when the AddVideo
method returns, the AddImageVideoAnimation
receives the HyperlinkButton
control as a parameter, hlButton
, and brings life to the video that it displays, as explained for the images.
The following screenshot shows one of the frames for the animated HyperlinkButton
reproducing the video and growing after the user right-clicked on it:
We defined the horizontal reproduction area for the video to be _maxImageWidth * 3
pixels and we assigned the UniformToFill
value to the Stretch
property. Thus, the MediaElement
resizes the original to fill the container's dimensions while preserving the video's native aspect ratio.
The following screenshot shows the results of using the four possible values in the Stretch
property with the same original video:
The following table explains the results of using the aforementioned values:
Stretch value |
Description |
Aspect ratio |
---|---|---|
|
The video preserves its original size. |
Preserved |
|
The video is resized to fit in the destination dimensions. |
Preserved |
|
The video is resized to fill the destination dimensions. The video content that does not fit in the destination rectangle is clipped. |
Preserved |
|
The video is resized to fill the destination dimensions. |
Not preserved |
Silverlight 4 supports the video encodings shown in the following table:
Encoding name |
Description and restrictions |
---|---|
None |
Raw video |
YV12 |
YCrCb(4:2:0) |
RGBA |
32-bit Red, Green, Blue, and Alpha |
WMV1 |
Windows Media Video 7 |
WMV2 |
Windows Media Video 8 |
WMV3 |
Windows Media Video 9 |
WMVA |
Windows Media Video Advanced Profile (non-VC-1) |
WMVC1 |
Windows Media Video Advanced Profile (VC-1) |
H.264 (ITU-T H.264 / ISO MPEG-4 AVC) |
H.264 and MP43 codecs; base main and high profiles; only progressive (non-interlaced) content and only 4:2:0 chroma sub-sampling profiles |
If we want to use a video with an encoding that does not appear in the previously shown table in a Silverlight RIA, we will have to convert it to one of the supported formats before uploading it to a SharePoint assets library.
When the file type is MediaFileType.Audio
, the ShowItem
method calls the AddBackgroundMusic
method with the Url
string as a parameter.
case MediaFileType.Audio: AddBackgroundMusic(Url); break;
The AddBackgroundMusic
method checks whether it was called before (_backgroundMusicAdded == true
) before running the rest of the code, because it doesn't want to reproduce two audio files as the background music. If _backgroundMusicAdded
is true
, it assigns true
to _backgroundMusicAdded
.
The code creates a new MediaElement
instance, backgroundMusic
, adds it to a parent container, LayoutRoot
, and sets its Volume
property to 80% (0.8
). The Volume
ranges from 0 to 1. It uses a linear scale.
_backgroundMusicAdded = true; MediaElement backgroundMusic = new MediaElement(); LayoutRoot.Children.Add(backgroundMusic);
Then, it assigns the absolute Uri
from the URL received as a parameter, url
, to the backgroundMusic.Source
property and calls the Play
method to start reproducing the audio file with the specified volume level. The background music will be reproduced just once.
backgroundMusic.Volume = 0.8; backgroundMusic.Source = new Uri(url); backgroundMusic.Play();
Silverlight 4 supports the audio encodings shown in the following table:
Encoding name |
Description and restrictions |
---|---|
LPCM |
Linear 8 or 16-bit Pulse Code Modulation. |
WMA Standard |
Windows Media Audio 7, 8, and 9 Standard. |
WMA Professional |
Windows Media Audio 9 and 10 Professional; Multichannel (5.1 and 7.1 surround) is automatically mixed down to stereo. It supports neither 24-bit audio nor sampling rates beyond 48 kHz. |
MP3 |
ISO MPEG-1 Layer III. |
AAC |
ISO Advanced Audio Coding; AAC-LC (Low Complexity) is supported at full fidelity (up to 48 kHz). HE-AAC (High Efficiency) will decode only at half fidelity (up to 24 kHz); Multichannel (5.1) audio content is not supported. |
If we want to use an audio file with an encoding that does not appear in the previously shown table, we will have to convert it to one of the supported formats before uploading it to a SharePoint assets library.
The Visual Web Part is a great candidate for applying the themes included in Silverlight's Toolkit to offer the user a more exciting UI.
System.Windows.Controls.Theming.Toolkit.dll
. Remember that it is located in the Bin
sub-folder System.Windows.Controls.Theming.ShinyRed
in the Themes
sub-folder. This way, we are going to be able to apply the ShinyRed
theme. UserControl
defined in MainPage.xaml:
xmlns:shinyRed="clr-namespace:System.Windows.Controls.Theming;assembly=System.Windows.Controls.Theming.ShinyRed"
Grid, LayoutRoot:
<shinyRed:ShinyRedTheme>
Grid, LayoutRoot:
</shinyRed:ShinyRedTheme>
ShinyRed
theme will be applied to the main Grid, LayoutRoot
, and all its child controls. Build and deploy the solution and open the page that displays the Visual Web Part. The Silverlight RIA looks really more attractive. However, the colors displayed by the rest of the SharePoint UI don't match the ShinyRed
theme colors. Municipal
theme in the list located at the right. This theme uses a color scheme that is appropriate for Silverlight applications that use the ShinyRed
theme.18.118.30.253