Welcome to MSDN Blogs Sign in | Join | Help

I was asked a couple of times recently about WCF and SharePoint—i.e. how do you integrate a WCF service with SharePoint that is hosted in IIS? Well, I did a little digging around over the past few weeks to see what was out there and besides finding Sahil’s blog, which has some decent coverage on deploying WCF to the SharePoint hive, I didn’t find a single place where I had ‘the’ (or should I say ‘my’) perfect walkthrough for the following: deploying a WCF service to IIS and then integrating with SharePoint. (I tend to prefer this option as it maximizes my scalability/usability of the service—inside and out of SharePoint.) And while this perfect doc may exist, I grew a little impatient with my search and figured I would just put one together. So, hopefully you’ll find this useful.

That said, this blog will walk you through the following:

  1. Creation of a WCF service;
  2. Deployment of the WCF service to IIS 7.0;
  3. Creation of a SharePoint web part (ASP.NET web part); and
  4. Modification of the SharePoint web.config.

The core elements of my development environment are as follows:

  • Windows Server 2008
  • WSS 3/MOSS 2007 SP2
  • Visual Studio (VS) 2008 SP1
  • VSEWSS 1.3
  • IIS 7.0

Alright, let’s get started!

1. Creating the WCF Service

First, open VS 2008. Create a blank solution, as you’re going to add a couple of projects to it. Second, right-click the solution and select Add and then New Project. I have my VS instance optimized for C# and select WCF Service Application. Provide a name and location and click OK—see figure below.

image

A number of files are added to the solution. Of specific interest to you should be the IService.cs file and the Service.svc file (and the Service.svc.cs code-behind file). I renamed mine to be a little more intuitive to my example (which will be a simple tax calculator for three states plus federal tax—so essentially calculating net salary for three states)—see figure below. Note that you’ll need to make some amendments in the web.config file to ensure you’ve got the same service and class names in the that file.

image

The service contract (which will be located in the ISalary.cs file) is coded as below, which defines the one class I’m going to include within this service. The code for the contract is as follows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

[ServiceContract]
public interface ITaxCalculator
{

    [OperationContract]
    double GetData(double grossSalary, string state);

}

The actual meat of the salary calculation is as follows, which as per the contract will accept a double and string as parameters—returning a double for use in the service implementation.  You can see in the following code that I’m validating what state is being passed to the service and then setting a state tax level based on that state. I’m then making a calculation and returning the net salary.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

public class TaxCalculator : ITaxCalculator
{
    double netSalary = 0;
    double fedTaxRate = .25;
    double stateTaxRate = 0;
    double totalTax = 0;

    public double GetData(double grossSalary, string state)
    {
        if (state == "WA")
        {
            stateTaxRate = .08;
        }
        else if (state == "IL")
        {
            stateTaxRate = .05;
        }
        else if (state == "CA")
        {
            stateTaxRate = .12;
        }
        else if (state == "NY")
        {
            stateTaxRate = .14;
        }

        totalTax = (grossSalary * fedTaxRate) + (grossSalary * stateTaxRate);
        netSalary = grossSalary - totalTax;
        return netSalary;
    }

}

Before you move on to deploying your service, debug your service to make sure there are no errors. Once you’ve got no errors, you can move on to deploying the service.

Note: One thing I reiterate constantly when I present on SharePoint is testing often. This may mean using tools like Fiddler or just employing defensive testing practices such as debugging after each new amendment to your code. Either way, this is one of the best ways to understand not only what is happening with your code, but also to ensure you understand where things may go awry with your code.

2. Deploying the Service to IIS 7.0

With the service code done, the next thing you’ll want to do is deploy the service. This is a fairly simple process and requires you to open IIS 7.0. To do this, click Administrative Tools and select Internet Information Services 7.0. You’ll want to add a new web site, as we will deploy this as an isolated service, so right-click Sites and then select Add Web Site. You’ll then be prompted to add some information for your site. For example, you’ll need a Site name, you’ll need to set an application pool, you’ll need to set the virtual path (which will map to the .svc file we created earlier in this blog—which further references your class/service object), and then you’ll need to assign a port number that is not assigned to 80 (e.g. 774), which is likely your SharePoint server—see figure below. Note that you’ll also want to ensure you’ve got Authentication set properly, so click the Features View tab and select Authentication. Make sure Windows Authentication is enabled.

image

Once you’ve done this, click on the Content tab and you’ll see all of the different files that make up your deployed service in IIS. The figure below shows all of the different files and folders in my service. image

Note that I have not deployed the service DLL to the GAC; my service is being accessed from within the bin folder within the virtual directory mapping. This is evident from the directives in my Salary.svc file, which reads as follows:

<%@ ServiceHost Language="C#" Debug="true" Service="SalaryWCFApp.CalcTax" CodeBehind="Salary.svc.cs" %>

You could deploy your service DLL to the GAC if you wanted, but then you’d need to have a fully qualified reference in your .svc file that pointed to the GAC-deployed DLL.

To test run your service, right-click Salary.svc (or whatever your .svc file is called) and select Browse. This will invoke the service. If you’ve deployed correctly and everything is working, you should be prompted with something similar to the below—the invoked service. If your service invokes correctly, you can now move onto creating the SharePoint web part.

image

3. Creating the SharePoint Web Part

To add the web part project (as mentioned earlier you should have VSEWSS 1.3 installed), right-click the solution and select Add, New Project, select the SharePoint node, and then select the Web Part project template. You’ll need to provide a name for your web part—see figure below.

image

Note that I typically delete the web part item from the web part project and then re-add the web part as an item and then provide the name I want. This is quirky but it is because the default name of the web part is WebPart1, and I find it quicker and easier to just remove and re-add the web part. I called my new web part TaxCalculatorWP. After creating the project, the following files are created for you.

image

I added some code into the TaxCalculatorWP.cs file, which is listed below. The gist of this code is fairly straightforward: it represents a UI that comprises some labels, textboxes, and buttons, which will accept some input parameters, call the WCF tax calculation service and then return (and set one of the textbox Text properties with) the net salary (as a double).

using System;
using System.Runtime.InteropServices;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Serialization;

using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;

namespace TaxCalculator
{
    [Guid("e3783c6e-e74a-4871-8865-72de21de4916")]
    public class TaxCalculatorWP : System.Web.UI.WebControls.WebParts.WebPart
    {

        Label lblTitle = new Label();

        Label lblGrossSalary = new Label();
        TextBox txtGrossSalary = new TextBox();

        Label lblStates = new Label();
        ListBox lstbxStates = new ListBox();

        Label lblNetSalary = new Label();
        TextBox txtNetSalary = new TextBox();

        Button btnCalcSalary = new Button();
        Button btnClearFields = new Button();

        string strState = "";
        double grossSalary = 0;
        double netSalary = 0;

        public TaxCalculatorWP()
        {
        }

        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            lblTitle.Font.Bold = true;
            lblTitle.Font.Size = 14;
            lblTitle.Height = 25;
            lblTitle.Width = 300;
            lblTitle.Text = "Salary Calculator";
            this.Controls.Add(lblTitle);
            this.Controls.Add(new LiteralControl("<br>"));
            this.Controls.Add(new LiteralControl("<br>"));

            lblGrossSalary.Font.Bold = true;
            lblGrossSalary.Height = 25;
            lblGrossSalary.Width = 100;
            lblGrossSalary.Text = "Grs. Salary:";
            this.Controls.Add(new LiteralControl("  "));
            this.Controls.Add(lblGrossSalary);

            txtGrossSalary.Height = 25;
            txtGrossSalary.Width = 75;
            this.Controls.Add(txtGrossSalary);
            this.Controls.Add(new LiteralControl("<br>"));
            this.Controls.Add(new LiteralControl("<br>"));

            lblStates.Font.Bold = true;
            lblStates.Height = 25;
            lblStates.Width = 100;
            lblStates.Text = "States:";
            this.Controls.Add(new LiteralControl("  "));
            this.Controls.Add(lblStates);

