DevExpertise

Practical tips and tricks for all things .NET, SharePoint, Silverlight, InfoPath, and general application development.

Archive for March, 2009

ASP.NET Tip/Trick: Use a Base Page Class for All Application Pages

Posted by DevExpert on 25th March 2009

All coders worth their salt know that duplicating code isn’t a best practice, and you should consolidate and leverage object inheritance where necessary.  When done correctly, this promotes better maintainability and a better overall application.

I’ve been doing a lot of straight ASP.NET application programming lately, and no matter how many ASP.NET applications I write, I always use a certain number of common methods in my pages’ code-behind.  For example, I get and set view state values, I retrieve values from the cache, or verify query strings have been included.  Because I’m doing these same types of things in most of my pages, it makes sense to include a lot of the leg work in a base class that each of my pages can inherit.  This blog post will provide you the default base class I always start with, and will explain and demonstrate a few of the methods.

At a high level, my base class is divided into the following four regions:

image

Query String Methods
Let’s take a look at the Query String Methods first.  Many pages will have support for query strings, and most of the time these query string values will need to be validated.  Consider a scenario where you are accessing data from a database, and you navigate to a page with a URL of http://webapp/page.aspx?id=10.  You pull out the ID query string value and pass that in as a parameter and retrieve the appropriate results.  But what if you modify the query string value and access a page with a URL of http://webapp/page.aspx?id=abc?  Are you checking to make sure the value is numeric? Are you even checking to make sure an ID query string has been specified?  Well, you should!  A lot of this logic can be wrapped in a base class, and through the use of inheritance and overridden methods, the specific logic can be written. 

I have three methods declared in my base class:

  • RequiredQueryStrings: Returns a list of all required query string keys.
  • CheckQueryStrings: Ensures all required query strings have been specified.
  • CheckQueryStringValues: Ensures the specified query string values are actually valid.

These methods are declared as follows:

/// <summary>
/// When overriden, returns a list of all required query string keys
/// </summary>
/// <returns></returns>
protected virtual List<string> RequiredQueryStrings() {
    return null;
}

/// <summary>
/// When overriden, checks the actual values of query strings
/// </summary>
/// <returns></returns>
protected virtual bool CheckQueryStringValues() {
    return true;
}

/// <summary>
/// Verifies the required query string are actually present
/// </summary>
/// <returns></returns>
protected bool CheckQueryStrings() {
    List<string> values = RequiredQueryStrings();

    if (values == null || values.Count == 0) {
        return true;
    }
    else {
        foreach (string value in values) {
            if (Request.QueryString[value] == null) {
                return false;
            }
        }
    }

    return true;
}


In addition to these methods, my base page’s Init method checks these methods and depending on the results, allows the rest of the code to run or redirects you to an error page:

protected override void OnInit(EventArgs e) {
    base.OnInit(e);

    if (!CheckQueryStrings()) {
        // all required query strings have not been specified
        RedirectToPage("Error.aspx");
    }

    if (!CheckQueryStringValues()) {
        // one or more query string values are invalid
        RedirectToPage("Error.aspx");
    }
}


To use this, I simply inherit my application pages from my PageBase class, and override the appropriate methods:

public partial class ViewWidget : PageBase {

    protected override List<string> RequiredQueryStrings() {
        List<string> values = new List<string>();
        values.Add("widgetID");

        return values;
    }

    protected override bool CheckQueryStringValues() {
        int widgetID = 0;
        int.TryParse(Request.QueryString["widgetID"].ToString(), out widgetID);

        return (widgetID > 0);
    }

    protected void Page_Load(object sender, EventArgs e) { }
}


Notice all I’m doing is overriding the base class methods, and specifying the things that are required.  It’s up to the base class to do the actual error handling, which in my case involves navigating to an error page.  The important thing to note is that the error handling logic is declared in ONE place.  Each individual page is only responsible for identifying the values that should be checked.

