Using a Custom CatalogPart to add WebParts dynamically at run time

On my current project I am working on using WebParts to format the pages.  I needed a way to be able to add WebParts dynamically at run time based upon the default settings as well as if they are going to be shared with other sections of the web site.

I have spent about 10 hours working on how to be able to add WebParts dynamically to the CatalogPart section for the WebPartManager.  I have had to search the web and look at several different posting about WebParts to finally get the desired result.

I need to give special credit to the following:

  1. Jesse Liberty (his article gave me the base understanding of how to implement the built in functionality of WebParts and how to use them)
    1. Web Parts in ASP.NET 2.0 http://ondotnet.com/pub/a/dotnet/2005/01/10/liberty.html
  2. Johan Danforth’s Blog (his three blog entries gave me the understanding on how to write a custom CatalogPart)
    1. [.NET 2.0] Adding Web Parts programmatically http://weblogs.asp.net/jdanforth/archive/2004/08/12/213542.aspx
    2. [.NET 2.0] Adding Web Parts programmatically (part 2) http://weblogs.asp.net/jdanforth/archive/2004/08/19/216990.aspx
    3. [.NET 2.0] Creating your own CatalogPart http://weblogs.asp.net/jdanforth/archive/2005/09/26/426005.aspx

The custom CatalogPart class that I came up with is as follows:

using System.Collections.Generic;
using System.Web.UI.WebControls.WebParts;

namespace Controls {
  public class WebCMSCatalogPart : CatalogPart {
    private Dictionary<WebPartDescriptionWebPart> webParts = new Dictionary<WebPartDescriptionWebPart>();

    public int GetWebPartCount() {
      return webParts.Count;
    }

    public void AddWebPart(WebPart wp) {
      if (wp.ID == null || wp.ID.Length == 0) {
        wp.ID = string.Format("WebPart{0}", webParts.Count);
      }
      WebPartDescription wpDescription = new WebPartDescription(wp);
      webParts.Add(wpDescription, wp);
    }

    public override WebPartDescriptionCollection GetAvailableWebPartDescriptions() {
      List<WebPartDescription> list = new List<WebPartDescription>();

      foreach (KeyValuePair<WebPartDescriptionWebPart> kvp in this.webParts) {
        list.Add(kvp.Key);
      }

      return new WebPartDescriptionCollection(list);
    }

    public override WebPart GetWebPart(WebPartDescription description) {
      WebPart wp = null;
      if (webParts.ContainsKey(description) == true) {
        wp = webParts[description];
      }
      return wp;
    }

  }
}

To add controls to the custom CatalogPart I used the following code on the page that contains the WebParts:

Aspx Page:

<%@ Register Namespace="Controls" TagPrefix="wc" %>

        <asp:CatalogZone ID="CatalogZone1" runat="server">
            <ZoneTemplate>
                <wc:WebCMSCatalogPart ID="WebCMSCatalogPart1" runat="server" Title="Available Web Parts Catalog" />
                <asp:DeclarativeCatalogPart ID="DeclarativeCatalogPart1" runat="server">
                    <WebPartsTemplate>
                    </WebPartsTemplate>
                </asp:DeclarativeCatalogPart>
                <asp:PageCatalogPart ID="PageCatalogPart1" runat="server" />
                <asp:ImportCatalogPart ID="ImportCatalogPart1" runat="server" />
            </ZoneTemplate>
        </asp:CatalogZone>

Code Behind Page:

  protected void Page_Init(object sender, EventArgs e) {
    AddWebParts();
  }

  private void AddWebParts() {
    Controls.WebCMSCatalogPart cp = CatalogZone1.CatalogParts.OfType<Controls.WebCMSCatalogPart>().FirstOrDefault();
    if (cp != null) {

      Calendar cal = new Calendar();
      cal.ID = string.Format("WebPart{0}", cp.GetWebPartCount());
      GenericWebPart genericWebPart = WebPartManager1.CreateWebPart(cal);
      genericWebPart.Title = "Calendar Control";
      cp.AddWebPart(genericWebPart);

      Login login = new Login();
      login.ID = string.Format("WebPart{0}", cp.GetWebPartCount());
      genericWebPart = WebPartManager1.CreateWebPart(login);
      genericWebPart.Title = "Login Control";
      cp.AddWebPart(genericWebPart);
    }
  }

Again I spend about 10 hours trying to get an understand of how to implement a custom CatalogPart so I could add WebParts dynamically at run time.

Have a textbox only allow whole numbers

I needed a textbox that will only allow numbers:

