As a designer, I constantly struggle with the time required for designing great software programs and writing user documentation that helps people understand how to use the program. [Some people argue that if it’s a great program, it doesn’t need documentation, but I know that even the brightest users often need a push in the right direction for how to get the most out of the product.]
I am particularly interested in auto-generating documentation for Web Part Properties. The CMS Admin Panel got me thinking about this problem. The Admin Panel needed an Admin Help Index – a list of the Web Parts in the Admin Panel, who can access them and a short summary of what they do. Since this information is readily available in the Web Part itself, I decided to auto-generate this documentation.
Auto-generating the Admin Help Index
I auto-generated the Admin Help Index in a Web Part using the following algorithm:
// Order the Web Parts in alphabetical orderSome notes about the algorithm used in Code Snippet 1:
WebPart[] webParts = new WebPart[mgr.WebParts.Count];
mgr.WebParts.CopyTo(webParts, 0);
IEnumerable<WebPart> parts = webParts.OrderBy(x => x.DisplayTitle);
foreach (WebPart wp in parts)
{
IAdminRole admin = wp as IAdminRole;
if (admin == null)
continue;
C.AddBreak(2, this);
C.AddLabel(String.Empty, "Title: " + C.AddStyle2H3(wp.DisplayTitle), Unit.Empty, this);
C.AddBreak(1, this);
if (admin.AdminRole != String.Empty)
{
C.AddLabel(String.Empty, "* Roles required to access: " + admin.AdminRole, Unit.Empty, this);
C.AddBreak(1, this);
}
C.AddLiteral("* Description: " + wp.Description, this);
}
Code Snippet 1: Generate an Admin Help Index
- Use the WebPartManager to find all Web Parts on the Page. The Admin Panel is on a page that contains all Web Parts indexed from the Admin Panel.
- Using LINQ, order the Web Parts alphabetically. In this case, I used the DisplayTitle property as the key.
- The ‘IAdminRole’ interface is used in Web Parts to identify the none, one or more roles that can access the (admin) Web Part. This comes in handy in filtering out the Admin Web Parts on the page. [There are other Web Parts on the page used for image header and navigation menu.]
- Format the output as shown below.
Title: Assign Roles to CMS User
* Roles required to access: WebMaster,Admin
* Description: TrackerRealm Assign Users Web Part assigns roles to a user.
The Admin Help Index uses Web Part Properties: DisplayTitle, Description and the IAdminRole interface to create a help index.
Auto-generating Documentation for Web Part Properties
Auto-generating Documentation for Web Part properties needs to use reflection to find the properties for the Web Part (because we only have the base class). I decided to write a Web Part for auto-generating documentation for Web Part Properties. This allows me to dynamically add the Web Part to a page and change the default property values.Here are the requirements for the AutoGenWebPart:- Accept input from the query string for the type of Web Part requested.
- Generate a header and list of properties showing each display name, description and default value, if used.
- Output the documentation in html for display on a web page, plain text, or wiki, for inclusion in a wiki.
Query String input
/// <summary>
/// Gets and Sets the format that the documentation is output: wiki, text, html
/// </summary>
[Category("Display"), Browsable(true), Personalizable(PersonalizationScope.Shared),
WebDisplayName("Output Type"), DisplayName("Output Type"),
Description("Describes the format that the documentation is output: wiki, text, html.")]
public OutputType OutputType
{
get { return this.op; }
set { this.op = value; }
}
Code Snippet 2: Sample Property for a Web Part Property
The property in Code Snippet 2 shows the Display Name and Description used in the documentation. This is meaningful to the user because the Web Part editor displays the Display Name for each Property.
string wpType = this.Context.Request.QueryString.Get("name");Code Snippet 3: Create the Web Part
if (wpType == null)
this.OutputText("No request received");
Type type = System.Web.Compilation.BuildManager.GetType(wpType, false);
if (type == null)
this.OutputText("Cannot find " + wpType);
WebPart wp = Activator.CreateInstance(type) as WebPart;
if (wp == null)
this.OutputText(wpType + " is not a Web Part");
The first step in the Web Part is getting the requested (type) name for the Web Part from the query string. If there is no parameter for ‘name’, then the user receives no output ("No request received").
With a text version of the type present, we can use reflection to get the Class and then create an instance of the class. If the type is not a Web Part class, then the user receives no output ("Cannot find " + wpType).
Auto-generate the Documentation
sb.AppendFormat("{0}{1}{2}{3}", this.h4s, wp.Title, this.h4e, this.br);
sb.AppendFormat("{0}Description{1}: {2}{3}", this.bs, this.be, wp.Description, this.br);
sb.AppendFormat("{0}Properties{1}{2}", this.h5s, this.h5e, String.Empty);
sb.AppendFormat("Properties are listed alphabetically{0}", this.br);
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(wp);
PropertyDescriptor[] props = new PropertyDescriptor[properties.Count];
properties.CopyTo(props, 0);
IEnumerable<PropertyDescriptor> pds = props.Where<PropertyDescriptor>(x => x.Category == "Display")
.OrderBy(x => x.DisplayName);
// Sets an PropertyDescriptor to the specific property.Code Snippet 4 shows the steps involved in auto-generating the documentation for the Web Part. It looks unnecessarily complicated with the formatting options defined by the instance variables. Let’s dissect the first statement: sb.AppendFormat("{0}{1}{2}{3}", this.h4s, wp.Title, this.h4e, this.br);
foreach (PropertyDescriptor p in pds)
{
switch (p.Name)
{
case "Title":
case "Description":
continue;
}
sb.AppendFormat("{0} {1}{2}{3}: {4}{5}", this.star, this.bs, p.DisplayName, this.be, p.Description, this.br);
object o = p.GetValue(wp);
if (o == null)
continue;
string value = o.ToString();
if (value != String.Empty)
sb.AppendFormat("{0}{1} Default Value: {2}{3}", this.tab, this.star, value, this.br);
}
Code Snippet 4: Auto-Generate the Property list for a Web Part
I wanted to dress up the title, so I included text formatting in the formatter.
- wp.Title is the Title of the Web Part
- this.h4s is the starting format for the title
- this.h4e is the ending format for the title
- this.br is the break between lines
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(wp);
This statement uses reflection to get the properties of the Web Part.
IEnumerable<PropertyDescriptor> pds = props.Where<PropertyDescriptor>(x => x.Category == "Display")
.OrderBy(x => x.DisplayName);Copy the properties into an array and then using System.Linq extensions, we find the Display Category properties and order them by DisplayName.Display the Output
private void OutputText(string text)
{
if (this.outputType == OutputType.Html)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
string styleSheet = String.Format("http://{0}{1}/app_themes/{2}/stylesheet.css",
context.Request.Url.Authority, context.Request.ApplicationPath, "default");
sb.AppendFormat(@"<html><head><link href=""{0}"" type=""text/css""
rel=""Stylesheet""/></head><body>", styleSheet);
sb.Append("</body></html>");
// Write the content to the Web Page
context.Response.ContentType = "text/HTML";
context.Response.Write(sb.ToString());
context.Response.End();
return;
}
// Display the Documentation as a text file (applies to txt and wiki options)
this.Context.Response.ContentType = "text/plain";
this.Context.Response.Write(text);
this.Context.Response.End();
}
Code Snippet 5: Output the text
The wiki and plain text format are output by writing the text to the browser.
Writing html is essentially the same, but is complicated because I like seeing the documentation with a good looking style sheet, in this case the style sheet from the default theme.
The following table shows a partial sample output of documenatation auto-generated for the CMS Admin Panel Web Part.
The following information is a brief description and list of properties for a Web Part developed by TrackerRealm. It is meant as a help summary.
CMS Admin Panel
Description: TrackerRealm CMS Admin Panel Web Part presents the administrative options for the site. The Correct Web Part is displayed for the selected action.
Properties
Properties are listed alphabetically
* Admin Role: The roles allowed to access this Web Part.
* Default Value: Admin
* Allow login to Jetfire?: Check if the login to Jetfire is allowed for users
* Default Value: False
* Back Color: Back Color for the Web Part.
* Default Value: Color [Empty]
* Devices: Devices that are allowed to see this WebPart: All, Normal, Mobile.
* Default Value: AllDevices
<--snip-->
0 comments:
Post a Comment