Refactoring a web project so you can share user controls across others


I recently built a brand new site, which launched last week and have now been tasked with adding an additional B2B component to it. The B2B site will have all the same styling as the B2C site, and will share a lot of functionality that I’ve already written into User Controls for the main web site. So I was faced with a problem- How can I share these controls across both projects? I don’t want to simply copy and paste, because then I have two controls to maintain, so I took to Google where I found this old article by Scott Guthrie.

I figured I would document the re-factoring exercise I went through.

So I started with my main web project which looked like this;

I shall break down the refactor into some simple steps;

  1. I created a new class library project which could be shared between the two web projects, in this instance I called mine Chinook.Web.Helpers
  2. Create a “Controls” folder and drag all the controls from the original web project, into the new shared projects control folder (Make sure both the ASPX and the code behind parts came across!). Build the new shared project- you will probably find some build errors and will need to add references to some web specific .net dll’s- I needed
    • System.Configuration
    • System.Web
    • System.Web.Extensions
    • System.Web.Extensions.Design
    • System.Web.Services

    As well as these I also had to add references to my .Model and .Interfaces projects, but this will be different depending on how you’ve setup your project. At this stage I had a projects which built and looked like this;

  3. I updated the namespace for each controls code behind and the first line of each ascx to reflect the namespace of the new project;

    //namespace Chinook.Web.Controls
    namespace Chinook.Web.Helpers.Controls
    {
        public partial class QuickContact : System.Web.UI.UserControl
    

    ..and the new inherits parameters in the ascx;

    <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="QuickContact.ascx.cs"
        Inherits="Chinook.Web.Helpers.Controls.QuickContact" %>
    
  4. Next I added a reference in my web project to the new shared project, then deleted the controls from my controls folder, leaving an empty controls folder. All the references to the controls in your pages remain the same; for example
    <%@ Master Language="C#" AutoEventWireup="True" CodeBehind="Site.master.cs" Inherits="Chinook.Web.SiteMaster" %>
    <%@ Register Src="Controls/RSSLister.ascx" TagName="RSSLister" TagPrefix="uc1" %>
    <!DOCTYPE html>
    <html lang="en">
    <head runat="server">
    ...
    <ul id="rss" class="orange-bullets">
      <uc1:RSSLister ID="BlogRSSFeed" runat="server" />
    </ul>
    
  5. So the project is now happy- we just need to add the pre-build step which will copy the ascx files (but not the code behind) over to each projects control folder on pre-build. So in the original web project, right click the project and go to properties -> Build Events and set the pre-build event so that it copies;

    copy $(SolutionDir)\Chinook.Web.Helpers\Controls\*.ascx $(ProjectDir)\Controls\
    
  6. Build and run your web project!

I also moved over a few other bits. I had an ashx file which handles file uploads- so I moved the code behind to the shared projects and just updated the ashx file to inherit from the same class, but in it’s new namespace. I also had a resx file which contained user friendly error messages which I moved to be central. My final helper looked like this;

Make sure that you don’t edit the ascx files that now exist within your web project, as there will be overwritten by the versions in the shared project everytime you re-build.

If you use the publish option (you probably do!), make sure you “show all files” on your web project after a build include the ascx files into your web project, so it gets copied up.

  1. #1 by Gareth on August 12, 2011 - 08:16

    Awesome stuff. Another MSBuild miracle :)

  2. #2 by shawson on August 12, 2011 - 13:22

    haha cheers mate- though to be honest it’s pretty much a copy of the original article with some stuff specific to my implementation. A good find though I thought- it seems like a pretty common scenario but there’s not much online about how to approach it. Feels like something which should be easier to do!

(will not be published)