SharePoint Development with Visual Design User Controls


Table of Contents

As you may know, developing SharePoint Web Parts currently is completely done via code behind if you want to add TextBoxes and Labels. This is not what ASP.NET developers are used to as you get a Visual Design surface to work with.
Serge Luca did a great web cast introducing VSeWSS 1.3 and in the process demonstrated how you can move a User Control (ASCX) into a SharePoint Web Part and deploy it using a WSP.

Create a Web Application Project

Essentially the approach is to have a normal web application project with a web page and a user control. You can then do your visual design directly in this and see it running.

The User Control ascx code will look something like this below

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="WebUserControl.ascx.cs" Inherits="WebUserControl" %>
<asp:Panel ID="Panel1" runat="server">
    <asp:Label ID="Label1" runat="server" Text="Label">First Name: </asp:Label>
    &nbsp;<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
    <br />
    <asp:Button ID="Button1" runat="server" Text="Submit" onclick="Button1_Click" />
</asp:Panel>
</asp:Panel>

and the code behind (ascx.cs) will look something like this

Unable to find source-code formatter for language: csharp. Available languages are: actionscript, html, java, javascript, none, sql, xhtml, xml
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class WebUserControl : System.Web.UI.UserControl
{
    protected Label Label1;
    protected void Page_Load(object sender, EventArgs e)
    {

    }
    protected void Button1_Click(object sender, EventArgs e)
    {
        Label1.Text = DateTime.Now.ToString();
    }
}

Below is a screenshot showing the Visual Design of the User Control with the split source code view also

Why use User Controls?

To do this in a SharePoint Web Part you would need the following code and you would not get any design surface!

Unable to find source-code formatter for language: csharp. Available languages are: actionscript, html, java, javascript, none, sql, xhtml, xml
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.WebControls;

namespace WSPBuilderProject1
{
    [Guid("80f1424f-202c-42c7-a519-145a5c9f511c")]
    public class WebPartFeature1 : Microsoft.SharePoint.WebPartPages.WebPart
    {
        private bool _error = false;
        protected Label Label1;
        public WebPartFeature1()
        {
            this.ExportMode = WebPartExportMode.All;
        }

        /// <summary>
        /// Create all your controls here for rendering.
        /// Try to avoid using the RenderWebPart() method.
        /// </summary>
        protected override void CreateChildControls()
        {
            if (!_error)
            {
                try
                {
                    base.CreateChildControls();

                    Panel Panel1 = new Panel();
                    Label1 = new Label();
                    Panel1.Controls.Add(Label1));
                    Button Button1 = new Button();
                    Button1.Click += new EventHandler(Button1_Click);
                    Panel1.Controls.Add(Button1);
                    this.Controls.Add(Panel1);
                }
                catch (Exception ex)
                {
                    HandleException(ex);
                }
            }
        }

        void Button1_Click(object sender, EventArgs e)
        {
           Label1.Text = Label1.Text + DateTime.Now.ToString();
           //TODO: do stuff!
        }

        /// <summary>
        /// Ensures that the CreateChildControls() is called before events.
        /// Use CreateChildControls() to create your controls.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnLoad(EventArgs e)
        {
            if (!_error)
            {
                try
                {
                    base.OnLoad(e);
                    this.EnsureChildControls();

                    // Your code here...
                }
                catch (Exception ex)
                {
                    HandleException(ex);
                }
            }
        }

        /// <summary>
        /// Clear all child controls and add an error message for display.
        /// </summary>
        /// <param name="ex"></param>
        private void HandleException(Exception ex)
        {
            this._error = true;
            this.Controls.Clear();
            this.Controls.Add(new LiteralControl(ex.Message));
        }
    }
}

Create a WSP Project

Using your favourite solution package development tool (WSPBuilder, STSDev, VSeWSS) you can create a web part artifact.

Copy the User Control to the WSP Project

The User Controls will be deployed to the \12\TEMPLATES\CONTROLTEMPLATES\ directory. The code behind file can go in the same folder or in a class file folder.

Change the User Control code behind

Class change

For the user control to work in the SharePoint web part you will need to change the class so it is no longer 'partial'.

Add a namespace around the class

Add a namespace around the class to make the assembly reference easier

Unable to find source-code formatter for language: csharp. Available languages are: actionscript, html, java, javascript, none, sql, xhtml, xml
using System;
using System.Web.UI.WebControls;
namespace WSPBuilderProject1
{
    public class WebUserControl : System.Web.UI.UserControl
    {
        protected Label Label1;
        protected void Page_Load(object sender, EventArgs e)
        {

        }
        protected void Button1_Click(object sender, EventArgs e)
        {
            Label1.Text = DateTime.Now.ToString();
        }
    }
}