            lstbxStates.Items.Add("WA");
            lstbxStates.Items.Add("IL");
            lstbxStates.Items.Add("CA");
            lstbxStates.Items.Add("NY");
            lstbxStates.Height = 40;
            lstbxStates.Width = 150;
            this.Controls.Add(lstbxStates);
            this.Controls.Add(new LiteralControl("<br>"));
            this.Controls.Add(new LiteralControl("<br>"));

            lblNetSalary.Font.Bold = true;
            lblNetSalary.Height = 25;
            lblNetSalary.Width = 100;
            lblNetSalary.Text = "Net Salary:";
            this.Controls.Add(new LiteralControl("  "));
            this.Controls.Add(lblNetSalary);

            txtNetSalary.Height = 25;
            txtNetSalary.Width = 75;
            this.Controls.Add(txtNetSalary);
            this.Controls.Add(new LiteralControl("<br>"));
            this.Controls.Add(new LiteralControl("<br>"));

            btnCalcSalary.Text = "Calc. Salary";
            btnCalcSalary.Height = 10;
            btnCalcSalary.Height = 40;
            this.Controls.Add(btnCalcSalary);
            this.Controls.Add(new LiteralControl("  "));

            btnClearFields.Text = "Clear Fields";
            btnClearFields.Height = 10;
            btnClearFields.Height = 40;
            this.Controls.Add(btnClearFields);

            btnCalcSalary.Click += new EventHandler(btnCalcSalary_Click);
            btnClearFields.Click += new EventHandler(btnClearFields_Click);

        }

        void btnClearFields_Click(object sender, EventArgs e)
        {
            txtGrossSalary.Text = "";
        }

        void btnCalcSalary_Click(object sender, EventArgs e)
        {
            strState = lstbxStates.Text;
            grossSalary = Convert.ToDouble(txtGrossSalary.Text);

            TaxCalculator.MyTaxCalcWCF.SalaryClient proxy = new TaxCalculator.MyTaxCalcWCF.SalaryClient();
            netSalary = proxy.GetData(grossSalary, strState);
            txtNetSalary.Text = "$" + netSalary.ToString();
            proxy.Close();
        }
    }
}

If you want to have an intuitive title and description for your web part, then you’ll need to edit the .webpart file, which you can see I’ve done as per the below. You will see this when you browse for the deployed web part in the SharePoint Web part Gallery.

<?xml version="1.0" encoding="utf-8"?>
<webParts>
  <webPart xmlns="
http://schemas.microsoft.com/WebPart/v3">
    <metaData>
      <!--
      The following Guid is used as a reference to the web part class,
      and it will be automatically replaced with actual type name at deployment time.
      -->
      <type name="e3783c6e-e74a-4871-8865-72de21de4916" />
      <importErrorMessage>Cannot import TaxCalculatorWP Web Part.</importErrorMessage>
    </metaData>
    <data>
      <properties>
        <property name="Title" type="string">TaxCalculatorWP Web Part</property>
        <property name="Description" type="string">Web Part that calculates tax.</property>
      </properties>
    </data>
  </webPart>
</webParts>

You can debug your web part before deploying if you want to by clicking F5. Just remember to click Yes on the Script Debugging Disabled dialog, as per the below figure—which you can opt out of as well if you choose. When you deploy, VS will attach to w3wp and then you can deploy and test the web part.

image

At this point, then, you should have two projects in your solution—similar to the below figure. You can now build and deploy your web part to your local SharePoint server. To do this, right-click the web part project and select Deploy. You’ll note in the VS output window that a number of actions are invoked (e.g. checking for conflicts, resetting IIS, etc.).

image

The one thing I like about VSEWSS 1.3 is that I can amend and re-deploy the web parts and each time VS finds and prompts the resolution of conflicts—as per the figure below. This makes it easy for me to test and redeploy my web parts. For example, I made a number of enhancements when creating my ASP.NET UI, and this feature came in handy when I was doing this. 

image

Note that you might get an error on deployment if you don’t remove the pkg directory. The error will read something like this: “Value does not fall within expected range.” To get past this error, remove the pkg directory and redeploy.

 image

At this point, you should have created, tested and deployed your service and also created, tested and deployed your SharePoint web part. We’re now going to move on to the final stage of the blog: editing the web.config file.

4. Editing the Web.config File

Now this is where things get interesting. Because you’ve deployed the service, and you’ve deployed the web part but there is this little thing called the web.config (your SharePoint server web.config that is) that needs to contain some information for the service to actually run. If you don’t believe me, don’t add anything to the web.config file and then watch how you’ll never get past the postback of the web part. Okay, just take my word for it.

For the above service to work, I added a number of things to the system.servicemodel element in SharePoint’s web.config file. Specifically, I added an entry for the serviceHostingEnvironment with aspNetCompatabilityEnabled set to true, I added the actual service (SalaryWCFApp.CalcTax) along with a number of properties, I added the behavior, I added the bindings, and then I lastly added a client configuration element as well. Note that this information exists; it just lives within the web.config file of your service and the app.config file of your web part. For example, the information for the client element lives in your web part app.config file. Note that if you add an entry for behavior and don’t add a separate entry for it, SharePoint will complain. And to be frank, figuring out what needs to go into the web.config file is probably the trickiest part of this whole blog—and the part that is, in my humble opinion, under-documented.

To edit your SharePoint server web.config navigate to your SharePoint’s root directory (e.g. in my case this is c:\Inetpub\wwwroot\wss\VirtualDirectories\80) and then right-click the web.config and select Edit in Visual Studio 2008. In your web.config, you’ll want to make similar edits that I have in mine below. Note you also may want to add different elements as well.

<system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" /> 
<services>

  <service name="SalaryWCFApp.CalcTax"  behaviorConfiguration="SalaryWCFApp.Service1Behavior">
  <endpoint address="
http://stefoxdemo.redmond.corp.microsoft.com:774/Salary.svc" binding="wsHttpBinding" bindingConfiguration="NtlmBinding" contract="SalaryWCFApp.ISalary">
    <identity>
      <dns value="localhost" />
    </identity>
  </endpoint>
</service>

</services>

<behavior name="SalaryWCFApp.Service1Behavior">
          <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="true" />
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>

<bindings> 

<wsHttpBinding>
    <binding name="WSHttpBinding_ISalary" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
      <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
      <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
      <security mode="Message">
        <transport clientCredentialType="Windows" proxyCredentialType="None" realm="" />
        <message clientCredentialType="Windows" negotiateServiceCredential="true" algorithmSuite="Default" establishSecurityContext="true" />
      </security>
    </binding>
  </wsHttpBinding>
</bindings>

<client>
  <endpoint address="
http://localhost:774/Salary.svc" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ISalary" contract="MyTaxCalcWCF.ISalary" name="WSHttpBinding_ISalary">
    <identity>
      <dns value="localhost" />
    </identity>
  </endpoint>
</client>

</system.serviceModel>

So, with all of the above, you should have a fully functional ASP.NET web part that is deployed to SharePoint that you can use to calculate the net salary of a user entering some data. You can test out your salary calculator by adding the web part to a SharePoint site from your Web part Gallery. The result would likely look like the web part in the figure below: you enter in some numbers for the gross salary, select a state, and then click Calc. Salary. And shazam, SharePoint calls the WCF service and calculates the net salary.

image

Now while I know you’re trying to contain your enthusiasm, look beyond the simplicity of the app to the broader ‘pattern.’ This pattern (the integration of an ASP.NET web part with SharePoint and a WCF service deployed to IIS) can be reused across your other web parts or SharePoint applications.

Happy coding!

Steve

One of the key things you’ll likely want to do with SharePoint is interact a lot with lists. In earlier posts on this blog, I’ve discussed adding data to a SharePoint list using both custom and native SharePoint services (i.e. services that ship with either WSS or MOSS—when I say SharePoint I include both of these SharePoint pieces). A recent post got me thinking about how to not only retrieve data from SharePoint, but then having the ability to take that data offline (e.g. save to a file) or to use it in-memory for other types of processing (e.g. filtering/management within a UI), so I put together a small application that does a couple of things:

