Karine Bosch’s Blog

On SharePoint

Silverlight Media Viewer Web Part for SharePoint

One of my blog readers asked me for help with a variant of the Silverlight Media Viewer, which is one of the Silverlight BluePrint samples, and he wondered why he couldn’t make it run on his machine.

First of all, at this time of writing, not all of the BluePrint samples seem to be converted yet to the released version of Silverlight 2, and the Media Viewer is not among the converted ones. So I made the necessary changes to make it compatible for Silverlight 2. These changes include setting the MinimumVersion property of the SilverlightControl to 2.0.31005.0.

The Media Viewer is a web part that hosts a Silverlight application that is uploaded in a document library. When you want to add the web part to a page in your SharePoint site you can find it in the Silverlight Web Parts category of the Web Part Gallery.

If you work with a standard Picture library, the Media Viewer will show up as the classic Media Viewer from the BluePrint samples with all pictures listed in the list box at the left of the web part.

But you can also arrange your pictures in categories. In that case you have a custom list containing the different categories and a lookup field in the picture library referencing the category list. The Media Viewer shows up with an extra list box at the bottom of the web part. Clicking on one of the categories causes the left list box to display the pictures of the selected category.

 variant-mv2

The web part contains a custom editor part in which you can configure following options:

  • the document library where you uploaded the Silverlight application
  • the picture library that contains the pictures
  • the type of media: picture or movie
  • the list containing the categories
  • a field that will be used as the caption of the button
  • a field that contains an URL to a picture that can be used as the background of the button

customeditorpart1

 

 

 

 

 

 

 

 

 

 

If you don’t choose a document library from the first dropdown list, the Web Part will try to load the Silverlight application from the Shared Documents document library.

If you choose a Category list but no fields, the Title field will be used as button caption and the background of the button will be a simple glass button. With thanks to Michael for providing the code of his glass button.

The communication between SharePoint and Silverlight is established by passing the necessary data through the InitParameters property of the SilverlightControl.

// parameters passed are the name of the selected picture library, the media
// type and the url of the SharePoint site
string parameters = "lib=" + mediaLibName + ",type=" + mediaType.ToString() 
   + ",url=" + SPContext.Current.Web.Url;

if (!string.IsNullOrEmpty(categoryListName) && !categoryListName.StartsWith("Select"))
{
   parameters += ",evlist=" + categoryListName;

   if (!string.IsNullOrEmpty(categoryFieldName) && !categoryFieldName.StartsWith("Select"))
       parameters += ",caption=" + categoryFieldName;
   if (!string.IsNullOrEmpty(categoryPictureUrl) && !categoryPictureUrl.StartsWith("Select"))
       parameters += ",pic=" + categoryPictureUrl;
}
silverlightControl.InitParameters = parameters; 

Within the Silverlight application the SharePoint data from the picture library and category list are retrieved using the HttpWebRequest technique which on its turn can call the standard SharePoint web services. As this runs completely asynchronous and on different background threads now (this is different form the way it worked in Beta 2), I had to make some changes to the code to make this work again for Silverlight 2.

In Beta 2, when I initialized the HttpWebRequest object, I defined a callback method for both the BeginGetRequestStream method and the BeginGetResponseStream method. Now you have to define a callback for the BeginGetResponse method within the callback method passed in the BeginGetRequestStream.

This is the initialization code of the HttpWebRequest object:

internal void BuildPreviewBar(string title)
{
   HttpWebRequest request = (HttpWebRequest)WebRequest.Create(
   new Uri(siteUrl + "/_vti_bin/Lists.asmx", UriKind.Absolute));
   request.Method = "POST";
   request.ContentType = "application/soap+xml; charset=utf-8";
   request.Headers["ClientType"] = "Silverlight";
   // changed for additionally handling the event results
   request.Headers["ListType"] = "piclib";
   if (title != null)
      request.Headers["Event"] = title;
   request.BeginGetRequestStream(new AsyncCallback(RequestCallback), request);
}

Within the callback method for the BeginGetRequestStream method, you can define a callback method for the BeginGetResponse method. But before you can define that callback, you have to make sure to call the EndGetRequestStream method and to close the stream.

body = request.EndGetRequestStream(asyncResult);
System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
byte[] formBytes = encoding.GetBytes(envelope);
body.Write(formBytes, 0, formBytes.Length);
body.Close();
request.BeginGetResponse(new AsyncCallback(ResponseCallback), request); 