Now, when I access my ViewWidget.aspx page with a valid URL, such as http://webapp/ViewWidget.aspx?widgetID=10, I get the following page:

image


If I access it with an invalid URL, such as http://webapp/ViewWidget.aspx?widgetID=xyz, I get redirected to the error page:

image

 

Redirection Methods

Chances are your web application contains more than just one page, and you will have to navigate to other pages.  Once in awhile ASP.NET will throw a ThreadAbortException when redirecting to another page, which doesn’t matter, and we don’t really need to do anything when that occurs.  My redirection methods consist of two methods:

  • RedirectToPage: Redirects to a page and ignores the exception that is sometimes thrown.
  • RedirectToPageWithQueryStrings: Redirects to a page and includes all current query string keys and values.  Since this method ultimately calls RedirectToPage, any exception is also ignored.

These methods are declared as follows:

/// <summary>
/// Redirects the application to the specified page, and ignores the
/// erroneous error that is sometimes thrown
/// </summary>
/// <param name="url"></param>
protected void RedirectToPage(string url){
    try{
        Response.Redirect(url);
    }
    catch{
        // catch the ThreadAbortException that is occasionally thrown by ASP.NET
    }
}
/// <summary>
/// Redirects to another page and carries over all current
/// query string keys and values
/// </summary>
/// <param name="url"></param>
protected void RedirectToPageWithQueryStrings(string url) {
    string queryStringList = string.Empty;

    if (!string.IsNullOrEmpty(url)) {
        if (Request.QueryString.Count > 0){

            // rebuild the query string list
            for(int i = 0; i < Request.QueryString.Count;i++){
                queryStringList += string.Format("{0}={1}&",
                    Request.QueryString.GetKey(i), Request.QueryString.Get(i));
            }

            // remove the erroneous ampersand
            queryStringList = queryStringList.TrimEnd(new char[] { '&' });

            // append the '?' to the beginning
            if (!string.IsNullOrEmpty(queryStringList)) {
                url = "?" + queryStringList;
            }
        }

        RedirectToPage(url);
    }
}

 

View State Methods

I utilize view state occasionally, and have included a couple methods to help manage this:

  • GetViewStateValue: Retrieves a value from view state, and if it doesn’t exist returns the specified default value.
  • SetViewStateValue: Sets a view state value.
  • ClearViewStateValue: Clears a view state value.

These methods are declared as follows:

/// <summary>
/// Retrieves a value from ViewState
/// </summary>
/// <param name="key"></param>
/// <param name="defaultValue"></param>
/// <returns></returns>
protected object GetViewStateValue(string key, object defaultValue) {
    return ((ViewState[key] == null) ? defaultValue : ViewState[key]);
}

/// <summary>
/// Sets a value in ViewState
/// </summary>
/// <param name="key"></param>
/// <param name="val"></param>
protected void SetViewStateValue(string key, object val) {
    ViewState[key] = val;
}

/// <summary>
/// Clears a value from ViewState
/// </summary>
/// <param name="key"></param>
protected void ClearViewStateValue(string key) {
    ViewState[key] = null;
}

 

Cache Methods

I also leverage the application cache, and have included a few methods to help manage this as well:

  • GetCachedItem: Retrieves a value from the application cache, and if it doesn’t exist returns null.
  • SetCachedItem: Adds an item to the application cache.
  • ClearCachedItem: Removes an item from the application cache.

These methods are declared as follows:

/// <summary>
/// Returns an item from the application cache
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
protected object GetCachedItem(string key) {
    object returnValue = null;

    try {
        returnValue = Cache.Get(key);
    }
    catch { }

    return returnValue;
}

/// <summary>
/// Adds in item to the application cache
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="minutes"></param>
protected void SetCachedItem(string key, object value, int minutes) {
    try {
        Cache.Insert(key, value, null,
            DateTime.Now.AddMinutes(minutes), TimeSpan.Zero);
    }
    catch { }
}