private void textBox_KeyPress(object sender, KeyPressEventArgs e) {

     if (e.KeyChar != 8 && char.IsNumber(e.KeyChar) == false) {

          e.Handled = true;

     }

}

To create a select statement for all tables in the database

The following will create a select statement for Sql Server for all tables in the database:

SELECT ‘SELECT FROM ‘ + name FROM sys.Tables

Getting the years between 2 dates

I needed to get the years between 2 dates. When taking 2 dates and subtracting the one from the other it returns a TimeSpan. The timespan does not contain a Years or TotalYears property or method.

To get to the needed result I have created the following method:

private int GetYears(DateTime dtm) {

       TimeSpan ts = DateTime.Today.Subtract(dtm);

 

      // Used to calc start year

     int adjustYears = 4;

 

     for (int i = 4 – 1; i >= 0; i—) {

         if (DateTime.DaysInMonth(dtm.Year – i, 2) == 29) {

              adjustYears += i;

              break;

         }

     }

 

     return new DateTime().AddYears(adjustYears – 1).AddDays(Math.Abs(ts.TotalDays)).Year –

                            adjustYears;

}

Using Linq over HtmlDocument

I needed to get the anchor tags OuterHtml from HTML that I already had as text.

The following is how I used linq to get the outerhtml:

private void LinqHtmlDocument(string htmlText) {
  WebBrowser wb = new WebBrowser();
  wb.Navigate(string.Empty);
  HtmlDocument hd = wb.Document;
  hd.Write(htmlText);
  var query = from HtmlElement he in hd.All
              where he.TagName == "A"
              && he.OuterHtml.Contains("SomeText")
              select new {
                OuterHtml = he.OuterHtml,
              };
}

Extension Method to give the value of a passed object

This extension method will return the value to a given type:

public static T Val<T>(this object expression) where T : IConvertible
{
    T retVal = default(T);
 
    if (expression != null)
    {
        var regEx = new Regex(@"^([0-9]|-|\+|,|\$|\.){1,}");
        if (regEx.IsMatch(expression.ToString().Trim()))
        {
            var match = regEx.Match(expression.ToString().Trim()).Groups[0].Value.Replace("--", "-");
            var returnType = retVal.GetType();
            // intergers can not have decimal points in the 
            // removes the part of the string from the decimal point afterword
            if (returnType.Name.StartsWith("Int") && match.Contains("."))
            {
                match = match.Substring(0, match.IndexOf("."));
            }
            retVal = (T)Convert.ChangeType(match, returnType);
        }
    }
 
    return retVal;
}

Getting an Anonymous Type Value

The following is a extension method that will get the value from an Anonymous Type:

public static T GetAnonymousValue<T>(this object o, string propertyName)
{
    System.Reflection.PropertyInfo pi = o.GetType().GetProperty(propertyName);
    T retVal = default(T);
    if (pi != null)
    {
        retVal = (T)pi.GetValue(o, null);
    }
 
    return retVal;
}

Take into account that it will throw a Specified cast is not valid if it cannot cast to the passed type.

Different ways to code a BackgroundWorker

One way is for it to call bw_RunWorkerCompleted as follows:

private void RunBackgroundWorker()

     System.ComponentModel.BackgroundWorker bw = new System.ComponentModel.BackgroundWorker();

     bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);

     bw.RunWorkerAsync();

}

void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {

     MessageBox.Show(“Done”, “CAPTION”, MessageBoxButtons.OK, MessageBoxIcon.Information);

}

A more concise method it to call it inline:

private void RunBackgroundWorkerInline() {

     System.ComponentModel.BackgroundWorker bwInline = new

                                                       System.ComponentModel.BackgroundWorker();

     bwInline.RunWorkerCompleted += (s, ea) => { MessageBox.Show(“Done”, “CAPTION”,

                                          MessageBoxButtons.OK, MessageBoxIcon.Information); };

     bwInline.RunWorkerAsync();

}

Getting the path of a xml node

I needed a function that would give the path of an xml node to the root.

This is the result that I came up with:

private string XmlNodePath(XmlNode node) {

  List<string> list = new List<string>();

  XmlNode workNode = node;

  do {

    if (workNode.Name != “#text”) {

      list.Insert(0, workNode.Name);

    }

    workNode = workNode.ParentNode;

  } while (workNode.NodeType == XmlNodeType.Element);

 

  return string.Join(“/”, list.ToArray());

}

Instantiate a class with named parameters

Person person = new Person { Name = “FirstName LastName”, Age = 23 };