1. Retrieves data from a SharePoint list using the Lists web service (a service native to SharePoint 2007);

2. Uses XML to add items to a custom object; and

3. Uses LINQ to query the object and filter/manage into a XAML UI.

All this using the latest Visual Studio 2010 Beta bits.

First, let’s take a quick look at the SharePoint list. It’s called “Products” and lives on my test SharePoint server (“http://stefoxdemo”). You can see a cropped screenshot of the list. It’s pretty simple and should be thought of as such because we really just want to show data coming from the list—not hoards of data.

image

If you’ve never created a list, you open SharePoint, navigate to your home page, click View All Site Content, click Create, and under Custom Lists click Custom List. Complete the Name and Description fields for your new list and click Create.

Now that you’ve created the list, open Visual Studio 2010 and click File, New Project. Select WPF Application and provide a name (e.g. MySharePointData) and location for your project and click OK—see the figure below.

image

We’re going to keep things simple here and add nine controls to the WPF UI:

  • Four labels
  • One listbox
  • Two textboxes
  • Two buttons

If you inspect the XAML code below, you’ll see that this is the resulting code that was generated from me dragging and dropping controls from the VS 2010 Toolbox onto the XAML designer. Check out the different properties of the controls.

<Window x:Class="MySharePointData.Window1"
        xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="SharePoint Data" Height="321" Width="356">
    <Grid>
        <Button Content="Get Data" Height="23" HorizontalAlignment="Left" Margin="54,233,0,0" Name="btnGetData" VerticalAlignment="Top" Width="75" Click="btnGetData_Click" />
        <Button Content="Exit" Height="23" HorizontalAlignment="Left" Margin="163,233,0,0" Name="btnExit" VerticalAlignment="Top" Width="75" Click="btnExit_Click" />
        <Label Content="My SharePoint List Data" Height="28" HorizontalAlignment="Left" Margin="21,12,0,0" Name="lblFormTitle" VerticalAlignment="Top" Width="232" FontSize="14" FontWeight="Bold" />
        <Label Content="Product Name:" Height="28" HorizontalAlignment="Left" Margin="21,64,0,0" Name="lblTitle" VerticalAlignment="Top" Width="89" />
        <Label Content="Product Num:" Height="28" HorizontalAlignment="Left" Margin="21,110,0,0" Name="lblProductNum" VerticalAlignment="Top" Width="120" />
        <Label Content="Product Sales:" Height="28" HorizontalAlignment="Left" Margin="21,161,0,0" Name="lblSales" VerticalAlignment="Top" Width="120" />
        <ListBox SelectionChanged="lstBxProducts_SelectionChanged" Height="37" HorizontalAlignment="Left" Margin="116,55,0,0" Name="lstBxProducts" VerticalAlignment="Top" Width="196" />
        <TextBox Height="25" HorizontalAlignment="Left" Margin="116,110,0,0" Name="txtBxProductNum" VerticalAlignment="Top" Width="196" IsEnabled="False" />
        <TextBox Height="25" HorizontalAlignment="Left" Margin="116,161,0,0" Name="txtBxSales" VerticalAlignment="Top" Width="196" IsEnabled="False" />
    </Grid>
</Window>

The resulting UI from the above code looks like the following:

image

Once you’ve created your WPF UI, you now will want to add some code-behind to actually do something. The first thing you’ll want to do is to add the SharePoint Lists web service. This provides you with the ability to interact with lists and data in your lists. You can see from the figure below the subset of methods that are available to you. To add the web service to the VS 2010 project, right click References and select Service References. Click Advanced and then click Add Web Reference, and then discover the Lists web service by clicking Web Services on the Local Machine (or entering the service URL in the URL: field, e.g. “http://stefoxdemo/_vti_bin/Lists.asmx”). Provide a name for the service reference (e.g. SPListGetData) and click Add Reference. You now have a reference added to the Lists web service and can use it in code.

image

One thing I used in my solution was a custom object, which I used to store (in-memory) a list collection of the different elements in my list. This allowed me to use the collection of objects later and run a simple LINQ query against it. To add an object, right-click the project node and select Add, and then select Class.

image

The custom object comprises three strings (mapping to the three parts of my SharePoint list): 1) a title (or product name), 2) a product number, and 3) a sales figure. The code I have for my custom object is as follows—note that I’ve set the properties on my object to both read/write, but in this file all we’re really doing is reading.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MySharePointData
{
   public class MySPData
    {
        public string clsTitle { get; set; }
        public string clsProductNum { get; set; }
        public string clsSales { get; set; }
    }
}

After you’ve added the class to the project, you can now add some of the heavier-weight code. This code is going to set some class-level variables, use the Lists web service to get the data in my list, iterate through the list and use XLINQ to create and populate the custom object, and then if the user changes the selection on the list the fields will be updated through a LINQ query.

Let’s start with the class-level variables. I’ve added a string to represent the selected item in the listbox, a list collection object (which will be in-memory collection object) called lstSPData, and the declaration of an XDocument (called myXMLDoc), which will be used as the document to which I’ll add the items from the SharePoint list.


string strSelectedItem = "";

List<MySPData> lstSPData = new List<MySPData>();

XDocument myXMLDoc = new XDocument();
 

Once you’ve added these three top-level items to the class, you’ll want to double-click on one of the buttons you added to the UI and then add some code for that. In my case, the name of the button was btnGetData and the subsequent event was called btnGetData_Click. The code below shows what I added into that method. There are a few things that are interesting in the code. For example, note that one of the first things we do is create a proxy for the web service, pass my default credentials to call the service (SharePoint must know who you are even at the service level), and then uses an XMLNode object to get all of the items from the Products list. You’ll then also see that I’ve began creating an XML object, but creating an XElement and then setting the declaration type of the XML Document I created earlier. Then to get the data out of the object, you iterate through the nodes and grab the inner text of the XML. In this case, you need to be sure you’re pulling from the SharePoint XML (this is the “ows_Title,” “ows_ProductNum,” and “ows_Sales”). From there on, two major things are happening. The first is that I’m reassigning the XML data from the object being passed back from the web service call with some XML that I’m more comfortable with (and a structure that I prefer to work with), and then I’m creating an instance of the custom object I created and then am populating the list collection for that custom object as I iterate through each of the XML nodes of the returned XML data. You’ll then see two lines of code lying outside of the foreach statement, and this is where I’m first adding the more comfortable version of the XML data to my originally-created XML document, and then I’m saving that XML document to a file share.

private void btnGetData_Click(object sender, RoutedEventArgs e)
{

    MySharePointData.SPListGetData.Lists proxy = new MySharePointData.SPListGetData.Lists();
    proxy.Credentials = System.Net.CredentialCache.DefaultCredentials;
    proxy.Url = "
http://stefoxdemo/_vti_bin/Lists.asmx";

    XmlNode myLIstItems = proxy.GetListItems("Products", null, null, null, null, null, null);

    XElement newRootElement = new XElement("NewData");    

    myXMLDoc.Declaration = new XDeclaration("1.0", "utf-8", "true");

    foreach (XmlNode outerNode in myLIstItems.ChildNodes)
    {

        if (outerNode.NodeType.Equals(System.Xml.XmlNodeType.Element))
        {

            foreach (XmlNode node in outerNode.ChildNodes)
            {

                if (node.NodeType.Equals(System.Xml.XmlNodeType.Element))
                {

                    XmlNode listFieldTitle = node.Attributes.GetNamedItem("ows_Title");
                    XmlNode listFieldProductNum = node.Attributes.GetNamedItem("ows_ProductNum");
                    XmlNode listFieldSales = node.Attributes.GetNamedItem("ows_Sales");

                    string strListFieldTitle = listFieldTitle.InnerText;
                    string strListFieldProductNum = listFieldProductNum.InnerText;
                    string strListFieldSales = listFieldSales.InnerText;

                    XElement xmlData = new XElement("MyData",
                            new XElement("Title", strListFieldTitle),
                            new XElement("ProductNum", strListFieldProductNum),
                            new XElement("Sales", strListFieldSales));

                    newRootElement.Add(xmlData);
                    lstBxProducts.Items.Add(strListFieldTitle);

                    MySPData clsSPDataInstance = new MySPData();

                    clsSPDataInstance.clsTitle = strListFieldTitle;
                    clsSPDataInstance.clsProductNum = strListFieldProductNum;
                    clsSPDataInstance.clsSales = strListFieldSales;
                    lstSPData.Add(clsSPDataInstance);

                }
            }
        }

    }

    myXMLDoc.Add(newRootElement);
    myXMLDoc.Save("c:\\SPData\\MySharePointData.xml");
}

