图 3 从WebPart类继承的类
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;

namespace WingtipWebParts {
  public class HelloWorld : WebPart  {

    public HelloWorld()  {
      this.Title = "Hello World";
      this.TitleIconImageUrl = @"img\WhaleBoy.gif";
    }

    protected override void RenderContents(HtmlTextWriter writer)  {
      writer.Write("Can I have your attention please");
    }
  }
}

图 5 Web部件页面的显示模式

Display ModeDescription
BrowseDisplayModeStandard view mode. No personalization or editing.
DesignDisplayModeEnables drag and drop layout personalization or customization.
EditDisplayModeEnables personalization or customization of Web Part properties to change appearance and behavior. Also permits the user to delete Web Parts that have been added to the page dynamically.
ConnectDisplayModeEnables user to connect Web Parts at run time.
CatalogDisplayModeEnables user to add Web Parts into a WebPartZone at run time.

图 7 自定义个人化属性
public enum Timeframe 
{
  Today,
  Week,
  Month
}
...
protected Timeframe _Timeframe = Timeframe.Month;

[Personalizable(PersonalizationScope.Shared), 
 WebBrowsable, WebDisplayName("Select a news timeframe"),
 WebDescription("Use this property to set timeframe")]
public Timeframe Timeframe 
{
    get { return _Timeframe; } set { _Timeframe = value; }
}

图 8 向页面中添加新的Web部件
<asp:CatalogZone ID="CatalogZone1" runat="server" >
  <ZoneTemplate>

    <!-- allows users to re-add Web Parts to page after being closed -->
    <asp:PageCatalogPart ID="PageCatalogPart1" 
                         runat="server" Title="Local Page Catalog" />

    <!-- allows users to add new Web Parts to page -->
    <asp:DeclarativeCatalogPart ID="DeclarativeCatalogPart1" 
                                runat="server" 
                                Title="Wingtip Web Parts">
      <WebPartsTemplate>
        <cc1:WeatherWebPart ID="WeatherWebPart1" 
                            runat="server" 
                            ZipCode="04090" />
      </WebPartsTemplate>
    </asp:DeclarativeCatalogPart>
  </ZoneTemplate>
</asp:CatalogZone>

图 12 动态属性的用户控件
public partial class HelloWorld2 : UserControl, IWebPart {

    protected string _title = "My custom user control";
    public string Title {
        get { return _title; }
        set { _title = value; }
    }

    private string _titleIconImageUrl = "~/img/ALLUSR.GIF";
    public string TitleIconImageUrl {
        get { return _titleIconImageUrl; }
        set { _titleIconImageUrl = value; }
    }

    private string _catalogIconImageUrl = "~/img/ALLUSR.GIF";
    public string CatalogIconImageUrl {
        get { return _catalogIconImageUrl; }
        set { _catalogIconImageUrl = value; }
    }

    // Remaining properties not shown...
}

图 13 IWebPart
public class WebPartBase : UserControl, IWebPart {

  protected string _title = "[Generic Title]";
  public string Title {
    get { return _title; }
    set { _title = value; }
  }

  private string _titleIconImageUrl = "~/img/star.GIF";
  public string TitleIconImageUrl {
    get { return _titleIconImageUrl; }
    set { _titleIconImageUrl = value; }
  }

  private string _catalogIconImageUrl = "~/img/star.GIF";
  public string CatalogIconImageUrl {
    get { return _catalogIconImageUrl; }
    set { _catalogIconImageUrl = value; }
  }

  // Remaining properties not shown...
}

// Code behind class for CustomerList.ascx User Control
public partial class CustomerList : WebPartBase {
  public CustomerList() {
    this.Title = "Wingtip Customer List";
    this.TitleIconImageUrl = @"~\img\ALLUSR.GIF";
    this.CatalogIconImageUrl = @"~\img\ALLUSR.GIF";
  }
}

图 14 自定义Web部件还是用户控件

FeatureWebPart-Inherited ClassUser Control
Can be built in separate DLL and installed in the GAC to reuse across applicationsYesNo
Can be added to Visual Studio toolbox with a fancy iconYesNo
Can contained extended verbsYesNo
Designer support availableNoYes

