What to know about SmartPart, and LoadControl()
At Infusion ASP.NET developers regularly ask how to easily build a web part or how to host a user control (.ascx) in SharePoint. Someone invariably replies "SmartPart!" at which point my job is to make sure they understand what they're getting into.
SmartPart
is a cleverly coded web part by fellow MOSS MVP
Jan Tielens. Once
installed, you can drag an instance of SmartPart into a
SharePoint web part zone and configure the new web part to
host an .ascx file stored in the file system. And like
magic, you can host user controls in SharePoint. Recent
versions (v1.3+) are AJAX-aware, support web part
connections, and improved the deployment story, and the
underlying code is really quite good.
But, there are drawbacks, some of which may be overcome,
some of which are realities.
1. SmartPart examples set web.config to Full Trust. Not
for the web part, but for the entire SharePoint
application. You do not want to do this in Production, and
happily this is avoidable. However, it does mean that you
can't avoid the pain of creating a CAS policy (e.g. if
your controls require access to the SharePoint API or
unmanaged resources) by using the SmartPart.
2.
SmartPart is an open source project, but the download
contains only its classes so you can't compile it without
some work of your own. If you work for clients who want to
use SmartPart, I strongly recommend building your own
SmartPart project as a starting point, and maintaining
that code base as new features are added to the CodePlex
project. While you won't "own" the code (copyright rules
would say it's still Jan's and you should attribute it as
such), you will now have full control and accountability
for the code running on your servers, as it should be.
3. The SmartPart disclaimer defines exactly what to expect should things go wrong, or should you need to extend its capabilities: ". . . the Software comes 'as is', with no warranties. None whatsoever. This means no express, implied or statutory warranty, including without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement." Part of the license is to distribute this license whenever SmartPart is used.
To be fair, this disclaimer is similar to many software licenses, and all open source licenses. Linux is open source and offers no support. When things go wrong with Linux, Linus Torvalds will not show up at your door to hold your hand. To make Linux palatable for Production, Red Hat sells support. If you pay extra, they may even hold your hand.
SmartPart carries no support, and no commercial version
exists that would provide support. But, if you follow the advice of the last point and create
you own project so you have control of the code, you can
support your own. This is what you want to avoid:
there was a period of several months after GotDotNet was
decommissioned and SmartPart did not yet exist on
CodePlex. During this time it was impossible to download
any "official" version of SmartPart, current or otherwise.
had the source been released earlier (it wasn't) this
wouldn't have inconvenienced anyone. From now on should
the source become unavailable for any reason, you'll only
have yourself to blame.
4. Building a custom web part that uses LoadControl() to
host a user control is not difficult, and SmartPart's
source code is a great way to learn how! The sample code
below shows you how to load a user control and interact
with its content from a web part, in this case to retrieve
the content of a SharePoint list and implement paging.
Using the sample code
This is intended to roughly parallel the Smartpart - the
user control will go into a usercontrols folder at the root
of your application and the web part's assembly can be
deployed to the GAC unless you'd like to write a CAS policy
to deploy it to the bin folder.
Place the ascx in a \usercontrols folder into the Web
Application's root (e.g.
C:\Inetpub\wwwroot\wss\VirtualDirectories\80\usercontrols).
Create the web part, sign the project and add a line like
this to the SafeControls section of web.config. Remember to
replace the Assembly attribute with your own, best obtained
by opening your assembly with
Reflector:
<SafeControl Assembly="LoadControlWP,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=myToken"
Namespace="LoadControlWP" TypeName="LoadControlWebPart"
Safe="True" />
Then get your web part's
assembly into the GAC and recycle the application pool, or
build a CAS policy (if you control requires it) and place it
in the web application's bin folder.
Sample code (LoadControlWP.cs):
using System;
using System.Collections.Generic;
using
System.Text;
using System.Web;
using
System.Web.UI;
using System.Web.UI.WebControls;
using
System.Data;
using
System.Web.UI.WebControls.WebParts;
using
System.Diagnostics;
using Microsoft.SharePoint;
namespace
LoadControlWP
{
public class
LoadControlWebPart :
System.Web.UI.WebControls.WebParts.WebPart
{
private UserControl usercontrol;
private
GridView gvDemo;
private const string
defaultlist = "";
private string _listtolink =
defaultlist;
protected DataTable dtDemo =
null;
[Personalizable(),
WebBrowsable(),
WebDisplayName("List to
display"),
WebDescription("Name of the list in
this site to display")]
public string
ListToLink
{
get { return
_listtolink; }
set { _listtolink = value;
}
}
protected override void
CreateChildControls()
{
try
{
base.CreateChildControls();
this.Controls.Clear();
this.GetData();
usercontrol =
(UserControl)Page.LoadControl(@"/usercontrols/wpgrid.ascx");
gvDemo =
(GridView)this.usercontrol.FindControl("gvDemo");
gvDemo.AllowPaging = true;
gvDemo.DataSource = dtDemo;
gvDemo.PageSize = 3;
gvDemo.PageIndexChanging += new
GridViewPageEventHandler(gvDemo_PageIndexChanging);
this.Controls.Add(usercontrol);
gvDemo.DataBind();
}
catch
(Exception ex)
{
EventLog.WriteEntry("WebParts", "UCWebPart" +
ex.ToString());
}
}
void gvDemo_PageIndexChanging(object sender,
GridViewPageEventArgs e)
{
gvDemo.PageIndex = e.NewPageIndex;
gvDemo.DataBind();
}
private
void GetData()
{
try
{
if (ListToLink.Length > 0)
{
dtDemo = new DataTable();
dtDemo.Columns.Add("Title",
Type.GetType("System.String"));
SPWeb site = SPContext.Current.Web;
SPList list = site.Lists[_listtolink];
foreach (SPListItem item in list.Items)
{
DataRow newRow =
dtDemo.NewRow();
newRow["Title"] = item["Title"];
dtDemo.Rows.Add(newRow);
}
}
}
catch (Exception
ex)
{
EventLog.WriteEntry("WebParts", "UCWebPart - Retrieving
items from " + _listtolink + "-" + ex.ToString(),
EventLogEntryType.Error);
}
}
}
}
Sample code (wpgrid.ascx):
<%@ Control Language="C#" ClassName="WebUserControl"
%>
<script runat="server">
</script>
<asp:GridView
ID="gvDemo" runat="server" AutoGenerateColumns="False"
CellPadding="4" ForeColor="#333333" GridLines="None">
<Columns>
<asp:BoundField
DataField="Title" HeaderText="Title" />
</Columns>
<FooterStyle
BackColor="#5D7B9D" Font-Bold="True" ForeColor="White"
/>
<RowStyle BackColor="#F7F6F3"
ForeColor="#333333" />
<EditRowStyle
BackColor="#999999" />
<SelectedRowStyle
BackColor="#E2DED6" Font-Bold="True" ForeColor="#333333"
/>
<PagerStyle BackColor="#284775"
ForeColor="White" HorizontalAlign="Center" />
<HeaderStyle BackColor="#5D7B9D" Font-Bold="True"
ForeColor="White" />
<AlternatingRowStyle
BackColor="White" ForeColor="#284775" />
</asp:GridView>