The key takeaway from this, though, is that there are now two data structures in this app: one that I’ve created as an XML file and saved to disk (or alternatively could be used to inject into another program, say a Word document through Open XML); and one that now represents a list collection of the custom object—this is my in-memory version of the data that I can use for querying.

The next bit of code we’ll look at handles the user interacting with the listbox; that is, when the user changes their selection this will invoke the lstBxProducts_SelectionChanged event, which triggers the following code. This code is fairly straight-forward. I’ve set three string variables as temporary variables, grabbed the currently selected item in the listbox (and am storing it in strSelectedItem) and then am using a LINQ query to get the other data in the record that maps to the product name selected in the listbox. Given the fact that this results in a list of one record, I’m then using the First method to grab the first (and only) element in that record and assigning the text property of the controls to those strings.

private void lstBxProducts_SelectionChanged(object sender, SelectionChangedEventArgs e)
   {
       string strTempTitle = "";
       string strTempProductNum = "";
       string strTempSales = "";

       strSelectedItem = lstBxProducts.SelectedItem.ToString();

       var lstXMLObject = from lstXML in lstSPData
                          where lstXML.clsTitle == strSelectedItem
                          select new
                          {
                              strTempTitle = lstXML.clsTitle,
                              strTempProductNum = lstXML.clsProductNum,
                              strTempSales = lstXML.clsSales

                          };

       txtBxProductNum.Text = lstXMLObject.First().strTempProductNum;
       txtBxSales.Text = lstXMLObject.First().strTempSales;

   }

The finished product results in the following cool and slick (okay, not so much…but functional) WPF UI first getting SharePoint list data and then allowing you to query that data in the context of your whiz-bang UI. Note that it also saves the XML to your local file system. When you F5, you should see the following UI. Click the Get Data button, which triggers the service call. This will populate the Product Name listbox and subsequently allow you to click on an item in the listbox and filter on that item.

image

If you browse to the location where you saved your XML file (in my case this was c:\SPData), you will find an XML file that hopefully resembles the data you have in your list.

<?xml version="1.0" encoding="utf-8"?>
<NewData>
  <MyData>
    <Title>ALK-Bike Crank</Title>
    <ProductNum>0398021</ProductNum>
    <Sales>$209,002.98</Sales>
  </MyData>
  <MyData>
    <Title>OUU-Bicycle Riding Helmet</Title>
    <ProductNum>38271920</ProductNum>
    <Sales>$901,199.23</Sales>
  </MyData>
  <MyData>
    <Title>YTT-Road Bicycle Wheel</Title>
    <ProductNum>03929901</ProductNum>
    <Sales>$890,872.12</Sales>
  </MyData>
  <MyData>
    <Title>HGT-Off-Road Bike Gear Pack</Title>
    <ProductNum>3746281</ProductNum>
    <Sales>$459,922.10</Sales>
  </MyData>
  <MyData>
    <Title>BKP-Bianchi Racing Bike</Title>
    <ProductNum>3902443</ProductNum>
    <Sales>$2,019,100.29</Sales>
  </MyData>
</NewData>

Lastly, the Exit button you see on my UI also calls a method; basically a simple method to exit the application. The code for this is as follows:

private void btnExit_Click(object sender, RoutedEventArgs e)
{
    Application.Current.Shutdown();
}

And that’s it! Wasn’t that fun? I thought so. For your reference, the entire code sample from the core Windows.xaml.cs file is pasted below.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Xml;
using System.Xml.Linq;

namespace MySharePointData
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        string strSelectedItem = "";

        List<MySPData> lstSPData = new List<MySPData>();

        XDocument myXMLDoc = new XDocument();

        private void btnGetData_Click(object sender, RoutedEventArgs e)
        {
           
            MySharePointData.SPListGetData.Lists proxy = new MySharePointData.SPListGetData.Lists();
            proxy.Credentials = System.Net.CredentialCache.DefaultCredentials;
            proxy.Url = "
http://stefoxdemo/_vti_bin/Lists.asmx";

          
            XmlNode myLIstItems = proxy.GetListItems("TR8", null, null, null, null, null, null);
            //Creation
            XElement newRootElement = new XElement("NewData");
            myXMLDoc.Declaration = new XDeclaration("1.0", "utf-8", "true");

            foreach (XmlNode outerNode in myLIstItems.ChildNodes)
            {

                if (outerNode.NodeType.Equals(System.Xml.XmlNodeType.Element))
                {

                    foreach (XmlNode node in outerNode.ChildNodes)
                    {

                        if (node.NodeType.Equals(System.Xml.XmlNodeType.Element))
                        {

                            XmlNode listFieldTitle = node.Attributes.GetNamedItem("ows_Title");
                            XmlNode listFieldProductNum = node.Attributes.GetNamedItem("ows_ProductNum");
                            XmlNode listFieldSales = node.Attributes.GetNamedItem("ows_Sales");

                            string strListFieldTitle = listFieldTitle.InnerText;
                            string strListFieldProductNum = listFieldProductNum.InnerText;
                            string strListFieldSales = listFieldSales.InnerText;

                            XElement xmlData = new XElement("MyData",
                                    new XElement("Title", strListFieldTitle),
                                    new XElement("ProductNum", strListFieldProductNum),
                                    new XElement("Sales", strListFieldSales));

                            newRootElement.Add(xmlData);
                            //tempAll = strListFieldTitle + " " + strListFieldProductNum + " " + strListFieldSales;
                            lstBxProducts.Items.Add(strListFieldTitle);

                            MySPData clsSPDataInstance = new MySPData();

                            clsSPDataInstance.clsTitle = strListFieldTitle;
                            clsSPDataInstance.clsProductNum = strListFieldProductNum;
                            clsSPDataInstance.clsSales = strListFieldSales;
                            lstSPData.Add(clsSPDataInstance);

                        }
                    }
                }

            }

            myXMLDoc.Add(newRootElement);
            myXMLDoc.Save("c:\\SPData\\MySharePointData.xml");

            MessageBox.Show("XML Data Saved to File!");
        }

        private void btnExit_Click(object sender, RoutedEventArgs e)
        {
            Application.Current.Shutdown();
        }

        private void lstBxProducts_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            string strTempTitle = "";
            string strTempProductNum = "";
            string strTempSales = "";

            strSelectedItem = lstBxProducts.SelectedItem.ToString();

            var lstXMLObject = from lstXML in lstSPData
                               where lstXML.clsTitle == strSelectedItem
                               select new
                               {
                                   strTempTitle = lstXML.clsTitle,
                                   strTempProductNum = lstXML.clsProductNum,
                                   strTempSales = lstXML.clsSales

                               };

            txtBxProductNum.Text = lstXMLObject.First().strTempProductNum;
            txtBxSales.Text = lstXMLObject.First().strTempSales;

        }
    }
}

Happy coding!

Over the past few months, we’ve received some very positive feedback on the OBA Sample Application Kits that we’ve released. Well, today we release to web the latest in the family of kits, the OBA Sample Application Kit for SAP & Siebel. Included in this kit are:

  • Overview whitepaper
  • Source code
  • Installation guide
  • Developer guide