Within the callback method for the BeginGetResponse method you first have to retrieve the request object of type HttpWebRequest and then call the EndGetResponse method on it to retrieve the response object of HttpWebResponse. Use the GetResponseStream method to retrieve the response. If the status is OK you can store the response in a class-level variable of type string and call the BeginInvoke method of the Dispatcher to return on the UI thread. Pass in the name of a method to indicate the Dispatcher where you want to continue.

private void ResponseCallback(IAsyncResult asyncResult)
{
   HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
   HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult);
   Stream content = response.GetResponseStream();
   if (response.StatusCode == HttpStatusCode.OK)
   {
       using (StreamReader reader = new StreamReader(content))
       {
           responsestring = reader.ReadToEnd();
       }
       try
       {
           // changed for additionally handling the event results
           if (request.Headers["ListType"] != null)
           {
               switch (request.Headers["ListType"])
               {
                   case "event":
                      this.Dispatcher.BeginInvoke(ProcessResponseEvents);
                      break;
                   case "piclib":
                      this.Dispatcher.BeginInvoke(ProcessResponsePictureLibrary);
                      break;
               }
           }
       }
       catch { }
   }
} 

The Silverlight application of the original BluePrint sample (for Silverlight 2 Beta 2) contained one Page.xaml with all Silverlight controls and styles on it. To have a better insight in it, I encapsulated the different controls and styles in different user controls.

I wanted to keep the SharePoint communication outside the controls and in the Page.xaml.cs code behind. This has some implications for the events: I had to create and raise custom events. The event arguments in Silverlight are of type RoutedEventArgs and inheritance is not supported. So I had to work with the standard Silverlight event arguments and place the significant data into properties of the user controls before raising the custom event.

The ItemSource property of the list boxes is set to use data binding.

As you can see there is a difference between the vertical list box at the left of the Silverlight application and the horizontal list box at the bottom of the Silverlight application. The horizontal listbox is a normal listbox with only a custom style template for the item to be able to use the glass button.

<UserControl x:Class="SL.XAML.MediaViewer.HorizontalListBox"
    xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
    xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
    xmlns:my="clr-namespace:SL.XAML.MediaViewer"
    Width="690" Height="90" Background="Transparent">
   <UserControl.Resources>
      <Style TargetType="ListBox" x:Key="EventListBoxStyle">
          <Style.Setters>
              <Setter Property="Background" Value="Transparent" />
              <Setter Property="ItemsPanel">
                 <Setter.Value>
                     <ItemsPanelTemplate>
                         <StackPanel Orientation="Horizontal"/>
                     </ItemsPanelTemplate>
                 </Setter.Value>
              </Setter>
          </Style.Setters>
      </Style>
   <DataTemplate x:Key="EventButtonTemplate" >
          <Grid x:Name="EventElement">
              <my:GlassButton ButtonClick="GlassButton_ButtonClick" />
          </Grid>
      </DataTemplate>
   </UserControl.Resources>

 

    <Grid x:Name=”LayoutRoot” Background=”Transparent” >

      <ListBox x:Name=”EventListBox” Height=”90″ Width=”690″ Style=”{StaticResource EventListBoxStyle}”
                       ItemTemplate=”{StaticResource EventButtonTemplate}” />
   </Grid>
</UserControl>

 The vertical list box is a more customized control using styles for the ListBoxItem and the ScrollViewer:

