sfeldman.NET

.NET, code, personal thoughts

Factory Pattern for User Controls

User Controls are handy when working in web forms, but what a mess they can generate. I was asked what to do when UCs are used and some dependencies need to be injected during the construction - but UCs are a bit tricky in regards to construction. So the possibilities are:

1. Use new operator and instantiate a user control (Web Application Project) - no good as the visual elements residing on the designer surface (.ascx) then not loaded.

2. Use Page.LoadControl(<path>) - but no option to pass in parameters.

3. Use Page.LoadControl(<type>, params object) - not strongly typed.

So all these possibilities are nice, but not helping to use a load with strongly typed parameters. Then the idea proposed at one of the replies with almost the same question was to use a static factory. I tried to play with the idea and this was a result.

Step 1 - create the UC

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="SomeUserControl.ascx.cs" 
Inherits="LoadControlWithFactory.SomeUserControl" %>
<asp:TextBox runat="server" id="txtName"></asp:TextBox>

Step 2 - create the code behind with static factory method

   1:    public partial class SomeUserControl : UserControl
   2:    {
   3:      private string name;
   4:   
   5:      protected override void OnLoad(EventArgs e)
   6:      {
   7:        base.OnLoad(e);
   8:        txtName.Text = name;
   9:      }
  10:   
  11:      public static SomeUserControl Create(string name)
  12:      {
  13:        SomeUserControl some = 
       ((Page)HttpContext.Current.Handler).LoadControl("~/SomeUserControl.ascx") as SomeUserControl;
  14:        some.name = name;
  15:        return some;
  16:      }
  17:    }

Note: the underlined code has a bad smell - but that was the only idea I had to gain access to the currently executed page. Any other ideas are welcomed.

Step 3 - load UC dynamically through the code with injected data

   1:    public partial class _Default : System.Web.UI.Page
   2:    {
   3:      protected override void OnPreRender(EventArgs e)
   4:      {
   5:        base.OnPreRender(e);
   6:        phContent.Controls.Add(SomeUserControl.Create("Sean Feldman"));
   7:        phContent.Controls.Add(SomeUserControl.Create("Anna Feldman"));
   8:      }
   9:    }

Conclusions

What it seems is that this allowed a few more things than just a pattern implementation. It allowed:

  1. DRY principle - rather than replicating the .ascx path wherever UC is loaded, this information is now stored and encapsulated inside the factory.
    LoadControl("~/SomeUserControl.ascx") as SomeUserControl; // for Sean
    LoadControl("~/SomeUserControl.ascx") as SomeUserControl; // for Sean
  2. Simplicity - Drop Dead Simple principle (well, this is something I like to apply recently to the code to make is easy to digest, so this is the name I came up with).
    SomeUserControl.FactoryMethod(<typed params>);
  3. Maintainability - simple and affordable.

Update 2007-12-17: I have re-arranged slightly the post to minimize clipping and attached the code.

Published Monday, December 17, 2007 10:01 AM by Sean Feldman
Filed under: , , ,

Comments

# re: Factory Pattern for User Controls@ Monday, December 17, 2007 10:24 AM

Your code is getting clipped...is there anyway you can provide a download of your samples?

by John S.

# re: Factory Pattern for User Controls@ Monday, December 17, 2007 12:03 PM

@John,

the file is attached at the bottom. Stupid of me not to do that from the first time.

# re: Factory Pattern for User Controls@ Monday, December 17, 2007 12:25 PM

I like SomeUserControl uc = SomeUserControl.Create();

I use a static public field on the control so that the path only appears inside the user control

# re: Factory Pattern for User Controls@ Monday, December 17, 2007 2:18 PM

@Andrew,

Agree. The reason I went for method is to allow parameters in to have constructing point with options. The only thing I am still not happy about is casting to the page.

# re: Factory Pattern for User Controls@ Monday, December 17, 2007 2:51 PM

The control must go on a Page, the only "fix"" I've found is to move the uglyness to a helper class and to throw with a helpfull message if the .Handler isn't of type page.

My first post was about not specifying the url to the control :)

# re: Factory Pattern for User Controls@ Monday, December 17, 2007 4:09 PM

Out of curiosity, why are you not using the Page property on the user control?

Page.LoadControl(...)

instead of

((Page)HttpContext.Current.Handler).LoadControl

by Bill

# re: Factory Pattern for User Controls@ Monday, December 17, 2007 5:11 PM

@Bill,

Page is not static, so it's not accessible from the factory method that is declared as a static.

# re: Factory Pattern for User Controls@ Friday, February 01, 2008 2:21 AM

>>Page is not static, so it's not accessible from the factory method

>>that is declared as a static.

Why not to pass Page as a parameter of the static factory method?

by romb

# re: Factory Pattern for User Controls@ Friday, February 01, 2008 11:01 AM

@romb,

you are right, it is possible and then a page becomes a dependency for the user control. But UC can be added on any surface that support Controls collection, and it's not a page only (another UC, repeater, placeholder, etc). So I would pass in the parameter as the lowest common type (I wish that would be an interface) that is possible.

# re: Factory Pattern for User Controls@ Friday, March 13, 2009 7:17 AM

Sehr gute Seite. Ich habe es zu den Favoriten.

by ...

# re: Factory Pattern for User Controls@ Sunday, May 31, 2009 3:18 AM

thanks !! very helpful post!