As per the diagram below, this latest OBA kit provides guidance on how developers can integrate Outlook and MOSS 2007 with Siebel and SAP. It integrates a variety of key MS technologies (e.g. BizTalk LOB adapters, WCF, VSTO, and so on) to help you understand how you can integrate LOB systems such as Siebel and SAP into your Office and SharePoint solutions.

image

The actual developer kit can be found here: http://code.msdn.microsoft.com/sapsiebel. And for more information on other OBA developer kits, you can go here: http://msdn.microsoft.com/en-us/office/cc442491.aspx.

Enjoy!

Steve

Hey gang,

Just a quick announcement, if you attended the Office Developer Conference (ODC) last year you may be wondering what happened to it this year? Well, it's going to be subsumed within the SharePoint Conference (SPC), which is happening October 19th-22nd. This is actually going to be great because that means you'll be able to get all of the latest SharePoint and Office development sessions in one conference--what we call the Twofer deal! More info can be found here: http://blogs.msdn.com/sharepoint/archive/2009/06/01/office-developer-conference-moving-to-sharepoint-conference-2009.aspx.

Cheers,

Steve

I recently downloaded the Visual Studio 2010 Beta 1 and have been playing around with the different features for Office Business Application development. For the public Beta 1, you’ll be limited to the 2007-centric templates (e.g. Excel 2007 Add-in, Word 2007 Document, etc.), but the nice thing is that if you’re familiar with these templates when future Betas are released you’ll already have had a chance to get experience with the Beta 1. That said, I presented at a customer conference this week and one of the demos I showed was an Excel Add-in that used a sales cube. This was a 2007-centric HOL that we built for developers, which I’ve posted here: http://cid-40a717fc7fcd7e40.skydrive.live.com/browse.aspx/VSTO%7C_HOLs.

To test out the new bits, I upgraded one of the samples from the HOL. One of the requests that I had was to post the updated code (the Beta 1) for folks. So here it is for you: http://cid-40a717fc7fcd7e40.skydrive.live.com/browse.aspx/Beta1%7C_OBA.

The code is zipped and some details on the code are as follows:

  • Unzip into a folder on your c: drive (e.g. c:\OBA).
  • Ensure that you have added the resources in the Settings tab of your Properties – see figure below. To see the properties, click Project, Properties and then click the Settings tab. Make sure you also add the other two settings as per the below.
  • Also, ensure that the resource files (that are in the Settings tab) are also copied from the Resources folder into the folder you add as a string setting.

image

Once you’ve configured the above four settings, you should be able to F5 and run the application. When you invoke the application, you can filter on the cube (from the custom task pane) and then click the Generate Forecast button to load the data in the Excel template. The figure below shows the finished product.

image

In the coming weeks, I’ll post some additional samples for Office with the Beta 1 bits.

Happy coding!

Steve

Last week, the Visual Studio (VS) 2010 Beta 1 was released to the public. I’m personally very excited about the VS 2010 release because of the additional support for Office and SharePoint development. While you won’t see the SharePoint 2010 project templates in the Beta 1 release, you’ll find the legacy templates (remember VS Tools for Office 3.0?) that have persisted forward to this release which are present in the Beta 1 drop.

Note: If you have not downloaded the VS 2010 Beta 1 yet, you can get it here: http://www.microsoft.com/downloads/details.aspx?FamilyID=75cbcbcd-b0e8-40ea-adae-85714e8984e3&displaylang=en.

To install VS 2010 B1, I simply ran the install process (for the Professional SKU) and it installed fine (as an FYI, my laptop is a T61P with 4G RAM and Win 2008 Server, and is 32x). Goodness so far.

After you install VS 2010 B1, you can open it, choose File, New Project and then click on the Office tab to see what ships in the Beta 1 version of VS 2010. For the most part, you’ll see all of the 2007-centric templates that you had in VS 2008 for Office 2007 save for the SharePoint workflow templates.

image

I thought I would give the tools a test-run and create a Word 2007 document-level solution. To do this, I provided a name for my solution, browsed to a folder, and then clicked OK.

image

The experience was similar to the VS 2008 experience. That is, VS prompted me to either create a new document (you have a sub-set of the Word functionality available to you in the VS IDE so you can create and edit documents (or templates) directly within the IDE) or you can leverage an existing document. I chose the former and clicked OK.

image

The one thing I really like about VS 2010 is the new WPF-rendering of the IDE UI. It’s very slick. You can see below, that after I created the project here is the view of the project. To test out some controls, I dragged and dropped some Word content controls over to the Word document surface (they will be data containers for me later on). I prefer using these content controls because you can either data-bind directly to them or you can populate them from data pulled from web services or databases—which gets at the heart of why we build OBAs in the first place: integrate data directly into the information worker experience. Anyway, you can see here that the content controls have accessible properties that we can manipulate programmatically.

image

Here is a view of the Add Connection dialog—you’ll be familiar with this if you’ve added connections in VS 2008.

image

Adding a connection enabled me to add a Windows Form user control and then bind some data to the control—this is my proxy for what would normally be a web service call to a LOB system such as SAP, Siebel, Dynamics, and so on. You can see my last couple of posts where I’ve posted links to kits that provide you more detail here. (Note that you could also create a WPF user control and then use the Element Host to host the WPF on a WinForm user control. For the sake of expediency, I chose to use the WinForm.)

By default, the adding of a new connection and then dragging and dropping of the data onto a user control does not load the data on initialization, so you must add the following lines of code to the Recruitment_Load event.

private void Recruitment_Load(object sender, EventArgs e)
       {
           RecruitingDataSetTableAdapters.RecruitsTableAdapter adaptr = new TAPAirliftOBA.RecruitingDataSetTableAdapters.RecruitsTableAdapter();
          adaptr.Fill(this.recruitingDataSet.Recruits);

       }

image

The above is what the user control looks like.

You’ll see that I’ve added two events to test out the integration across the custom actions pane and SharePoint. The first will synchronize the text within the above fields with the content controls in the Word doc, and the update SharePoint button will take data from the Word document and update a SharePoint list. This is a nice way to begin to tie together information that you’re storing in an outside data source with document generation and SharePoint site data.

The code for the Sync Word event is simply assigning the Text property of the content controls to the appropriate fields that you’ve got displayed in the custom actions pane.

private void btnSyncWord_Click(object sender, EventArgs e)
{
    Globals.ThisDocument.wccHireeName.Text = recruitName;
    Globals.ThisDocument.wccPositionAppliedFor1.Text = positionAppliedFor;

    …

}

The code for the Update SharePoint event is listed below. The code uses the native Lists web service that ships with SharePoint 2007. A tip here is ensure you keep the Column names simple so you can ensure your CAML construct will adequately update the SharePoint list.

private void btnSyncSharePoint_Click(object sender, EventArgs e)
{
     startDate = Globals.ThisDocument.wccStartDate.Text;

     TAPAirliftOBA.MySPListService.Lists listService = new TAPAirliftOBA.MySPListService.Lists();
     listService.Credentials = System.Net.CredentialCache.DefaultCredentials;
     listService.Url = "
http://mossvm/_vti_bin/Lists.asmx";

     System.Xml.XmlNode xmlListView = listService.GetListAndView("Hires", "");
     XmlDocument xmlDoc = new XmlDocument();
     XmlElement xmlElement = xmlDoc.CreateElement("Batch");
     xmlElement.InnerXml = "<Method ID='1' Cmd='New'><Field Name='Title'>" + recruitID + "</Field><Field Name='RecruitName'>" + recruitName + "</Field><Field Name='Position'>" + positionAppliedFor + "</Field>" + "<Field Name='StartDate'>" + startDate + "</Field>" + "</Method>";
     XmlNode xmlReturn = listService.UpdateListItems("Hires", xmlElement);
     System.Windows.MessageBox.Show("Information Added!");
}  