/// <summary>
/// Clears an item from the application cache
/// </summary>
/// <param name="key"></param>
protected void ClearCachedItem(string key) {
    try {
        Cache.Remove(key);
    }
    catch { }
}

 


These are just a few of the methods that are good candidates for placement in a base class.  You could just as easily as methods to manage session variables, or some other type of common functionality in your application.  The point is to identify places where you can simplify and reuse code.  This will make it much easier to develop, maintain, and enhance in the future.

I’ve included my base page in the ZIP file below.  As always, my code is provide as-is and without warranty!

Download: PageBase.zip

Tags: ,
Posted in .NET, ASP.NET | 21 Comments »

Ten Free SharePoint Themes

Posted by DevExpert on 20th March 2009

The Ten Themes for SharePoint in VSeWSS was just released and contains 10 very cool themes, absolutely free and full of glitzy eye candy.  I personally don’t use VSeWSS, but you can just extract the contents of the MSI file and pull out the THEMES and LAYOUTS folders within the Visual Studio solutions, and use that for a custom theme. 

For tips and tricks on how to properly deploy themes to your SharePoint environment, take a look at my previous post here, and you may also be interested in the Vista theme I put together that I describe here.

Here are screenshots of the ten free themes:

Construction:

Construction_Preview

Contoso:

Contoso_Preview

Corporate:

Corporate_Preview

Events:

Events_Preview

OARP:

OARP_Preview

Procurement:

Procurement_Preview

Publishing: 

Publishing_Preview

Sporting:

sporting_Preview

Startup:

Start-Up_Preview

Team:

Team_Preview

Tags: , ,
Posted in SharePoint, SharePoint UI | 1 Comment »

Integrating a Custom ASP.NET Application into SharePoint (Part 3)

Posted by DevExpert on 4th March 2009

In my last two posts here and here I began describing how to integrate a custom ASP.NET application into SharePoint.  The first post focused on the essentials, and detailed how to get your application into the SharePoint LAYOUTS folder structure, specifically where to place your files and how to inherit SharePoint’s look and feel by using its master page.  The second post focused on configuring permissions for your application and also demonstrated a few handy built-in controls that you can leverage to give your application that true SharePoint-like integrated look and feel.

This post will explain how to add custom navigation for your application.  There are a few different approaches that I like to take depending on the application, and I’ll demonstrate a couple of them for you.  These can be used in any combination in order to achieve the navigation you’re aiming for.  In fact, I recommend a combination of these to achieve a fully integrated navigation structure.

Approach #1: Quick Launch Navigation
Now, modifying the Quick Launch navigation menu is a trivial task.  Simply go to Site Actions > Site Settings and find the settings to modify the navigation (Quick Launch on team sites, and Navigation on publishing sites), and add and remove nodes to your heart’s content.  While this manual method works just fine, it’s just that: manual.  I always have the mindset of if I’m already deploying an application somewhere, I might as well automate as much of the setup steps as I can.  Luckily through the use of features we are able to accomplish this easily.

For the sake of brevity, I am going to assume you know what a SharePoint feature is, what a solution package is, and how to create and deploy them.  If not, there are plenty of great resources out there that will help you out.  Anyways, the first step is to create the feature and associated feature receiver that will execute when the feature is activated on the site.  The feature receiver is going to utilized the SharePoint Object Model to create navigation items.

The first step is to create the feature receiver, which must inherit from the SPFeatureReceiver class and must implement the standard 4 feature operations: FeatureActivated, FeatureDeactivated, FeatureInstalled, and FeatureUninstalled.  For this post I’m only adding the items in the FeatureActivated event, but it’s probably a good idea to clean these up in the FeatureDeactivated event in case the feature is ever deactivated.  My feature receiver looks like this:

