Wednesday, October 24, 2012

Adding a custom CSS file to your Visual Web Part

If you would like to add a custom CSS file to a Visual Web Part that allows the styling of the web part to remain consistent no matter where it's installed, you can do so via the following steps:

  1. Using Visual Studio 2010, open your Visual Web Part solution
  2. In Solution Explorer, right-click on the project and select Add -> SharePoint "Layouts" Mapped Folder
  3. Right-click on the newly created Layouts folder and select Add -> New Folder
  4. Edit the name from "NewFolder1" to "1033"
  5. Right-click on the "1033" folder and select Add -> New Folder
  6. Edit the name from "NewFolder1" to "Styles"
  7. Right-click on the "Styles" folder and select Add -> New Folder
  8. Edit the name from "NewFolder1" to the name of your Project (i.e. MyProject in this example)
  9. Right-click on the "MyProject" folder and select Add -> New Item...
  10. Under the Installed Templates section, expand Visual C# and click on Web
  11. Click on the Style Sheet icon, update the name of your style sheet (i.e. Standard.css in this example), and click Add
  12. You may now enter the appropriate style sheet rules in this file and save the changes
During the deployment of your Visual Web Part, the custom style sheet will be deployed directly to the SharePoint farm under the following location on the web front end servers:  C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\1033\STYLES\MyProject.

Now, to reference the style sheet in your Visual Web Part, you may do so via the following steps:
  1. Open your Visual Web Parts .ascx file
  2. After the "<%@ Control" tag, add the following line:  <SharePoint:CssLink ID="cssLink1" runat="server" DefaultUrl="MyProject/Standard.css" />
  3. Save the change
At this point, you will be able to reference the new style sheet within the controls on your .ascx page.

Wednesday, October 17, 2012

Programmatically locating and cancelling running workflows associated with a content type

If you're looking for a way to programmatically cancel all running workflows associated with a specific content type (i.e. a custom content type called "Enterprise Document" is highlighted in this example) across all of the document libraries contained within a given site collection, I've put together some code that can do this as follows:

Guid siteId = new Guid("siteIdGuid");
using (SPSite site = new SPSite(siteId))
{
    foreach (SPWeb web in site.AllWebs)
    {
        // Only iterate through document libraries since this content type is associated with documents
        foreach (SPList list in web.GetListsOfType(SPBaseType.DocumentLibrary))
        {
            // Make certain the content type exists in the library so you don't waste cycles stepping through a document library that doesn't contain it
            SPContentType ctEntDoc = list.ContentTypes["Enterprise Document"];
            if (ctEntDoc != null)
            {
                foreach (SPListItem item in list.Items)
                {
                    // Only cancel active workflows that are associated with items of this content type
                    if (item.ContentType.Name == "Enterprise Document")
                    {
                        SPWorkflowManager mgr = site.WorkflowManager;
                        SPWorkflowCollection activeWFs = mgr.GetItemActiveWorkflows(item);
                        foreach (SPWorkflow activeWF in activeWFs)
                        {
                            SPWorkflowManager.CancelWorkflow(activeWF);
                        }
                    }
                }
            }
        }
    }
}



Tuesday, October 16, 2012

Good technique for programmatically deleting items from SharePoint collections

If you're attempting to delete items from a SharePoint collection (i.e. this could include containers such as lists, groups, document libraries, workflow collections, etc), you'll often encounter a situation where you'll need to iterate through a given collection in order to find the appropriate item (i.e. using a "for" or "foreach" loop) and then delete that specific item.  Now, if you happen delete an item from the collection while you're iterating through it, your code will thrown an error message that will stop the process dead in its tracks due to the fact that you've actually changed the contents of the collection while your also in the process of looping through the contents.  Here is an example scenario in which I'm attempting to remove a given task list called "Expired Items Tasks" from a site:

Incorrect Code:

foreach (SPList expiredItemTaskList in web.GetListsOfType(SPBaseType.GenericList))
{
    if (expiredItemTaskList.Title == "Expired Item Tasks")
    {
        expiredItemsTaskList.Delete();
    }
}


This code will throw an error (i.e. often times "Collection was modified; enumeration operation may not execute") on the next iteration of the loop that takes place after you're deleted the selected item and will stop the process dead in it's tracks.  For your code to properly handle this scenario, a good strategy will be to temporarily store the specific object(s) targeted for deletion in a generic list until you've completed iteration process.  Once you've completed your review of the collection, you can then delete those objects at your liesure:

Correct Code:

List<SPList> listsToDelete = new List<SPList>();
foreach (SPList expiredItemTaskList in web.GetListsOfType(SPBaseType.GenericList))
{
    if (expiredItemTaskList.Title == "Expired Item Tasks")
    {
        listsToDelete.Add(expiredItemTaskList);
    }
}