This will update a list in SharePoint as per the below.

image

This was a fairly straightforward example that illustrated how you could use the legacy support in Visual Studio 2010 Beta 1 to begin coding against the Office object model and integrate your Office customization with SharePoint. As I explore the Visual Studio Beta 1 tools a little more, I’ll post my findings with some additional code samples.

Happy coding!

Steve

This past week I attended and worked at TechEd 2009 in Los Angeles. You can check out all of the conference details here: http://www.msteched.com/teched/default.aspx.

Overall, there were a lot of great sessions and speakers, and it was great to get to see a lot of the people with whom I talk over email but so rarely get to see in person. There are some great talks on the MS TechEd Online site, which is here: http://www.msteched.com/online/home.aspx. (Martin Harwar and I recorded a short session on Silverlight and SharePoint.)

One of the sessions I did this week was on SharePoint and Web Services, and as promised I’ve posted the deck and the code samples from the session to my Skydrive. (You’ll note that I’ve also got a bunch of collateral and resources posted to my Skydrive from past conferences and lots of resources for SharePoint developers.)

http://cid-40a717fc7fcd7e40.skydrive.live.com/browse.aspx/TechEd%7C_USA%7C_2009

On the above link, you’ll find code samples that illustrate the following:

  • SharePoint object model integration (creates a site in an existing site collection)
  • Consumption of native SharePoint web service (updates a list)
  • App that shows different code syntax across SP OM integration, native web services , WCF service, and ASMX service.
  • Integration between Silverlight and SharePoint (that uses a service to call into SharePoint to update a list).

Most of the above are client apps, and use the following code snippets which you can use to create your services. The service-code requires you create a list called TR8. The service is then deployed to IIS 7.0.

ASMX Web Service Code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using Microsoft.SharePoint;

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class UpdateSharePointList : System.Web.Services.WebService
{
    public UpdateSharePointList () {

    }

    [WebMethod]
    public void useNormalMOSSApi(string SalesSPSite, string productName, string productNumber, string FY08Sales)
    {
        string strDashListRoot = SalesSPSite;

        using (SPSite site = new SPSite(strDashListRoot))
        {
            using (SPWeb web = site.OpenWeb())
            {
                web.AllowUnsafeUpdates = true;

                SPList list = web.Lists["TR8"];
                SPListItem Item = list.Items.Add();
                Item["Title"] = productName;
                Item["ProductNum"] = productNumber;
                Item["Sales"] = FY08Sales;
                Item.Update();
            }
        }

    }
}

WCF Web Service Code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using Microsoft.SharePoint;


{
    public void useNormalMOSSApi(string SalesSPSite, string productName, string productNumber, string FY08Sales)
    {
        string strDashListRoot = SalesSPSite;

        using (SPSite site = new SPSite(strDashListRoot))
        {
            using (SPWeb web = site.OpenWeb())
            {
                web.AllowUnsafeUpdates = true;

                SPList list = web.Lists["TR8"];
                SPListItem Item = list.Items.Add();
                Item["Title"] = productName;
                Item["ProductNum"] = productNumber;
                Item["Sales"] = FY08Sales;
                Item.Update();
            }
        }

    }
}

image

So I’ve been here in sunny Orlando for the past few days attending the Collaborate 09 conference, a conference that focuses predominantly on Oracle technology (http://collaborate09.com/).

I presented a couple of times this week. The first session was on Monday and focused on an OBA that a company named Aellius have created—I co-presented in this one. It’s a great OBA that integrates JD Edwards with Office. You can check them out here: http://www.aellius.com. The second presentation was one that I did this morning and covered how to integrate Office and SharePoint with PeopleSoft. I covered client-side assemblies in Outlook and Word and then discussed how to use the Business Data Catalog (BDC) and then Silverlight as ways of integrating PeopleSoft (and indeed wider LOB systems) into SharePoint.  I showed a few demos and have posted some materials for your viewing pleasure in my Skydrive folder. Included in the folder, you’ll find:

1. The deck from the 05/06/09 session;

2. Some simple source code from the Office Word Doc and Silverlight and SharePoint demos along with the Recruiting database and BDC application definition file (ADF);

3. A couple of (un-narrated) videos that show you how to 1) create a web service in PeopleSoft and 2) how to create an ADF in the BDC Definition Editor (free tool that ships with the SP 2007 SDK).