namespace DevExpertise.LayoutsApp {
    public class FeatureReceiver: SPFeatureReceiver {
        public override void FeatureActivated(SPFeatureReceiverProperties properties) {
            using (SPWeb site = (properties.Feature.Parent as SPWeb)) {
                // create the nodes
                SPNavigationNode widgetManagementNode = new SPNavigationNode("Widget Management",
                    SPUrlUtility.CombineUrl(site.Url, "DevExpertise.LayoutsApp/WidgetMgmt.aspx"), true);
                SPNavigationNode viewWidgetsNode = new SPNavigationNode("View Widgets",
                    SPUrlUtility.CombineUrl(site.Url, "DevExpertise.LayoutsApp/WidgetList.aspx"), true);
                SPNavigationNode addWidgetNode = new SPNavigationNode("Add Widget",
                    SPUrlUtility.CombineUrl(site.Url, "DevExpertise.LayoutsApp/AddWidget.aspx"), true);
                SPNavigationNode widgetSettingsNode = new SPNavigationNode("Modify Widget Settings",
                    SPUrlUtility.CombineUrl(site.Url, "DevExpertise.LayoutsApp/WidgetSettings.aspx"), true);

                // add the Widget management node to the menu (must be done first)
                site.Navigation.QuickLaunch.AddAsLast(widgetManagementNode);

                // add the sub-items to the Widget Management node
                widgetManagementNode.Children.AddAsLast(viewWidgetsNode);
                widgetManagementNode.Children.AddAsLast(addWidgetNode);
                widgetManagementNode.Children.AddAsLast(widgetSettingsNode);

                // update the site
                site.Update();
            }
        }

        public override void FeatureDeactivating(SPFeatureReceiverProperties properties) {
            // do nothing
        }

        public override void FeatureInstalled(SPFeatureReceiverProperties properties) {
            // do nothing
        }

        public override void FeatureUninstalling(SPFeatureReceiverProperties properties) {
            // do nothing
        }
    }
}


As you can see, it’s not complicated at all.  Simply add as many SPNavigationNodes as you like!  The next step is to tell the feature to execute the custom feature receiver.  For that just add the ReceiverAssembly and ReceiverClass elements to your feature definition file:

<?xml version="1.0" encoding="utf-8"?>
<Feature
  Id="5856617E-BED2-4705-B030-735F7483225E"
  Title="DevExpertise Layouts Application"
  Description="Contains the necessary components for the DevExpertise custom LAYOUTS application."
  Version="1.0.0.0"
  Scope="Web"
  Hidden="false"
  ImageUrl="DevExpertise\devexpertiseLogo.png"
  ReceiverAssembly="DevExpertise.LayoutsApp, Version=1.0.0.0, culture=neutral, PublicKeyToken=d39eedb6cff9b1c8"
  ReceiverClass="DevExpertise.LayoutsApp.FeatureReceiver"
  xmlns="http://schemas.microsoft.com/sharepoint/">
</Feature>


Once the feature is properly installed, it will show up under Site Features (note that this is scoped at the site level, and will need to be activated on each site):

image


Cross your fingers, activate it, and you should the new navigation items:

image


Not too shabby for a few lines of code, huh?  You’re just as able to add items to the top navigation if you so desire too. 


Approach #2: Custom Actions

One of my favorite things about SharePoint is the ability to extend just about anything, making it a true application development.  Menu items are no exception, and they’re painfully simple to implement.  You are able to add a custom link to the Site Actions menu, Site Settings menus, list menus, individual item menus, Central Administration menus, etc.  If SharePoint has a menu somewhere, chances are you’re able to add your own item to it.  There are 2 ways to do this and I’ll only be demonstrating it one way.  In a future blog post I’ll show how to do all this stuff programmatically for an even more robust navigation structure.