Change the User Control ascx

Remove the CodeFile attribute from Control block

You need to remove the CodeFile attribute completely as it will not reference this anymore.

Assembly reference

Now that the class has moved into your WSP project, you will need to modify the assembly reference. More info is available here on how to get the assembly reference line.

<%@ Control Language="C#" AutoEventWireup="true" Inherits="WSPBuilderProject1.WebUserControl, WSPBuilderProject1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c710c1834d2c527b" %>
<asp:Panel ID="Panel1" runat="server">
    <asp:Label ID="Label1" runat="server" Text="Label">First Name: </asp:Label>
    &nbsp;<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
    <br />
    <asp:Button ID="Button1" runat="server" Text="Submit" onclick="Button1_Click" />
</asp:Panel>

Modify the SharePoint Web Part code behind

Modify the SharePoint Web Part CreateChildControls() method to add the WebUserControl. Note that it uses the Page.LoadControl method and locates it in '/_controltemplates/' directory.

Unable to find source-code formatter for language: csharp. Available languages are: actionscript, html, java, javascript, none, sql, xhtml, xml
        protected override void CreateChildControls()
        {
            if (!_error)
            {
                try
                {
                    base.CreateChildControls();
                    WebUserControl webUserControl = (WebUserControl)this.Page.LoadControl("/_controltemplates/WebUserControl.ascx");

                    this.Controls.Add(webUserControl);
                }
                catch (Exception ex)
                {
                    HandleException(ex);
                }
            }
        }

Deploy and Activate

Now you just need to simply deploy your WSP and activate the feature on your site. The web part will now be available to add to a page like below screenshot:

Issues

The problem with this approach is that once you've copied the User Control into the WSP Project you obviously lose the Visual Design experience. An alternative approach would be to automate the copying whenever you make changes in the Visual Design surface using a file level comparison tool such as Beyond Compare - this way you can copy the changes without the two tweaks made above.

External References

Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.


  1. Apr 27, 2009

    Thomas Holland says:

    I like to create two projects - 1 for the web part (using STSDEV) and one web ap...

    I like to create two projects - 1 for the web part (using STSDEV) and one web app project that contains the ascx file.  I then modify the post-build events in Visual Studio to copy the ascx to the STSDEV project location on build.  That way you can test and debug in a normal web project and then deploy directly to SharePoint.

    1. Sep 04, 2009

      Alex Angas says:

      I agree. This seems to be the most reliable way to approach it so you still main...

      I agree. This seems to be the most reliable way to approach it so you still maintain the design experience but can continue the use a tool such as STSDEV or WSPBuilder.

  2. Jul 03, 2009

    Anonymous says:

    Rather than copying files around, you can instead use the Add as Link option whe...

    Rather than copying files around, you can instead use the Add as Link option when adding the .ascx file to the Control Templates folder.  This ensures that you don't have any issues with using the wrong version of the .ascx file as it only exists in one location.

    See item 10 in the section Creating the ASP.NET User Control within the SharePoint Guidance package for more details: http://msdn.microsoft.com/en-us/library/dd206945.aspx

  3. Aug 06, 2009

    Anonymous says:

    Hi, I'm sitting with a big issue. I need to change the display of my webpart af...

    Hi,

    I'm sitting with a big issue. I need to change the display of my webpart after hitting a button and then fire another event for one of the buttons on the new ui but the new buttons event won't work. how do I do this?
    My code is below. The saveTitle button's event works but not the btnAcceptClocks event.
    protected override void CreateChildControls()
    {

    Controls.Clear();

    if (ZoneContent == "1")

    Unknown macro: { btnAcceptClocks = new Button(); btnAcceptClocks.ID = "btn1"; btnAcceptClocks.Text = "Button1"; btnAcceptClocks.Click += new EventHandler(btnAcceptClocks_Click); Controls.Add(btnAcceptClocks); Controls.Add(btnAcceptClocks); }

    else

    Unknown macro: { //Create lable //base.CreateChildControls(); lblName = new Label(); lblName.Text = "Lable"; this.Controls.Add(lblName); //Create text box newTitle = new TextBox(); newTitle.Text = Convert.ToString(X); Controls.Add(newTitle); //Create button saveTitle = new Button(); saveTitle.ID = "save"; saveTitle.Text = "Set Web Part Title"; saveTitle.Click += new EventHandler(saveTitle_click); Controls.Add(saveTitle); }

    }

    public void btnAcceptClocks_Click(object sender, EventArgs e)

    Unknown macro: { ZoneContent = "x"; CreateChildControls(); }

    public void saveTitle_click(object sender, EventArgs e)

    Unknown macro: { ZoneContent = "s"; CreateChildControls(); }

Creative Commons License
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License. Hosted generously by CustomWare