foreach (SPList expiredItemsTaskList in listsToDelete)
{
    expiredItemsTaskList.Delete();
}

Anyway, I've run into this scenario on many occasions with other .NET code; however, I've found this scenario to be more prevalent in SharePoint due to the fact that most objects contained within it are collections.  Hope this helps!

Thursday, October 11, 2012

Allowing a Windows Forms Application to manage SharePoint 2010

If you'd like to create a Windows Forms Application that a power user (i.e. perhaps a Business Analyst who isn't versed in PowerShell, but has a high level of access to the SharePoint Farm) could use for administering functionality on your SharePoint farm that isn't covered by standard tools, there are a few steps that you'll need to carry out first before your application will run on the farm.  After creating your Windows Forms Application, the first thing you'll need to do is add a reference to the Microsoft.SharePoint.dll file.  This can be accomplished via the following steps:
  1. Using Visual Studio 2010, open your Windows application solution
  2. Expand the tree under your solution in the Solution Explorer view
  3. Under your project, right-click on References and select Add Reference...
  4. There will be two routes you can pursue for adding the assembly based on your development environment:
  • If your developing on an environment with SharePoint installed, you can reference the assembly via the following steps:
    1. Click on the .NET tab
    2. Locate and click on the Microsoft.SharePoint item
    3. Click OK
  • If you don't have SharePoint installed in your environment, you can reference the assembly via the following steps:
    1. Obtain the assembly via the steps that I've covered in my blog post titled Accessing files contained in the Global Assembly Cache
    2. Create a new folder in your project called "External Assemblies"
    3. Add the Microsoft.SharePoint.dll file to the new folder
    4. Under your project, right-click on References and select Add Reference...
    5. Click on the Browse tab
    6. Navigate to the "External Assemblies" folder
    7. Click on the Microsoft.SharePoint.dll file
    8. Click OK
Once you've associated the SharePoint assembly, the next step will be to make certain your Windows application can run in a 64 bit environment if your app is going to be accessing SharePoint 2010.  By the way, if you don't follow these steps, your code will throw an error message that reads "Object reference not set to an instance of an object".  Here are the steps that will allow your app to run in a 64 bit environment: 
  1. In the Solution Explorer view, right-click on the project and select Properties
  2. Under General, locate the Platform target: dropdown and change the value from "x86" to "Any CPU"

  3. Save the change and build your solution
At this point in time, your Windows application will now be able to run directly on one of your SharePoint WFE servers in order to carry out the admin activities that your power user would like it to execute.

Wednesday, October 10, 2012

Easy way to identify the Content Type ID and/or GUID of a content type

If you're looking for an easy way to identify the Content Type ID and/or GUID that is associated with a content type, here are a few quick steps that you can use:

  1. Navigate to the root site where your content types are stored
  2. Click on Site Actions -> Site Settings
  3. Under Galleries, click on Site content types
  4. Locate and click on the link associated with the content type you wish to obtain the GUID for
  5. Right-click on the URL in your browser and select Copy
  6. Open Notepad and paste the link so that you can review it
  7. The Content Type ID associated with your content type is located immediately in between the text that reads "ctype=" and the first ampersand, "&", character.  Here is a quick sample of what this will look like with the appropriate Content Type ID bolded:

    http://webapplicationname/sitename/_layouts/ManageContentType.aspx?ctype=0x01010035C1DFBDC85613499278F0D16DBD4C5D&Source=...
If you're interested in obtaining the GUID from a Content Type ID associated with a Document Library, you simply need to remove the "0x010100" prefix.  Further details on what makes up a Content Type ID can be found at the following location:

http://msdn.microsoft.com/en-us/library/aa543822.aspx

Tuesday, October 2, 2012

Strategies for implementing a 'Wait Notification' for a long running operation: AJAX

If you're looking for a way to inform the users of your ASP .NET web application that processing is actually taking place during a long running process and you happen to be using an AJAX UpdatePanel control, here is a strategy that you can use that will keep your users informed.  In order to make this work, you will need to make certain that the following three controls exist on your ASPX page (NOTE: two of these controls, the asp:ScriptManager and asp:UpdatePanel, will already be present on your page if you're already using an asp:UpdatePanel control on your web page):

<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
<asp:UpdateProgress ID="updateProgress" AssociatedUpdatePanelID="updatePanel1" runat="server" DynamicLayout="true">
    <ProgressTemplate>
        <div align="center" id="div_ProcessingMessage" runat="server"><span>Processing...</span></div>
        </div>
    </ProgressTemplate>
</asp:UpdateProgress>
<asp:UpdatePanel ID="updatePanel1" runat="server">'
...

After these changes are in place, you can fire up your application and the message or image contained in the <div> tag will pop up in the specified location whenever a postback is triggered on the page.  The longer the process takes, the longer this message will stay visible to the users.  Once the postback operation is complete, the message will be hidden again.