Skip to main content

Weblog Ton Stegeman [MVP]

Go Search
Home
  

ODC 2008
If you have a question or suggestion, please contact me through Windows Live Messenger.
My status: .

If I am not online, please send me an e-mail.
Weblog Ton Stegeman [MVP] > Posts > My First Silverlight part 4 – Create the Silverlight application
My First Silverlight part 4 – Create the Silverlight application

This is the final part of a series on Silverlight development in SharePoint. In the first part I have described how to solution works for a user and how to prepare your SharePoint server and development machine for Silverlight development. The second post describes how to create the WCF service that gets the news items from SharePoint. In the third post we have created a web part this will act as a host for our Silverlight application. And this is the final part. In this part we finally get to doing some Silverlight work. As I have described in the first post, this article is not about Silverlight itself, but mainly about creating a working solution in SharePoint that uses SharePoint data.

Part 1 - Introduction and preparation
Part 2 - Create the WCF News service
Part 3 - Create the Silverlight web part
Part 4 - Create the Silverlight application
Download the source code

Step 1 – Create a new project

We start off again with adding a new project to our Visual Studio solution. We now select the Silverlight Application template.

image

Next thing to do is to add a service reference to our WCF service. Right click the project and click Add Service Reference. In the dialog that appears, click the little arrow next to the Discover button. Select Services in Solution:

image

If you used the correct Visual Studio template while creating your WCF service, you should now be able to select the news service. Give your reference a name and click OK.

Please note: If you start with the wrong WCF project template (like I did, I used a regular class library first), it is more difficult to get the right service reference. After building and deploying my WCF assembly and SVC file, I tried to add a service reference by entering the url to my url in the LAYOUTS folder. Visual Studio then asked for my Discovery Credential, but I was not able to login. My problem was that I do not have anonymous access enbled on my web application and therefore Visual Studio asked me to login. One way to get around this is to start Fiddler before you click the Add Service Reference button. Fiddler then takes care of logging in and you will be able to add the service reference.

image

But if you use the correct template, you should not have this issue. And I no longer have it, thanks to Donald, who pointed me to the little arrow button.

Step 2 – Configure the WCF client

Before we can deploy our Silverlight control and web part to SharePoint, we need to configure the client binding and endpoint to the WCF service. We will need to do this in the web.config file for our SharePoint web application. I have added this system.servicemodel to my web.config file. In this configuration, the binding information is set. We use basicHttpBinding to connect to our WCF service. If this is the correct binding for you to use, depends on a number of factors. Besides the binding, the servicemodel element configures the endpoint. This endpoint selects the binding we previously configured and it specifies the contract to be used. Apart from that the address of our WCF service is configured in the config file.

<system.serviceModel>
  <bindings>
    <basicHttpBinding>
      <binding name="NewsService" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647">
        <security mode="TransportCredentialOnly" />
      </binding>
    </basicHttpBinding>
  </bindings>
  <client>
    <endpoint
        address="http://moss/_layouts/sbb/TST.BlackBelt.Silverlight.Services.News.svc" 
        binding="basicHttpBinding" 
        bindingConfiguration="NewsService" 
        contract="NewsService.NewsService" 
        name="NewsService" />
  </client>
</system.serviceModel>
 
The reason that I show this configuration before showing you how to use it, is that this configuration has an issue. as you can see, the endpoint is configured to connect to the WCF service at http://moss/_layouts/sbb/TST.BlackBelt.Silverlight.Services.news.svc. This has to be an absolute url. You cannot configure it to be relative. This means that your Silverlight application will always connect on this url and we will loose the context of the web part. Which we needed, to get the right news items, depending on the Scope setting of the user. This is solved by using dynamic binding configuration, which is covered in the next step.

Step 3 – Get the scope and filter values

When configuring the web part, we have selected one of the three available scopes. This scope value is passed to this Silverlight application by using the InitParameters property of the Silverlight control. The same applies to the filter value that was set by connecting a filter web part. We now first need to get the value of the scope and filter init parameters. To access these parameters, we use the InitParams property of the StartupEventArgs parameter in the Startup event. First open the cs file for App.xaml. In the constructor of the Page object, register a new event handler for the Startup event. In that event, save the InitParams value of the ‘e’ parameter. I added a readonly property ApplicationParameters to the App object to access these parameters:
 
public partial class App : Application
{
    IDictionary<string, string> _parameters;