<UserControl x:Class="SL.XAML.MediaViewer.VerticalListBox"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="86" Height="600" Background="Transparent">
    <UserControl.Resources>
        <!-- Linear Brushes for the preview controls -->
        <LinearGradientBrush x:Key="BorderGradientBrush"  StartPoint="0,0" EndPoint="1,1">
            <GradientStop Color="Turquoise"  Offset="0.0" />
            <GradientStop Color="Black" Offset="0.75" />
        </LinearGradientBrush>
        <LinearGradientBrush x:Key="ActiveBorderGradientBrush"  StartPoint="0.2,0.2" EndPoint="1,1">
            <GradientStop Color="Turquoise" Offset="0.0" />
            <GradientStop Color="DarkTurquoise"  Offset="1" />
        </LinearGradientBrush>
        <LinearGradientBrush x:Key="SelectedGradientBrush" StartPoint="0,0" EndPoint="1,1">
            <GradientStop Color="White" Offset="0.0" />
            <GradientStop Color="DarkTurquoise" Offset="0.85" />
        </LinearGradientBrush>
        <!-- Style for the List Item -->
        <Style x:Key="PreviewPictureItemStyle" TargetType="ListBoxItem">
            <Setter Property="Foreground" Value="#FF000000" />
            <Setter Property="Width" Value="80" />
            <Setter Property="Height" Value="80" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">
                        <Grid x:Name="PreviewElement" MouseEnter="PreviewElement_MouseEnter" MouseLeave="PreviewElement_MouseLeave"
                              MouseLeftButtonDown="PreviewElement_MouseLeftButtonDown">
                            <Border BorderBrush="{StaticResource BorderGradientBrush}"
                                    BorderThickness="5" CornerRadius="4" Margin="2" Background="Transparent" >
                                <Image Width="70" Height="70" Stretch="Fill" Source="{Binding Url}" />
                            </Border>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style x:Key="PreviewMovieItemStyle" TargetType="ListBoxItem">
            <Setter Property="Foreground" Value="#FF000000" />
            <Setter Property="Width" Value="80" />
            <Setter Property="Height" Value="80" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBoxItem">
                        <Grid x:Name="PreviewElement" MouseEnter="PreviewElement_MouseEnter"
                              MouseLeave="PreviewElement_MouseLeave" MouseLeftButtonDown="PreviewElement_MouseLeftButtonDown">
                            <Border BorderBrush="{StaticResource BorderGradientBrush}"
                                    BorderThickness="5" CornerRadius="4" Margin="2" Background="Transparent" >
                                <MediaElement Width="70" Height="70" Stretch="Fill" AutoPlay="True" Source="{Binding PreviewUri}" />
                            </Border>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <!-- Style for the ScrollViewer -->
        <Style x:Key="ScrollViewerStyle1" TargetType="ScrollViewer">
            <Setter Property="IsEnabled" Value="true"/>
            <Setter Property="Foreground" Value="#FF000000"/>
            <Setter Property="BorderBrush" Value="#FFA4A4A4"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="HorizontalContentAlignment" Value="Left"/>
            <Setter Property="VerticalContentAlignment" Value="Top"/>
            <Setter Property="Cursor" Value="Arrow"/>
            <Setter Property="FontSize" Value="11"/>
            <Setter Property="Background" Value="Transparent" />
            <Setter Property="VerticalScrollBarVisibility" Value="Visible"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ScrollViewer">
                        <Border BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="5">
                            <Grid Background="Transparent">
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="*"/>
                                    <RowDefinition Height="Auto"/>
                                </Grid.RowDefinitions>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*"/>
                                    <ColumnDefinition Width="Auto"/>
                                </Grid.ColumnDefinitions>
                                <Rectangle Grid.Column="1" Grid.Row="1" Fill="Transparent" />
                                <ScrollBar Cursor="Arrow" x:Name="VerticalScrollBar" HorizontalAlignment="Stretch"
                                           Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
                                           Grid.Row="0" Orientation="Vertical" Opacity="1"
                                           ViewportSize="{TemplateBinding ViewportHeight}"
                                           SmallChange="1"
                                           Maximum="{TemplateBinding ScrollableHeight}"
                                           Minimum="0" Value="{TemplateBinding VerticalOffset}"  />
                                <ScrollContentPresenter x:Name="ScrollContentPresenter" Cursor="{TemplateBinding Cursor}"
                                           Margin="0,18,0,18"
                                           Grid.Column="0" Grid.Row="0" Content="{TemplateBinding Content}"
                                           ContentTemplate="{TemplateBinding ContentTemplate}"
                                           />
                            </Grid>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <!-- Style for the ListBox -->
        <Style TargetType="ListBox" x:Key="PreviewListBoxStyle">
            <Setter Property="ItemContainerStyle" Value="{StaticResource PreviewMovieItemStyle}" />
            <!-- <Setter Property="Width" Value="86" /> -->
            <Setter Property="Background" Value="Transparent" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListBox">
                        <Grid x:Name="LayoutRoot">
                            <Border Background="Transparent" CornerRadius="5" Width="86">
                                <ScrollViewer x:Name="PreviewScrollViewer" Width="86"
                                              HorizontalScrollBarVisibility="Disabled"
                                              VerticalScrollBarVisibility="Auto"
                                              Style="{StaticResource ScrollViewerStyle1}"                                            
                                              >
                                    <ItemsPresenter />
                                </ScrollViewer>
                            </Border>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="Transparent">
        <ListBox x:Name="PreviewListBox" Height="600" Width="86"
                 Style="{StaticResource PreviewListBoxStyle}" >
        </ListBox>
    </Grid>