For this sample Widgets application, I would like to take the navigation a bit further and add an item to the Site Actions menu and also create a group in Site Settings that will allow me to manage my application.  To accomplish this, I added a new element manifest to my feature called CustomActions.xml, which will define our custom actions.  Per MSDN, custom action files are included as part of a feature and deployed as XML element descriptions, and structured with a CustomAction element, which serves as the core definition for a single action of a link or toolbar item. The following is my CustomActions.xml file, which defines a single action for the Site Actions menu:

<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomAction
    Id="ManageWidgetAction"
    GroupId="SiteActions"
    Location="Microsoft.SharePoint.StandardMenu"
    Sequence="1"
    Title="Manage Widgets"
    Description="Manage your company's widget catalog."
    ImageUrl ="/_layouts/images/actionssettings.gif"
    Rights="ManageWeb,ManageLists">
    <UrlAction Url="~site/_layouts/DevExpertise.LayoutsApp/ManageWidgets.aspx" />
  </CustomAction>
</Elements>


Let’s break this down a little bit:

  • Id.  The ID of your custom action.
  • GroupId. A pre-defined or custom that determines what group it will be placed into.  A comprehensive list of built-in values can be found here.
  • Location.  The location at which your custom action will be applied.  A comprehensive list of built-in values can be found here.
  • Sequence.  The order in which your custom action will appear in relation to other custom actions.
  • Title.  The display title of the action.
  • Description.  The description of the action, if applicable.
  • ImageUrl. The image that is displayed next to the action, if applicable.
  • Rights.  A comma delimited list of SPBasePermission enumeration items.  A comprehensive list of values can be found here.
  • RequireSiteAdministrator.  A true/false value indicating if this option is only visible to the site collection administrator.
  • UrlAction: This element defines where the link will take you.  Be sure to specify either the ~site or ~sitecollection token which tells SharePoint to build the URL relative to either the site or site collection, respectively.

Simply modify the feature to include this element manifest, redeploy it, reactivate it, and you should see the following in your Site Actions menu:

image


Easy, huh?  Let’s take this a step further and add a few items to the Site Setting page.  You can either add items to existing groups on that page, or create your own.  I created my own by specifying CustomActionGroup in my CustomActions.xml file, as well as a few CustomAction elements that will be a part of my custom group.  The XML is fairly straightforward:

<CustomActionGroup
  Id="LayoutsAppCustomActionGroup"
  Title="Widget Application Settings"
  Sequence="1"
  Location="Microsoft.SharePoint.SiteSettings">
</CustomActionGroup>

<CustomAction
  Id="UserGroupAdminLinkForSettings"
  GroupId="LayoutsAppCustomActionGroup"
  Location="Microsoft.SharePoint.SiteSettings"
  Rights="ManageWeb,ManageLists"
  Sequence="1"
  Title="Manage Widget Categories">
  <UrlAction Url="~site/_layouts/DevExpertise.LayoutsApp/Categories.aspx" />
</CustomAction>

<CustomAction
  Id="UserGroupAdminLinkForSettings"
  GroupId="LayoutsAppCustomActionGroup"
  Location="Microsoft.SharePoint.SiteSettings"
  RequireSiteAdministrator="TRUE"
  Sequence="2"
  Title="Modify Widget Permissions">
  <UrlAction Url="~site/_layouts/DevExpertise.LayoutsApp/Permissions.aspx" />
</CustomAction>


The only notable thing to point out here is for the CustomAction elements, the GroupId is that of the CustomActionGroup I specified first.  This tells SharePoint to put these actions in the custom group.  Redeploy and reactivate your feature, and you will now have this in your Site Settings page:

image


Hopefully you can see from this post that it’s pretty easy to build navigation for your custom application and have that created when your application is deployed via a custom feature.  In the next and final post in this series, I will show my approach to packaging everything up into features and solution packages, and how to deploy that to SharePoint in a simple and streamlined fashion.  Stay tuned!

Tags: , , , , ,
Posted in .NET, Object Model, SharePoint, SharePoint UI, XML | 10 Comments »