    public App()
    {
        this.Startup += this.Application_Startup;
        InitializeComponent();
    }

    private void Application_Startup(object sender, StartupEventArgs e)
    {
        _parameters = e.InitParams;
        this.RootVisual = new Page();
    }

    internal IDictionary<string, string> ApplicationParameters
    {
        get { return _parameters; }
    }
}
We now have our Scope and Filter values available. I have implemented 2 properties on my Silverlight Page object to access these values:
public NewsService.NewsScope Scope
{
    get
    {
        if (((App)App.Current).ApplicationParameters == null)
            return NewsService.NewsScope.CurrentWeb;
        string scope = ((App)App.Current).ApplicationParameters["Scope"];
        return (NewsService.NewsScope)Enum.Parse(typeof(NewsService.NewsScope), scope, true);
    }
}

private NewsService.NewsFilter Filter
{
    get
    {
        if (((App)App.Current).ApplicationParameters == null)
            return null;
        if (((App)App.Current).ApplicationParameters.ContainsKey("FilterField") &&
            ((App)App.Current).ApplicationParameters.ContainsKey("FilterValue"))
        {
            string fieldName = ((App)App.Current).ApplicationParameters["FilterField"];
            string val = ((App)App.Current).ApplicationParameters["FilterValue"];
            if (!string.IsNullOrEmpty(fieldName) && !string.IsNullOrEmpty(val))
            {
                NewsService.NewsFilter newFilter = new NewsService.NewsFilter();
                newFilter.FieldName = fieldName;
                newFilter.Value = val;
                return newFilter;
            }
        }
        return null;
    }
}

Step 4 – Configure the AgDataGrid and get the data

In the Silverlight application I have used the agDataGrid. This is a free to use Silverlight control by DevExpress. It is a very easy to use grid. Through this link, you can find a number of interesting videos that help you to get started with using that control. After adding the references required, I have added the Grid to my page xaml:

<Grid x:Name="LayoutRoot" Background="White">
    <StackPanel Orientation="Vertical">
        <TextBlock x:Name="labelSite"></TextBlock>
        <grid:AgDataGrid x:Name="newsGrid" ColumnsAutoWidth="True" ShowGroupPanel="Visible" Height="500" 
                         PreviewTemplate="{StaticResource newsTemplate}" FocusedRowChanged="newsGrid_FocusedRowChanged">
            <grid:AgDataGrid.Columns>
                <grid:AgDataGridDateColumn FieldName="Modified"></grid:AgDataGridDateColumn>
                <grid:AgDataGridTextColumn FieldName="Title" Width="200"></grid:AgDataGridTextColumn>
                <grid:AgDataGridTextColumn FieldName="Author"></grid:AgDataGridTextColumn>
                <grid:AgDataGridColumn FieldName="Year"></grid:AgDataGridColumn>
                <grid:AgDataGridTextColumn FieldName="Month"></grid:AgDataGridTextColumn>
            </grid:AgDataGrid.Columns>
        </grid:AgDataGrid>
    </StackPanel>
</Grid>

In the Columns element, I have added the columns of the grid. The FieldName attribute references a public property of my NewsItem objects. Now it is time to connect to the service and get some news items. To do that, I added a new method GetNews to my Page object and call that from the constructor. First thing to do in the GetNews method is to configure a new binding object. As we have seen in the previous step, the binding is configured in the servicemodel configuration in web.config. We need to create a new binding at runtime however, to make sure we call our WCF service in the correct context. To get the new address for the binding, we get the url to the xap file from App.Current.Host.Source and construct a new url to the WCF service. Which in this case means that the WCF service and the XAP file need to deployed to the same folder.

public Page()
{
    InitializeComponent();
    GetNews();
}

private void GetNews()
{
    BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
    string context = App.Current.Host.Source.ToString().Substring(0, App.Current.Host.Source.ToString().ToLower().IndexOf("_layouts/"));

    Uri uri = new Uri(new Uri(context), "_layouts/sbb/TST.BlackBelt.Silverlight.WCFServices.News.svc");
    EndpointAddress address = new EndpointAddress(uri);

    NewsService.NewsServiceClient newsClient = new NewsService.NewsServiceClient(binding, address);
    NewsService.NewsFilter filter = this.Filter;
    if (filter == null)
    {
        newsClient.GetNewsItemsCompleted += new EventHandler<TST.BlackBelt.Silverlight.News.NewsService.GetNewsItemsCompletedEventArgs>(newsClient_GetNewsItemsCompleted);
        newsClient.GetNewsItemsAsync(Scope);
    }
    else
    {
        newsClient.GetNewsItemsFilteredCompleted += new EventHandler<TST.BlackBelt.Silverlight.News.NewsService.GetNewsItemsFilteredCompletedEventArgs>(newsClient_GetNewsItemsFilteredCompleted);
        newsClient.GetNewsItemsFilteredAsync(Scope, filter);
    }
}

