Integrating Windows Form Click Once Application into SharePoint 2007–Part 3 of 4

(This is a series of posts covering how to include a WinForm app inside a SharePoint 2007 application.  For further info, please see Posts One, Two, and Four. All of the code can be downloaded in Post Two.)

As I promised several months ago, I’ll cover the Custom Action piece of our solution.  The custom action is used to allow the users to launch our Windows Forms application using Click Once deployment.  Our custom action adds a new link to the Action menu of a document library’s toolbar:


Adding a custom action is relatively simple.  First, you create a Feature in Visual Studio.  The feature.xml for my example was this:

   1:  <?xml version="1.0" encoding="utf-8"?>
   2:  <Feature  Id="f33bf219-4185-4be1-8aa6-4220933320a0"
   3:            Title="Custom Action"
   4:            Description="Adds a custom action for document uploads"
   5:            Version=""
   6:            Hidden="FALSE"
   7:            Scope="Web"
   8:            DefaultResourceFile="core"
   9:            xmlns="">
  10:    <ElementManifests>
  11:      <ElementManifest Location="elements.xml"/>    
  12:    </ElementManifests>
  13:  </Feature>

This is a pretty standard feature.xml file.

Second, you have the elements file (referenced in line #11 above):

   1:  <?xml version="1.0" encoding="utf-8" ?>
   2:  <Elements xmlns="">
   3:    <!-- Document Library Toolbar Upload Menu Dropdown -->
   4:    <CustomAction Id="81F9EBB9-B6D1-497f-9587-95C30B3B00A7"
   5:      GroupId="UploadMenu"
   6:      Location="Microsoft.SharePoint.StandardMenu"
   7:      Sequence="3"
   8:      ControlAssembly="ClickOnceSharePoint, Version=, Culture=neutral, PublicKeyToken=ff21f702ff80f059"
   9:      ControlClass="ClickOnceSharePoint.Controls.UploadCustomAction">
  10:    </CustomAction>
  11:  </Elements>

Let’s look at the elements file above.  Line #5 and Line #6 tell SharePoint where to add our choice to the menu.  These values can be found on this page on MSDN. (The complete schema reference for the CustomAction is here.)

Lines #8 and #9 tell SharePoint that when a user selects our custom action, it should look to the ControlAssembly to find the ControlClass.  (As you can tell by Line 8, our assembly is in the Global Assembly Cache.)

At first, I tried using just a URL here, instead of a custom control.  The <URLAction> tag allows you to define a static url to direct the user to.  However, for my scenario this wouldn’t work because I needed to generate the url on the fly – hence the need for a custom control.

Our custom control is going to generate a dynamic URL with some query string parameters.  This URL is the link to the Windows Form Click Once application and the query string parameters are used to pass information from SharePoint to the app when it starts on the client. 

Here’s the UploadCustomAction class:

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Text;
   4:  using System.Web;
   5:  using System.Web.UI;
   6:  using System.Web.UI.WebControls;
   7:  using Microsoft.SharePoint;
   8:  using Microsoft.SharePoint.WebControls;
   9:  using Microsoft.SharePoint.Administration;
  11:  namespace ClickOnceSharePoint.Controls
  12:  {
  13:      public class UploadCustomAction : System.Web.UI.WebControls.WebControl
  14:      {
  15:          protected override void CreateChildControls()
  16:          {
  17:              SPList myList = null;
  19:              //TODO: Change this list dynamically based on the web part calling it
  20:              //for this example, we're going to just assume it is the Shared Documents library
  21:              myList = SPContext.Current.Web.Lists["Shared Documents"];
  23:              //Do security check
  24:              if (myList.DoesUserHavePermissions(SPBasePermissions.AddListItems))
  25:              {
  26:                  MenuItemTemplate myCustomUploadAction = new MenuItemTemplate();
  27:                  myCustomUploadAction.Text = "Custom Upload";
  28:                  myCustomUploadAction.Description = "Assign permissions and meta data values while uploading multiple documents";
  29:                  myCustomUploadAction.ImageUrl = "/_layouts/Images/CustomUploadIcon.jpg";
  31:                  myCustomUploadAction.ClientOnClickNavigateUrl = ActionUrl("SharedDocuments");
  33:                  this.Controls.Add(myCustomUploadAction);
  34:              }
  35:              else
  36:              {
  37:                  this.Parent.Visible = false;
  38:              }
  41:          }
  43:          private string ActionUrl(string docLibrary)
  44:          {
  45:              //These parameters will be passed to the ClickOnceApp using the query string
  46:              //  
  47:              int _clientDownloadSpeed = 768000; //Highest speed clients are guarranteed to have (in bytes)
  48:              SPSite site = SPContext.Current.Site;
  49:              string server = this.Context.Request.Url.Host;
  50:              string secure = "N";
  51:              string siteName = SPContext.Current.Web.Name;
  53:              string Url = SPContext.Current.Site.Url.ToUpper();
  54:              string maxFileSize= SPContext.Current.Site.WebApplication.MaximumFileSize.ToString();
  56:              if (Url.StartsWith("HTTPS:"))
  57:              {
  58:                  secure = "Y";
  59:              }
  61:              return string.Format("/_layouts/ClickOnceApp/ClickOnceApp.application?Srv={0}&Sec={1}&Doc={2}&SiteName={3}&Speed={4}&Max={5}",
  62:                  server, secure, docLibrary, siteName, _clientDownloadSpeed, maxFileSize);
  63:          }
  65:      }
  66:  }

Let’s walk through this class.

The only required method in this class is the CreateChildControls() method on Line 15. When SharePoint adds our custom action to the menu, it executes this method.  The code checks the document library to verify that the user has permission to upload documents (lines 21-24). 

Please note that the document library is hard coded in line 21.  For production, we have code that determines the document library being used based on the URL of the browser.

The next step is to build the control that will be added to the menu.  The control is of type “Microsoft.SharePoint.WebControls.MenuItemTemplate” (line 26) and allows us to set the text that users see (line 27), the description that users also see (line 28), and the image/icon that users see (line 29).  BTW, this image was included in our solution file.

The last property to set for our MenuItemTemplate control is the “ClientOnClickNavigateUrl”.  This is the URL that SharePoint will send users to (line 31).  Since we’re building our URL dynamically, this line calls our ActionUrl() method.

The ActionUrl() method (line 43) does the final work of putting the URL together by concatenating strings.

Some notes about the parameters:

  • _clientDownloadSpeed This is a value that the WinForm app uses to let uploaders know how long a particular document may take for end users to download.  By letting them know how long it would take, in minutes/seconds, they were better able to judge file size.
  • maxFileSize This value is the maximum file size that the SharePoint Farm is configured to accept.  The value is set in Central Admin.  It allows our app to let the user know that the file is too big before they even try to upload it (and get some strange error message).
  • secure This setting lets the app know whether it needs to upload files to an HTTP or HTTPS address.
  • server, sitename, docLibary These values are used by the WinForm app to build the upload URL.

By using these parameters, we were able to reuse this custom action with different document libraries and different sites within SharePoint.

That’s it for the custom action.  In Part 4, I’ll detail the web service which sets the meta data values of the documents and also how we packaged all of this into one SharePoint Solution file (.wsp).

Print | posted @ Tuesday, August 31, 2010 10:13 PM