A look at Visual Web Parts in Visual Studio 11
Posted
Thursday, January 12, 2012 10:23 AM
by
CoreyRoth
I recently published my first post about SharePoint development with Visual Studio 11 and I mentioned that there were some changes in how the Visual Web Part worked. If you are familiar with the Visual Web Part in Visual Studio 2010, you know it really is just a glorified user control and it doesn’t work in the sandbox. Luckily, the community stepped up and provided some alternatives that do work. Visual Studio 11 now provides a Visual Web Part that works in the sandbox out of the box. Since I am always curious about how things work, I decided to take a deeper look to see just how this web part is implemented now.
Let’s first take a look at the files that we get when we add a Visual Web Part to the project.
There are actually a few less files that we had in Visual Studio 2010. We’ll see why here shortly. What we get are the following
- Elements.xml – Used to deploy the .webpart file via module element
- A .ascx file – The actual user control with the design surface
- A .ascx.cs file – The code behind for the user control
- A .ascx.g.cs file – This file is generated by the designer. We’ll talk about it more
- A .webpart file – The file that goes into the Web Part gallery that has the assembly path to the web part
If we look at the .ascx.cs file, we’ll see our first difference.
using System;
using System.ComponentModel;
using System.Web.UI.WebControls.WebParts;
namespace SharePointProject1.HelloWorldWebPart
{
[ToolboxItemAttribute(false)]
public partial class HelloWorldWebPart : WebPart
{
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
InitializeControl();
}
protected void Page_Load(object sender, EventArgs e)
{
}
}
}
You’ll notice that this file inherits from WebPart now instead of UserControl. At this point, you might be wonder how does all of this work. Well, the magic is in the designer and what ends up in the .ascx.g.cs file. Let’s take a look at it when it’s first created.
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:2.0.50727.5448
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace SharePointProject1.HelloWorldWebPart {
using System.Web;
using System.Text.RegularExpressions;
using Microsoft.SharePoint.WebPartPages;
using Microsoft.SharePoint.WebControls;
using System.Web.Security;
using Microsoft.SharePoint.Utilities;
using System.Web.UI;
using System;
using System.Web.UI.WebControls;
using System.Collections.Specialized;
using Microsoft.SharePoint;
using System.Collections;
using System.Web.Profile;
using System.Text;
using System.Web.Caching;
using System.Configuration;
using System.Web.UI.WebControls.WebParts;
using System.Web.SessionState;
using System.Web.UI.HtmlControls;
public partial class HelloWorldWebPart {
public static implicit operator global::System.Web.UI.TemplateControl(HelloWorldWebPart target)
{
return target == null ? null : target.TemplateControl;
}
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
private void @__BuildControlTree(global::SharePointProject1.HelloWorldWebPart.HelloWorldWebPart @__ctrl) {
}
private void InitializeControl() {
this.@__BuildControlTree(this);
this.Load += new global::System.EventHandler(this.Page_Load);
}
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
protected virtual object Eval(string expression) {
return global::System.Web.UI.DataBinder.Eval(this.Page.GetDataItem(), expression);
}
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
protected virtual string Eval(string expression, string format) {
return global::System.Web.UI.DataBinder.Eval(this.Page.GetDataItem(), expression, format);
}
}
}
It may not make a lot of sense at this point, but when you start adding controls, it makes more sense. I added a label, textbox, and button control to the design surface.
This looks like a typical user control that you are confortable working with. Now let’s take a look at what the .ascx.g.cs file has now. I’ll just include the parts that changed.
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
private global::System.Web.UI.WebControls.Label @__BuildControlLabel1() {
global::System.Web.UI.WebControls.Label @__ctrl;
@__ctrl = new global::System.Web.UI.WebControls.Label();
this.Label1 = @__ctrl;
@__ctrl.ApplyStyleSheetSkin(this.Page);
@__ctrl.ID = "Label1";
@__ctrl.Text = "Label";
return @__ctrl;
}
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
private global::System.Web.UI.WebControls.TextBox @__BuildControlTextBox1() {
global::System.Web.UI.WebControls.TextBox @__ctrl;
@__ctrl = new global::System.Web.UI.WebControls.TextBox();
this.TextBox1 = @__ctrl;
@__ctrl.ApplyStyleSheetSkin(this.Page);
@__ctrl.ID = "TextBox1";
return @__ctrl;
}
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
private global::System.Web.UI.WebControls.Button @__BuildControlButton1() {
global::System.Web.UI.WebControls.Button @__ctrl;
@__ctrl = new global::System.Web.UI.WebControls.Button();
this.Button1 = @__ctrl;
@__ctrl.ApplyStyleSheetSkin(this.Page);
@__ctrl.ID = "Button1";
@__ctrl.Text = "Button";
return @__ctrl;
}
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)]
private void @__BuildControlTree(global::SharePointProject1.HelloWorldWebPart.HelloWorldWebPart @__ctrl) {
System.Web.UI.IParserAccessor @__parser = ((System.Web.UI.IParserAccessor)(@__ctrl));
@__parser.AddParsedSubObject(new System.Web.UI.LiteralControl("\r\n<div>\r\n "));
global::System.Web.UI.WebControls.Label @__ctrl1;
@__ctrl1 = this.@__BuildControlLabel1();
@__parser.AddParsedSubObject(@__ctrl1);
@__parser.AddParsedSubObject(new System.Web.UI.LiteralControl("\r\n "));
global::System.Web.UI.WebControls.TextBox @__ctrl2;
@__ctrl2 = this.@__BuildControlTextBox1();
@__parser.AddParsedSubObject(@__ctrl2);
global::System.Web.UI.WebControls.Button @__ctrl3;
@__ctrl3 = this.@__BuildControlButton1();
@__parser.AddParsedSubObject(@__ctrl3);
@__parser.AddParsedSubObject(new System.Web.UI.LiteralControl("\r\n</div>\r\n"));
}
The designer creates methods for each ASP.NET control you drag onto the page and then the @__BuiltControlTree method calls those methods to effectively add those controls to the page. The HTML div elements that I added to the page simply get included as a LiteralControl. Needless to say you don’t want to mess with this file. I have to give props to the Visual Studio and SharePoint teams. I am sure this was a lot of work to implement. :) Give it a try if you want. It seems to work well and the best part is what I have tried so far works with SharePoint Online.
If you haven’t checked out Visual Studio 11 yet, head over to the developer site where you can get the download link and find out everything that’s new.