Because every call to a service is asynchronous, the next thing to do is add event handlers for the 2 GetNewsItems methods. After adding the event handlers, the Page object will call one of the 2 asynchronous functions, depending on the availability of a filter. The code snippet below shows the implementation of these event handlers:

void newsClient_GetNewsItemsFilteredCompleted(object sender, 
             TST.BlackBelt.Silverlight.News.NewsService.GetNewsItemsFilteredCompletedEventArgs e)
{
    newsGrid.DataSource = e.Result;
}

void newsClient_GetNewsItemsCompleted(object sender, 
              TST.BlackBelt.Silverlight.News.NewsService.GetNewsItemsCompletedEventArgs e)
{
    newsGrid.DataSource = e.Result;
}

They are pretty simple, they just bind the result of the WCF service to our grid.

Step 5 – Implement the row preview

As we have seen in the first part of this series, users can select a row in the grid by clicking it. The row then expands and shows extra information about the announcement. This is an out of the box feature of the AgDataGrid. the DevExpress site has an excellent video on how to use this. Basically you add a new resource to your Silverlight app (I have included it in the page.xaml) and point the PreviewTemplate property to this resource. See the xaml snippet in step 4. This is the xaml for that template:

<UserControl.Resources>
    <DataTemplate x:Key="newsTemplate">
        <Grid x:Name="ItemDetails" Background="White">
            <Grid.RowDefinitions>
                <RowDefinition Height="25"></RowDefinition>
                <RowDefinition Height="25"></RowDefinition>
                <RowDefinition Height="25"></RowDefinition>
                <RowDefinition Height="25"></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="100"></ColumnDefinition>
                <ColumnDefinition Width="*"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <TextBlock Text="Created" Grid.Row="0" Grid.Column="0"></TextBlock>
            <TextBlock Text="{Binding Created}" Grid.Row="0" Grid.Column="1"></TextBlock>
            <TextBlock Text="Created by" Grid.Row="1" Grid.Column="0"></TextBlock>
            <TextBlock Text="{Binding CreatedBy}" Grid.Row="1" Grid.Column="1"></TextBlock>
            <TextBlock Text="Last modified" Grid.Row="2" Grid.Column="0"></TextBlock>
            <TextBlock Text="{Binding Modified}" Grid.Row="2" Grid.Column="1"></TextBlock>
            <TextBlock Text="Last modified by" Grid.Row="3" Grid.Column="0"></TextBlock>
            <TextBlock Text="{Binding Author}" Grid.Row="3" Grid.Column="1"></TextBlock>
        </Grid>
    </DataTemplate>
</UserControl.Resources>

It creates a new grid with 4 rows and 2 columns. After declaring the RowDefinitions and the ColumnsDefinitions, you create a number of TextBlock controls and add these to the grid.

Step 6 – Implement the item preview

Last thing to do to finialize our Silverlight News project, is to add the preview for the news item itself. The challenge for this is that the content of this news item is HTML. Silverlight cannot display HTML directly. Therefore I use a technique called a HTML bridge. As you have seen in the previous post, that webpart has added a HTML DIV caled ‘contentDiv’ to the page. The AgDataGrid has an event called FocusedRowChanged. We implement an event handler for that event:

private void newsGrid_FocusedRowChanged(object sender, DevExpress.Windows.Data.FocusedRowChangedEventArgs e)
{
    int currentIndex = e.NewRowHandle;
    if (currentIndex >= 0)
    {
        AgDataGrid grid = sender as AgDataGrid;
        NewsService.NewsItem item = grid.GetDataRow(currentIndex) as NewsService.NewsItem;
        HtmlDocument htmlDocument = HtmlPage.Document;
        HtmlElement contentDiv = htmlDocument.GetElementById("contentDiv");
        contentDiv.SetStyleAttribute("left", "300px");
        contentDiv.SetStyleAttribute("top", string.Format("{0}px", currentIndex*30 + 120));
        contentDiv.SetStyleAttribute("width", "300px");
        contentDiv.SetStyleAttribute("height", "300px");
        contentDiv.SetAttribute("innerHTML", item.Body);
    }
}