图 15 个人化提供者程序
public abstract class PersonalizationProvider : ProviderBase {
    protected abstract void LoadPersonalizationBlobs(
        WebPartManager webPartManager,
        string path, string userName, 
        ref byte[] sharedDataBlob, 
        ref byte[] userDataBlob);

    protected abstract void ResetPersonalizationBlob(
       WebPartManager webPartManager, 
       string path, 
       string userName);

    protected abstract void SavePersonalizationBlob(
       WebPartManager webPartManager, 
       string path, 
       string userName, 
       byte[] dataBlob);

    ... // remaining methods not shown
}

图 17 自定义个人化提供者类
namespace Wingtip.Providers {
  public class FileBasedPersonalizationProvider : PersonalizationProvider
  {
        public override string ApplicationName {
            get { ... } set { ... }
        }

        public override void Initialize(
            string name, NameValueCollection configSettings) { ... }

        public override int GetCountOfState(PersonalizationScope scope,
            PersonalizationStateQuery query) { ... }

        public override int ResetUserState(string path, 
            DateTime userInactiveSinceDate) { ... }

        protected override void LoadPersonalizationBlobs(
            WebPartManager webPartManager, string path, 
            string userName, ref byte[] sharedDataBlob, 
            ref byte[] userDataBlob) { ... }

        protected override void ResetPersonalizationBlob(
            WebPartManager webPartManager, string path, 
            string userName) { ... }

        public override int ResetState(PersonalizationScope scope, 
            string[] paths, string[] usernames) { ... }

        protected override void SavePersonalizationBlob(
            WebPartManager webPartManager, string path, 
            string userName, byte[] dataBlob) { ... }

        public override PersonalizationStateInfoCollection FindState(
            PersonalizationScope scope,
            PersonalizationStateQuery query, int pageIndex, 
            int pageSize, out int totalRecords) { ... }
   }
}

图 18 LoadPersonalizationBlobs方法 和 SavePersonalizationBlob方法
protected override void LoadPersonalizationBlobs(
  WebPartManager webPartManager, string path, string userName, 
  ref byte[] sharedDataBlob, ref byte[] userDataBlob)
{
  string fileName = HttpContext.Current.Server.MapPath(
    string.IsNullOrEmpty(userName) ?
      ConstructAllUsersDataFileName(path) :
      ConstructUserDataFileName(userName, path));    
  if (!File.Exists(fileName)) return;

  fileName = string.Intern(fileName);
  try
  {
    // lock on the file name in case two clients try accessing 
    // the same file concurrently - note we lock on the Interned 
    // file name string, which will always return the same object 
    // for identical strings
    if (Monitor.TryEnter(fileName, 5000))
    {
      if (string.IsNullOrEmpty(userName))
        sharedDataBlob = File.ReadAllBytes(fileName);
      else
        userDataBlob = File.ReadAllBytes(fileName);
    }
    else throw new ApplicationException("Monitor timed out");
  }
  finally { Monitor.Exit(fileName); }
}

protected override void SavePersonalizationBlob(
  WebPartManager webPartManager, string path, 
  string userName, byte[] dataBlob)
{
  string fileName = HttpContext.Current.Server.MapPath(
    string.IsNullOrEmpty(userName) ?
      ConstructAllUsersDataFileName(path) :
      ConstructUserDataFileName(userName, path));    

  fileName = string.Intern(fileName);
  try
  {  
    // lock on the file name in case two clients try accessing 
    // the same file concurrently
    if (Monitor.TryEnter(fileName, 5000))
    {
      File.WriteAllBytes(HttpContext.Current.Server.MapPath(
        fileName), dataBlob);
    }
    else throw new ApplicationException("Monitor timed out");
  }
  finally { Monitor.Exit(fileName); }
}

// Helper function for creating a unique file name for all users 
// based on a path
private string ConstructAllUsersDataFileName(string path)
{
  string fileName = 
    path.Replace('/', '_').Replace('~', '_').Replace('.', '_');
  return "~/App_Data/allusers" + fileName + ".bin";
}

// Helper function for creating a unique file name for a particular 
// user based on a path
private string ConstructUserDataFileName(string user, string path)
{
  string fileName = 
    path.Replace('/', '_').Replace('~', '_').Replace('.', '_');
  return "~/App_Data/" + user + fileName + ".bin";
}