Warning: I don't consider myself a control developer in the classic sense (although I frequently write web parts for deployment within a SharePoint environment, the scope of that deployment is fairly small by default).
A lot of the functionality I build these days runs client-side, powered by my all-time best friend jQuery. Because of this, I find I'm most productive when I first establish the HTML structure in a static HTML file before moving the mark-up to a control. In the bad old days of complete ignorance, I would have had refactor this HTML and build it programmatically within a custom control—an approach I despise (HTML belongs in mark-up).
To overcome many of the drawbacks to custom control development, I prefer migrating my prototype to a user control (which has a front-end .ascx file) and then loading that control in my web part code-behind or custom field control. Check out the LoadControl documentation for information about how load a user control programmatically.
This works beautifully when the control's code behind is compiled to an assembly destined for deployment to the private bin directory. The user control can be built in isolation with the ascx file and code behind remaining wired up for easy access to Intellisense, etc. At build time, the .ascx file is copied somewhere and deployed somewhere useful (the CONTROLTEMPLATES virtual directory, if you like).
Things get trickier when that assembly is to be deployed to the global assembly cache (aka the GAC). In my case I wanted to do this for a custom field control; although most of the code for that control was already being deployed to the GAC, it made sense to keep these artefacts together in the same project. For my non-SharePoint readers, any assembly going into the GAC has to be strong named and signed (via the project's properties sheet > Signing tab); to add the assembly to the GAC, call gacutil:
gacutil –i "MyAsemblyName.dll"
With that out of the way, assume we've got two projects: the first (MyControls) is a class library outputting a signed assembly intended for the GAC; the second is a simple web site (Web).
The MyControls project contains our user control (for information about how to set this up, refer to my post How to add a web project item to a class library). The MyControls assembly is deployed to the GAC.
The web site project contains a copy of the .ascx user control file from the MyControls project and a web page with a @ Register directive pointing to the project-local .ascx file. The Web project doesn't reference the MyControls project because we want it to load the assembly it depends on from the GAC. The .ascx copy can be done manually but you'll likely want to automate this as a pre-build task.
While the MyControls project will now compile, the Web site project will fail to compile with the error: Could not load type 'MyControls.MyUserControl'. If you're in a SharePoint environment, you'll likely see get this as a parser error when the page is dynamically compiled at first request.
To fix this, you need to add an @ Assembly directive to the top of the .ascx file to reference the MyControls assembly deployed to the GAC. You'll need the assembly name and public key token to flesh out this directive. The assembly name can be retrieved from the project properties sheet (normally it's the same as the project anyway). Then extract the public key token using the strong name application (it's the short value):
sn –Tp "MyControls.dll"
If your AssemblyInfo.cs specifies a version number of 18.104.22.168, your @ Assembly directive should look something like this:
<%@ Assembly Name="MyControls, Version=22.214.171.124, Culture=Neutral, PublicKeyToken=b3de351d91c5d4d2"%>
If you version numbers are automatically updated by some kind of policy or build event, beware you'll also need to update this directive as well, which may prove cumbersome.
This directive can be added to both copies of the .ascx file without impacting the MyControls build or edit-time experience.
Both projects will now compile and run, successfully loading the user control base type (i.e. the code behind) from the GAC.
You can download this solution here.