</UserControl>

If you want to run the sample in your SharePoint environment and you didn’t yet configure it for Silverlight, read this blog post first but also make sure that your read the bottom of this post  otherwise the SharePoint web services will not be working anymore.

You can download the source code here.

January 22, 2009 - Posted by Karine Bosch | SharePoint 2007, Silverlight | | 23 Comments

23 Comments »

  1. Wow, great post! Thanks for the comment as well — I gave you a shout-out in my post also, especially since your web part is a lot prettier than mine!

    http://www.devexpertise.com/2009/02/07/retrieving-sharepoint-list-data-from-silverlight-without-a-custom-wcf-or-aspnet-web-service/

    Comment by Aaron | February 9, 2009 | Reply

  2. Thanks A lot karine for your help :)

    Comment by Anu | March 26, 2009 | Reply

  3. It was a pleasure doing this!
    Karine

    Comment by Karine | March 26, 2009 | Reply

  4. Thanks for the post, it’s well.

    But I have a problem because i have an error on the function ResponseCallback
    private void ResponseCallback(IAsyncResult asyncResult)
    {
    HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
    HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult);

    The message est SecurityExcetion. Someone could explain if I need to authorize something on my sharepoint site to use WebService.

    Thanks so much, if someone have an idea.

    Comment by Fabien | April 2, 2009 | Reply

  5. Hi Fabien,
    It is possible you have specified a wrong url when instantiating the web request. The best way to check is to open a internet browser and to enter the URL to the Lists.asmx you use in your code.

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(
    new Uri(siteUrl + “/_vti_bin/Lists.asmx”, UriKind.Absolute));

    Also check the name of the list you use in your soap envelope.

    Can you try this out?

    Comment by Karine | April 6, 2009 | Reply

  6. Great post and code. Thanks for this.

    Comment by Tony Bierman [MVP WSS] | April 17, 2009 | Reply

  7. Thanks for the post it is very well.

    But I have a problem with the Application. I don’t have any Connection between the Category List and the Picture Libray. I don’t get any Errors when i debug it.

    If i press the button in the horizontal Listbox, i don’t see any Pictures in the Viewer. I think the buttons don’t have any function.

    Thanks so much if anybody have an idea…

    Comment by Christian | June 22, 2009 | Reply

  8. Hi Christian,
    Clicking the horizontal list box will populate the vertical list box. Clicking the vertical list box will populate the Viewer (large image).
    If you don’t have a connection between the Category list and the Picture Library, then I don’t know how you want to populate the vertical list box.
    Can you explain which functionality you need?
    Karine

    Comment by Karine | June 22, 2009 | Reply

    • Hi Karine,

      thanks for the fast post. I click on a category button in the horizontal list box but it won’t populate the vertical list box and i don’t know why??? There is no function…

      If i have only Pictures without categories, it will be run correctly…

      thanks
      Christian

      PS: My steps are:

      - I made a picture library with the name pictures
      - than a custom List with the name “categorie”
      - I made a lookup field in the picture library referencing to the categorie List
      - I set the categories to the pictures

      Have I to make any Changes in the Code because of the “categorie” List?

      Comment by Christian | June 22, 2009 | Reply

      • Hi Christian,
        If you selected the correct lists in the custom tool pane of the web part, everything should work. The lists selected in the toolpane are passed to the silverlight control and are used to execute the HttpWebRequest.
        Karine

        Comment by Karine | June 22, 2009

      • I think it should work, but it doesn’t

        Thank you so much…

        Christian

        Comment by Christian | June 22, 2009

  9. Hi Karine,
    My scenario is like this:

    I have created a silverlight grid(with many controls inside the grid) and i have put it on a custom aspx page in my SharePoint site.
    Now what I need is “the name of the person who has logged into SharePoint site should automatically get filled in the TEXTBOX control of my Silverlight grid”..
    usually i wud have used SPSite appSite = SPControl.GetContextSite(Context);SPWeb myWeb = SPControl.GetContextWeb(Context);txtBox1.Text = myWeb.CurrentUser.Name.ToString();
    But I cant use object model since ShaarePoint dll’s cant be added into silverlight project…
    Is there any solution for this??

    Thanks in Advance,
    Akhil

    Comment by Akhil | July 9, 2009 | Reply

  10. Hi Akhil,

    Can you let me know how you place your Silverlight control on your page? Are adding it declaratively as follows:

    Or do you add the Silvelright control in code?
    If you can let me know the above, I will be able to help you out.

    Karine

    Comment by Karine | July 9, 2009 | Reply

  11. I am new to this but what is the process of getting this installed into sharepoint once I have downloaded the code from above. Do i have to make any changes to the code besides pointing it to the correct image libraries? Do I have to create a wsp and then deploy it like a typical wsp into sharepoint ? Just a bit confused so any help would be great.

    Comment by Anhal Bravo | November 7, 2009 | Reply

  12. Hi Anhal,
    All necessary parts for a SharePoint solution are there:
    - a Silverlight application
    - a web part
    - feature manifests
    - solution manifests
    You just have to generate a .wsp file out of this. For this purpose you can use WSP Builder or you generate the .ddf file manually.
    Karine

    Comment by Karine Bosch | November 8, 2009 | Reply

  13. Awesome…Thank you very much. i will give it a shot.

    Comment by Anhal Bravo | November 8, 2009 | Reply

  14. Karine,
    One last question..you have been very helpful..Thank you ! Can you tell me what the name of the solution is that I need to open and then build the wsp from? I guess I’m still a little confused on what to do after I extract the files. I see a SharePoint and a xmal directory each with solutions in them. If I try to open those solutions up in VS 2008 I get a project is not supported by this installation. I have installed the pre-reqs as stated in the article above. Is there anything else that you can see that I would need? Any other guidance would be greatly appreciated

    Comment by Anhal | November 8, 2009 | Reply

  15. Anhal,
    To be able to generate the wsp, you have to open the web part project with name SL.SharePoint.MediaViewer but it is possible it has been made using STSDEV or VSEWSS 2.1.
    For the Silverlight project, this is a Silverlight 2 project, so you will have to have Silverlight 2 installed.

    Karine

    Comment by Karine Bosch | November 9, 2009 | Reply

  16. Karine,
    I have silverlight 2 and 3 installed on the server. I can open the project and create the wsp using the wspbuilder but when I add the webpart to the page it say’s the file you imported is not valid.

    I’m sorry for all the questions, I just feel that this is the best solution out there and am very interested in getting this insatlled on our site.

    using vs 2008.
    moss 2007
    .net framework 3.5 is installed
    silverlight 2 tools as well.

    Thanks again for your patience as I learn

    Comment by Anhal | November 9, 2009 | Reply

  17. ok, it seems all required parts are installed. Can you please tell me the exact error message when adding the webpart to a SharePoint page?

    Comment by Karine Bosch | November 10, 2009 | Reply

  18. Thanks Karine,

    Basically what I’m doing is opening up the project as you stated before. Using wsp builder to build the wsp. Using stadm to add and deploy the wsp to my server and then when I select the wsp from the web part page to be added to a page I get:”Unable to add selected web part. Silverlight media viewer: The file you imported is not valid, Verify that the file is a web part description file and that it contains well-formated xml”

    I hope that helps.

    Thanks again.

    Comment by Anhal | November 10, 2009 | Reply

  19. A .wsp file is a solution that can be deployed using stsadm. A webpart file has a .webpart extension and is part of a feature within that solution. WebParts can be added on a web part pages through the Web Part gallery, which never contains .wsp files. So I don’t understand how you are able to add a .wsp to a web part page.
    But coming to the error: it seems like the file doesn’t contain XML. Did you modify the .webpart file? Or another XML file?

    Comment by Karine Bosch | November 10, 2009 | Reply

  20. If I just try to upload the mediaviewer webpart file I get a similiar error. I must be going about this the wrong way.
    I extract all the files.
    I open up the project and create a wsp.
    I add and deploy the wsp via stadm.
    I open up sharepoint – site settings – web parts
    Select upload
    browse and upload the webpart file that was part of the project.
    go to a sharepoint page.
    add the Sharepoint media View webpart

    and then I get the error.

    I must be way off base but am obviously new at this.

    Thanks again for all your help you have been FANTASTIC !!

    Comment by Anhal | November 10, 2009 | Reply


Leave a comment