In this handler we get a reference to the current selected news item and a reference to the HtmlDocument in which our Silverlight control is used. Then we search for the contentDiv and set the innerHtml attribute of that DIV to the value of the Body property of the newsitem. What you also need to do is set the top of your DIV, to make the DIV move with the current selected row.

If you want to learn more about Html bridging, read the quickstart, or this FAQ.

Step 7 – Deploy the XAP file

The last thing to do is to deploy the xap file. There are several places where you can deploy a xap file. I deploy the xap file to the LAYOUTS folder, because it is easy to deploy from my WSP file.

Wrap up

That’s it for now. In this series of 4 blog posts we have created a Silverlight News web part that can be configured using the normal SharePoint web part configuration. It is aware of it’s context in SharePoint and is able to handle incoming filters. I hope you enjpyed reading it. I certainly enjoyed creating and writing it. I also hope it inspired you to write Silverlight applications in SharePoint. I think Silverlight is great technology, and it is very powerful in combination with SharePoint. If you know how to integrate both technologies. I hope I have achieved that in this series. If you have any questions, please let me know. And I would also be interested in seeing what SharePoint solution you will build in Silverlight.

Download the source code here.

Comments

ZIP File

Hi perfect article. I tried to find the zip file but did not get it!

Is there a secret link ? ;-)
at 1/27/2009 1:26 AM

Source code - zip file?

Hi Tom,
You mentioned that youy would provide the complete code to your tutorial but I can't see any attachments.
at 1/27/2009 2:04 AM

Link to the zip file

I just updated the blogpost and added the secret link:
http://www.codeplex.com/eoffice/Release/ProjectReleases.aspx?ReleaseId=22471

thanks for the reminder, Ton
Ton Stegeman at 1/28/2009 12:34 PM

Add Comment

Items on this list require content approval. Your submission will not appear in public views until approved by someone with proper rights. More information on content approval.

Title


Body *


Your city *


Type the name of the city you live in (making it easier to handle spam...)

CurrentDate *

Select the current date (see if this gives me fewer spam...)
Attachments

 Links

  SharePoint Object on CodePlex
  Screencast introducing SharePoint Objects
  Content by Type and Filter Web Parts on CodePlex
  Archive
  Archive (Calendar)

 My Latest Blog Posts

Scripting SharePoint 2007 setup: choices and conceptsUse SHIFT+ENTER to open the menu (new window).
Adventures in Visual Studio 2010: Migrate the Content By Type web part to SharePoint 2010Use SHIFT+ENTER to open the menu (new window).
Register SharePoint themes by using a featureUse SHIFT+ENTER to open the menu (new window).
SharePoint 2010 development on Windows 2008 Server R2 – Getting StartedUse SHIFT+ENTER to open the menu (new window).
New release SharePoint Objects: features and groupsUse SHIFT+ENTER to open the menu (new window).
Constructing the url to the SharePoint Edit Permissions pageUse SHIFT+ENTER to open the menu (new window).
Screencast: introduction to SharePoint ObjectsUse SHIFT+ENTER to open the menu (new window).
SharePoint 2007 and Reporting ServicesUse SHIFT+ENTER to open the menu (new window).
SharePoint Objects – Insight in usage of your SharePoint artifactsUse SHIFT+ENTER to open the menu (new window).
SharePoint 2007 Custom list schema and the Content Query Web PartUse SHIFT+ENTER to open the menu (new window).
SharePoint 2010 Silverlight Client Object Model – ExecuteQuery vs ExecuteQueryAsyncUse SHIFT+ENTER to open the menu (new window).
SharePoint 2010, the Client Object Models and Bing MapsUse SHIFT+ENTER to open the menu (new window).
Having fun with SharePoint 2010, Silverlight 3 and Bing MapsUse SHIFT+ENTER to open the menu (new window).
Connecting TFS 2010 projects to SharePoint sitesUse SHIFT+ENTER to open the menu (new window).
Adding a database to the SharePoint database Server using SPDatabaseUse SHIFT+ENTER to open the menu (new window).