A couple of things to note. You’ll need to have SQL Server (even Express) to use the db, and then you’ll need to add it to your VS project. You’ll also have to build and upload the Silverlight XAP file to your SharePoint site to get that working (see my earlier post on how to do this: Silverlight, SharePoint, Services…Oh Yeah!!!.

You can download the code for the end-to-end demo I showed (the Outlook and SharePoint integration around the recruiting scenario) here: http://code.msdn.microsoft.com/obapsftsak 

I will try and re-record the vids when I get back to the office with some narration, but at least you have a visual walkthrough to get you started.

For those of you that are going to TechEd in Los Angeles next week, I’ll see you there!

Steve

Some great news: SharePoint 2007 Service Pack 2 shipped today. Note that SP 2 should be considered part of your process for readying yourself for SharePoint Server 2010 because there is a new ‘preupgradecheck’ operation, which has been added to stsadmn.

More information on the preupgradecheck operation and the other enhancements and download links can be found off of the SharePoint blog:

http://blogs.msdn.com/sharepoint/archive/2009/04/28/announcing-service-pack-2-for-office-sharepoint-server-2007-and-windows-sharepoint-services-3-0.aspx

Cheers,

Steve

I’ve written and talked about this convergence of technology quite a bit, and I’m pumped to see a lot of companies are starting to pick this up and run with it in interesting ways. A buddy of mine, Martin Harwar, has integrated Silverlight and SharePoint into a couple of cool products so to provide some visibility into the product (and also what is possible to do with this integrated set of technologies), I’ve pasted in a few screenshots of the learning tool in action as well as some online demos you can check out. Martin’s created a couple of products, but a quick explanation of a couple of them:

1. LearningPoint for SharePoint is an LMS (similar concept to the SLK, but built in Silverlight and with the ability for orgs to create their own e-learning in a really efficient, quick way).

LearningPoint: http://www.point8020.com/learningpoint/default.aspx

2. MediaPoint for SharePoint is a simple video/graphic player built in Silverlight that consumes images and videos from a SharePoint library. [This product is free.]

MediaPoint: http://www.point8020.com/mediapoint/demos.aspx

For the LearningPoint product, a wizard takes you through the process of creating a new learning object.

image

image

image

image

After you create the learning object, you can then add videos, text files, etc.

image

You can then create a structure for the learning.

image 

Once you’ve created your learning with LearningPoint, you can play training videos, read documents, and so on, which are tied into the learning structure.

It’s great to see this type of integration in the market…you’re definitely going to see more of this.

Steve

One of the cool things about the Media Streaming Services is that you can circumvent some of the server set-up for SharePoint and still integrate a Silverlight (SL) app into your SharePoint (SP) site. By using the Media Streaming Services, which are free by the way, you can integrate a cool little SL app into your SP site and then simply use the <iframe> code that is auto-generated to integrate the SL app into a Content Editor Web Part.

To complete this integration requires four main steps:

  1. Create the SL app;
  2. Add the SL app to your Media Streaming Services site;
  3. Configure the app and grab the <iframe> code; and
  4. Use the Content Editor Web Part to integrate the SL app with SP.

Let’s do a quick walk through to help get you started.

The Silverlight App

We’ll first assume that you’ve got the SL app created. I created a simple app that uses the Community toolkit to render a simple chart. You can get more information on this here: http://www.codeplex.com/Silverlight. I ensured the correct DLLs were registered in my GAC and then added the appropriate references in my SL app to create a somewhat interesting looking chart. The project contains an object called SharePointData.cs (an oddly named class that represents the items that will be displayed in my chart, which in this app is a set of courses in my bucket of course inventory). Note in the figure below the DataVisualization and Theming DLLs that have been added as references in the project. (I tweaked Tim’s example from his blog-post here.)

image

The custom class code is as follows:

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.ComponentModel;

namespace BarChart
{
    public class SharePointData
    {

        public class CourseData
        {

        public string TrnCourse { get; set; }
        public int NumCourseSales { get; set; }

        }

        public static List<CourseData> loadChartData()
        {
            List<CourseData> courseList = new List<CourseData>();

            courseList.Add(new CourseData() { NumCourseSales = 17, TrnCourse = "VSTS-399" });
            courseList.Add(new CourseData() { NumCourseSales = 39, TrnCourse = "OFC2-214" });
            courseList.Add(new CourseData() { NumCourseSales = 67, TrnCourse = "SQL1-151" });
            courseList.Add(new CourseData() { NumCourseSales = 13, TrnCourse = "ARCH-402" });
            courseList.Add(new CourseData() { NumCourseSales = 53, TrnCourse = "DEV1-164" });
            courseList.Add(new CourseData() { NumCourseSales = 42, TrnCourse = "VSP1-010" });
            courseList.Add(new CourseData() { NumCourseSales = 15, TrnCourse = "SPDEV-302" });
            courseList.Add(new CourseData() { NumCourseSales = 31, TrnCourse = "SPARC-201" });
            courseList.Add(new CourseData() { NumCourseSales = 29, TrnCourse = "SPD1-159" });
            courseList.Add(new CourseData() { NumCourseSales = 44, TrnCourse = "SPD2-203" });
            courseList.Add(new CourseData() { NumCourseSales = 44, TrnCourse = "AZUR-227" });
            courseList.Add(new CourseData() { NumCourseSales = 42, TrnCourse = "OFC3-365" });
            courseList.Add(new CourseData() { NumCourseSales = 32, TrnCourse = "VBA1-102" });
            courseList.Add(new CourseData() { NumCourseSales = 13, TrnCourse = "WEBD-838" });

            return courseList;

        }

    }
}

The XAML code that renders the chart is as follows:

<UserControl xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"  x:Class="BarChart.Page"
    xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:chart="clr-namespace:Microsoft.Windows.Controls.DataVisualization.Charting;assembly=Microsoft.Windows.Controls.DataVisualization"       
    Width="800" Height="450">
    <Grid x:Name="LayoutRoot" Background="White">
        <StackPanel Orientation="Vertical" Margin="15">
            <chart:Chart Height="200"
                         LegendTitle="Item"
                         Title="Course Inventory"
                         x:Name="MyBarChart">
                <chart:Chart.Series>
                    <chart:ColumnSeries x:Name="MySalesChartData"
                                        Title="Courses" 
                                        ItemsSource="{Binding}"                         
                                        IndependentValueBinding="{Binding TrnCourse}"                        
                                        DependentValueBinding="{Binding NumCourseSales}"/>
                </chart:Chart.Series>
            </chart:Chart>
        </StackPanel>
    </Grid>
</UserControl>

And the code-behind for the SL app is as follows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Ink;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Windows.Controls.DataVisualization.Charting;
using Microsoft.Windows.Controls.DataVisualization;
using Microsoft.Windows.Controls.Theming;
using System.Collections.ObjectModel;

namespace BarChart
{
    public partial class Page : UserControl
    {

        List<SharePointData.CourseData> cr = new List<SharePointData.CourseData>();
        public Page()     
        {  
            InitializeComponent(); 

            cr = SharePointData.loadChartData();
            ColumnSeries column = MyBarChart.Series[0] as ColumnSeries;
            column.ItemsSource = cr;
        } 
    }
}

Note that you could likely remove a number of the Using statements to optimize your code.

With these three elements working together, you can run the application and you will get the following results:

image

The thing you want to do now is to get this to the Media Streaming Services.

Uploading the XAP File into the Media Streaming Services

After you build your SL app, you’ll want to upload the XAP file into the Media Streaming Services.

To do this, go to http://silverlight.live.com/ and sign in with your Live account. Then click on Manage Applications and click Upload Application. Provide a name for the file you’re going to upload, click Create and then browse to your XAP file (i.e. your SL app) and then upload it to the Streaming Media Services.

Configuring the SL App

You’ll then need to click the Create link to create a manifest for the SL app. For my example, I only filled out a couple of the fields (see below), but you could complete others and even configure things like background images if you wanted.

image

Click Update when you’re done. You’ll then be presented with a number of different options for embedding the SL app into your web page. I typically use the first one, but you can use any one of these that would match your needs. So, copy the code and we’ll move onto the final step in the process.

image

 

 

 

Use the Content Editor Web Part to integrate the SL app with SP

Go to your SP site and click Site Actions and Edit Page. Click Add a web part and then select Content Editor Web Part from the gallery.

image

Once the web part’s been added, click the Open in tool pane link to configure the web part. Click the Source Editor and then paste the <iframe> code into the editor, which should look something like the following (depending on how you’ve configured the SL app).

image

Click Save, and voila your new whizbang SL app should render via the Streaming Media Services site. You can use the source editor to adjust the height and width if need be, and you can set the Chrome Type to none if you want. I personally like the cleaner look with the standard Content Editor Web Part title on the web part chrome. The end result should be the following:

image

 

 

Pretty cool! I will also mention that this not only works for your SharePoint site on premises (i.e. the SP instance your company hosts), but this also works for SP Online as well—so it’s a very easy way to code custom apps for SP Online. Further, this alleviates the need to do a lot of the web.config changes (so you don’t need direct admin access to the SP server), which is always helpful when you’re trying to develop and deploy cool and funky little apps for your shazammy SP site.

Happy coding all, and I hope this post was useful.

Steve

Hey gang. A few of you that attended DevConnections asked me to post my decks for you, so I’ve posted them to my SkyDrive site in an open folder. You can access the decks here:

http://cid-40a717fc7fcd7e40.skydrive.live.com/browse.aspx/DevConnections%7C_Orlando

I’ll poke through the code I showed to see what might be useful to post here as well when I get landed back in Seattle.

Also, as per my announcements throughout the day check out http://mssharepointdeveloper.com for more SharePoint developer information and training. We’re about to post another 10 modules on SharePoint on the Web—lots of great things ranging from source code, instructor recordings, decks, and hands-on labs.

Happy Coding!

Steve

I’m down in Orlando at DevConnections, 2009, and one of the requests I got in my session this morning was to publish my minimal master page. For reference, the master page is how you brand/control the look and feel of a SharePoint site. Often times, you’ll want to start with a minimal master page and then build out your branding/look and feel from the minimal master page. That said, my example here at the conference was a minimal master page with a Silverlight application embedded within the master page. Here is the code for the master page. You’ll note that the master page references a custom Silverlight app that provides the navigation for the SharePoint site. The reference code for this is outlined in red.

<%-- Identifies this page as a .master page written in Microsoft Visual C# and registers tag prefixes, namespaces, assemblies, and controls. --%>

<%@ Master language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<%@ Import Namespace="Microsoft.SharePoint" %>

<%@ Register Tagprefix="SPSWC" Namespace="Microsoft.SharePoint.Portal.WebControls" Assembly="Microsoft.SharePoint.Portal, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<%@ Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<%@ Register Tagprefix="PublishingWebControls" Namespace="Microsoft.SharePoint.Publishing.WebControls" Assembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<%@ Register Tagprefix="PublishingNavigation" Namespace="Microsoft.SharePoint.Publishing.Navigation" Assembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<%@ Register TagPrefix="wssuc" TagName="Welcome" src="~/_controltemplates/Welcome.ascx" %>

<%@ Register TagPrefix="wssuc" TagName="DesignModeConsole" src="~/_controltemplates/DesignModeConsole.ascx" %>

<%@ Register TagPrefix="PublishingVariations" TagName="VariationsLabelMenu" src="~/_controltemplates/VariationsLabelMenu.ascx" %>

<%@ Register Tagprefix="PublishingConsole" TagName="Console" src="~/_controltemplates/PublishingConsole.ascx" %>

<%@ Register TagPrefix="PublishingSiteAction" TagName="SiteActionMenu" src="~/_controltemplates/PublishingActionMenu.ascx" %>

<%@ Register Assembly="System.Web.Silverlight" Namespace="System.Web.UI.SilverlightControls" TagPrefix="asp" %>

<%-- Uses the Microsoft Office namespace and schema. --%>

<html>

<WebPartPages:SPWebPartManager runat="server"/>

<SharePoint:RobotsMetaTag runat="server"/>

<%-- The head section includes a content placeholder for the page title and links to CSS and ECMAScript (JScript, JavaScript) files that run on the server. --%>

<head runat="server">

<asp:ContentPlaceHolder runat="server" id="head">

<title><asp:ContentPlaceHolder id="PlaceHolderPageTitle" runat="server" /> </title>

</asp:ContentPlaceHolder>

<Sharepoint:CssLink runat="server"/>

<asp:ContentPlaceHolder id="PlaceHolderAdditionalPageHead" runat="server" />

<script type="text/javascript">

function onSilverlightError(sender, args) {

var appSource = "";

if (sender != null && sender != 0) {

appSource = sender.getHost().Source;

}

var errorType = args.ErrorType;

var iErrorCode = args.ErrorCode;

var errMsg = "Unhandled Error in Silverlight 2 Application " + appSource + "\n" ;

errMsg += "Code: "+ iErrorCode + " \n";

errMsg += "Category: " + errorType + " \n";

errMsg += "Message: " + args.ErrorMessage + " \n";

if (errorType == "ParserError")

{

errMsg += "File: " + args.xamlFile + " \n";

errMsg += "Line: " + args.lineNumber + " \n";

errMsg += "Position: " + args.charPosition + " \n";

}

else if (errorType == "RuntimeError")

{

if (args.lineNumber != 0)

{

errMsg += "Line: " + args.lineNumber + " \n";

errMsg += "Position: " + args.charPosition + " \n";

}

errMsg += "MethodName: " + args.methodName + " \n";

}

throw new Error(errMsg);

}

</script>

<meta name="Microsoft Theme" content="CONTOSO 1011, default">

</head>

<%-- When loading the body of the .master page, SharePoint Server 2007 also loads the SpBodyOnLoadWrapper class. This class handles .js calls for the master page. --%>

<body onload="javascript:_spBodyOnLoadWrapper();">

<%-- The SPWebPartManager manages all of the Web part controls, functionality, and events that occur on a Web page. --%>

<form runat="server" onsubmit="return _spFormOnSubmitWrapper();">

<wssuc:Welcome id="explitLogout" runat="server"/>

<PublishingSiteAction:SiteActionMenu runat="server"/>

<div id="silverlightControlHost">

<object data="data:application/x-silverlight," type="application/x-silverlight-2" width="800" height="200">

<param name="source" value="http://stefoxdemo/XAPS1/CoolMenuContainer.xap"/>

<param name="onerror" value="onSilverlightError" />

<param name="background" value="white" />

</object>

<!--

<iframe style='visibility:hidden;height:0;width:0;border:0px'></iframe>

-->

</div>

<PublishingWebControls:AuthoringContainer id="authoringcontrols" runat="server">

<PublishingConsole:Console runat="server" />

</PublishingWebControls:AuthoringContainer>

<%-- The PlaceHolderMain content placeholder defines where to place the page content for all the content from the page layout. The page layout can overwrite any content placeholder from the master page. Example: The PlaceHolderLeftNavBar can overwrite the left navigation bar. --%>

<asp:ContentPlaceHolder id="PlaceHolderMain" runat="server" />

<asp:Panel visible="false" runat="server">

<%-- These ContentPlaceHolders ensure all default SharePoint Server pages render with this master page. If the system master page is set to any default master page, the only content placeholders required are those that are overridden by your page layouts. --%><asp:ContentPlaceHolder id="PlaceHolderSearchArea" runat="server"/><asp:ContentPlaceHolder id="PlaceHolderTitleBreadcrumb" runat="server"/><asp:ContentPlaceHolder id="PlaceHolderPageTitleInTitleArea" runat="server"/><asp:ContentPlaceHolder id="PlaceHolderLeftNavBar" runat="server"/><asp:ContentPlaceHolder ID="PlaceHolderPageImage" runat="server"/><asp:ContentPlaceHolder ID="PlaceHolderBodyLeftBorder" runat="server"/><asp:ContentPlaceHolder ID="PlaceHolderNavSpacer" runat="server"/><asp:ContentPlaceHolder ID="PlaceHolderTitleLeftBorder" runat="server"/><asp:ContentPlaceHolder ID="PlaceHolderTitleAreaSeparator" runat="server"/><asp:ContentPlaceHolder ID="PlaceHolderMiniConsole" runat="server"/><asp:ContentPlaceHolder id="PlaceHolderCalendarNavigator" runat ="server" /><asp:ContentPlaceHolder id="PlaceHolderLeftActions" runat ="server"/><asp:ContentPlaceHolder id="PlaceHolderPageDescription" runat ="server"/><asp:ContentPlaceHolder id="PlaceHolderBodyAreaClass" runat ="server"/><asp:ContentPlaceHolder id="PlaceHolderTitleAreaClass" runat ="server"/><asp:ContentPlaceHolder id="PlaceHolderBodyRightMargin" runat="server" /></asp:Panel>

</form>

</body>

</html>

The way in which you deploy this once you’ve added and amended the master page is as follows:

  • Right-click the master page in SharePoint Designer and select Check In.
  • Select Publish a major version and click OK. 

image

  • Click Yes to approve the master page. SharePoint will automatically redirect to the Approvals page.
  • Right-click the drop-down arrow beside the master page you just added to SharePoint.

image

  • Select Approve/reject.
  • Click the Approved radio button, and then click OK.

image

Your new minimal master page is now ready for use. To associate it with a site, navigate to the site and click Site Actions, Site Settings, and then Modify All Site Settings. Click Master Page under Look and Feel, and then select your minimal master page.

image

And voila, your new minimal master page will render as you’ve designed it.

Steve

My colleague, John Durant, wrote an interesting little blog on how to integrate the Office client and Twitter…some cool stuff. Kind of brings a new spin to OBAs!

http://blogs.msdn.com/johnrdurant/archive/2009/03/16/microsoft-excel-and-twitter-via-smart-tags-in-a-vs-2008-solution.aspx

If you’re trying to install VSeWSS 1.3, the latest version of the Visual Studio Extensions for Windows SharePoint Services, you may find that you need to tweak a couple of things post-install to make sure the VSeWSS 1.3 web service is configured correctly.

To give you a little context, when I first installed the default installation and then created a web part project to test deployment I was getting the following error. This was resulting from the VSeWSS 1.3 WCF web service not having the appropriate permissions.

clip_image002

Note: To install VSeWSS 1.3, Kirk Evans gives a good overview here:

http://blogs.msdn.com/kaevans/archive/2009/03/17/installing-vsewss-1-3.aspx

For post-install config—i.e. to rid yourself of this error message, you might find you need to follow these steps:

1. Open IIS and verify that you have a VSeWSS web service app.

image

2. Click Advanced Settings on the  VSeWSS web app to ensure it is using the SharePoint Central Administration v. 3 application pool.

image

3. Go to the SharePoint Central Administration v.3 application pool and make sure that the Identity is running as NetworkService.

image

4. Go to Computer Management and click Groups, Administrators and then Add. You’ll want to, if it’s not already, add the NetworkService to the Administrators group. (Click Advanced, Find Now, and then select Network Service from the results.) Click OK to get out of the Administrators Properties dialog.

image

5. Reset IIS.

Once this was complete, I was able to go back and create a web part and successfully deploy into my local SharePoint site.

Thanks to Paul Andrew for walking me through this.

Steve

More Posts Next page »
 
Page view tracker