Saturday, September 29, 2012

Common Base Class Work Around for Page and UserControl

The Gotcha
This is a really sore subject for me because I have tried finding a unifying factor for both sets of objects, but the answer I have come up with is - there is no unifying factor. I have read posts of people coming up with ways to have a single base class for both pages and controls - but it was a very over complicated approach and they didn't post how to do it anyhow.

http://stackoverflow.com/questions/7840706/how-to-make-a-base-class-for-both-page-and-usercontrol

I followed the advice in the above link and I have decided it is the best approach (at least for me) to follow.

The Approach
When creating a new web app from scratch, I highly recommend almost all of your page and control code behinds inheriting from a base class. The problem obviously is when you need to have code available to both base classes to do something rudimentary and basic. I recommend having the following components:
  1. Page Base Class
  2. Control Base Class
  3. Static Class that has extension and static methods in it that both Base Classes will be using. This way you don't duplicate any code.
  4. Interface for the above classes. It is not fun to maintain, but it guarantees that the implementations and signatures being used are identical for both classes.
The following is an example of the implementation. These are methods I use because they make my life easier for doing redirects and constructing URIs in the code behind. Feel free to use them.

Pages
//PageBase - The base class for all aspx pages
//ICommonPageMethods - The interface that must be implemented 
//                     in order to use the common methods safely
public class PageBase : System.Web.UI.Page, ICommonPageMethods 
{
 #region ICommonPageMethods Members
 public void RedirectToPage(string uri, bool endResponse = false)
 {
  Response.RedirectToPage(uri, endResponse);
 }

 public void RedirectToPage(string uri, string queryStringParameters, bool endResponse = false)
 {
  Response.RedirectToPage(uri, queryStringParameters, endResponse);
 }

 public string AddQSP(string key, object value)
 {
  return PageExtensions.AddQSP(Server, key, value);
 }

 public string AddQSP(params object[] values)
 {
  return PageExtensions.AddQSP(Server, values);
 }
 #endregion
}

Controls
//ControlBase - The base class for all ascx controls
//ICommonPageMethods - The interface that must be implemented 
//                     in order to use the common methods safely
public class ControlBase : System.Web.UI.UserControl, ICommonPageMethods 
{ 
 #region ICommonPageMethods Members
 public void RedirectToPage(string uri, bool endResponse = false)
 {
  Response.RedirectToPage(uri, endResponse);
 }

 public void RedirectToPage(string uri, string queryStringParameters, bool endResponse = false)
 {
  Response.RedirectToPage(uri, queryStringParameters, endResponse);
 }

 public string AddQSP(string key, object value)
 {
  return PageExtensions.AddQSP(Server, key, value);
 }

 public string AddQSP(params object[] values)
 {
  return PageExtensions.AddQSP(Server, values);
 }
 #endregion
}

Static Class
/// 
/// All of the methods here are meant to be used in conjunction with a Page Base or a Control Base.
/// Common methods for both Pages (aspx) and Controls (ascx).
/// 
public static class PageExtensions
{
 public static void RedirectToPage(this HttpResponse r, string uri, bool endResponse = false)
 {
  r.Redirect(uri, endResponse);
 }

 public static void RedirectToPage(this HttpResponse r, string uri, string queryStringParameters, bool endResponse = false)
 {
  if (!string.IsNullOrEmpty(queryStringParameters))
  {
   if (!uri.EndsWith("?") && !queryStringParameters.StartsWith("?"))
    uri += "?";

   uri += queryStringParameters;
  }

  r.Redirect(uri, endResponse);
 }

 public static string AddQSP(HttpServerUtility s, string key, object value)
 {
  return key + "=" + s.UrlEncode(Convert.ToString(value));
 }

 public static string AddQSP(HttpServerUtility s, params object[] values)
 {
  if (values.Length.IsOdd())
   throw new Exception("Parameters provided must be an even number! Key Value Pairs.");

  StringBuilder sb = new StringBuilder();

  for (int i = 0; i < values.Length; i++)
  {
   sb.Append(values[i]);
   sb.Append("=");

   i++;

   sb.Append(s.UrlEncode(Convert.ToString(values[i])));
   sb.Append("&");
  }

  sb.TrimEnd("&");

  return sb.ToString();
 }        
}

Interface
//This interface will ensure that you keep your method definitions/signatures the same.
//I can't say that this is absolutely necessary - but it is more for discipline to
//make sure that you don't alter the method for one base class versus the other.
//It is easier to just play it safe and implement this interface.
public interface ICommonPageMethods
{
 void RedirectToPage(string uri, bool endResponse = false);
 
 void RedirectToPage(string uri, string queryStringParameters, bool endResponse = false);
 
 string AddQSP(string key, object value);
 
 string AddQSP(params object[] values);
}

No comments:

Post a Comment