This article is an excerpt from Advanced Microsoft Content Management Server Development published by Packt Publishing; ISBN 1904811531; copyright Packt Publishing 2005; all rights reserved.
Lim Mei Ying is a senior consultant with Avanade and has extensive experience in setting up Microsoft Content Management Server (MCMS) systems at the enterprise level, and she is a Microsoft Valuable Professional for MCMS. Mei Ying's blog
Stefan Goßner works for Microsoft as an escalation engineer in the Developer Support department. He maintains an extensive MCMS 2002 FAQ on the Microsoft Web site and provides MCMS tips and tricks on his personal blog. Stefan's blog
Angus Logan is a product specialist at Data#3 Limited, and he is a MCAD.NET and MCDBA, as well as a Microsoft Valuable Professional for MCMS. Angus' blog
Andrew Connell is a client/server consultant for Fidelity Information Services, and he is a Microsoft Valuable Professional for MCMS. Andrew's blog
No part of these chapters may be reproduced, stored in a retrieval system, or transmitted in any form or by any means—electronic, electrostatic, mechanical, photocopying, recording, or otherwise—without the prior written permission of the publisher, except in the case of brief quotations embodied in critical articles or reviews.
Chapter 8: Useful Placeholder Controls (Part 1 of 2)
Contents
8.1 Before We Begin
8.2 A Date-Time Picker Placeholder Control
8.2.1 The DateTimePickerPlaceholderControl Class
8.2.2 Loading the Controls for Authoring
8.2.3 Retrieving Saved Content
8.2.4 Saving the Date and Time
8.2.5 Validating the Date and Time
8.2.6 Presenting the Selected Date and Time
8.2.7 The Date and Time Picker Dialog Box
8.2.8 Adding the Placeholder Control to a Template File
One of the most exciting features of Microsoft Content Management Server (MCMS) 2002 is the flexibility to create custom placeholder server controls. You can create placeholder controls of all shapes and sizes, providing myriad solutions tailored to the needs of authors in any organization. If something is not available out of the box—build it! And there is a good chance that you don't even have to build custom placeholder controls from scratch. MCMS 2002 developers around the world may have already built a similar control in their own work. This chapter compiles a collection of commonly requested and very useful placeholder controls.
- Date-time picker placeholder control
This provides a picker for authors to select a date and time value in the format of their choice. Say goodbye to badly formed and invalid dates with this nifty control.
- Placeholder control that accepts multiple attachments
This enables authors to upload as many files as they need by giving them a control that acts like multiple SingleAttachmentPlaceholderConrols working in concert. We will also explore the undocumented Insert Attachment dialog box that is included with Web Author and show how you can reuse it within custom placeholder controls.
8.1 Before We Begin
It's always a good idea to place all code related to custom placeholder controls within a project separate from your main MCMS Web project. It allows cleaner deployment of controls across Web sites. In this chapter, we will do just that. Follow the steps outlined below to set up a project where we will build all class files for the custom placeholder controls in this chapter:
- Create a new Visual C# Class Library named UsefulPlaceholderControls.
- Add the following references to the project:
- Microsoft.ContentManagement.Common
- Microsoft.ContentManagement.Publishing
- Microsoft.ContentManagement.Publishing.Extensions.Placeholders
- Microsoft.ContentManagement.WebControls
- System.Web
The library files from the Microsoft.ContentManagement namespace can be found in the Microsoft Content Management Server\Server\bin\ directory.
Delete the Class1.cs file that was created by the wizard. We won't need it.
8.2 A Date-Time Picker Placeholder Control
Have you ever tried getting authors to enter date and time values in a regular text box? Even with well-placed instructions showing the proper format for the date (such as MM/DD/YYYY), you will often find that dates collected are invalid. Here are some real-life examples taken from a Web site that provided text boxes for entering the promotion start and end dates of a product.
Table 8-1. Examples of invalid date and time formats
| Dates Entered | Why It is Not Valid |
| Start Date: Immediate!
End Date: 31 February 2004 07:00 AM | The word "immediate" isn't a valid date and 31 February 2004 does not exist, not even in a leap year! |
| Start Date: 1 January 2005 :00:00 AM
End Date: 31 December 2005 (or while stocks last!) | While the start date is valid, the start time is missing the hour component.
The end date appears valid, but it has extra text, or while stocks last, appended to it—making it invalid. |
| Start Date: 1 Janaury 2005
End Date: 31/05/2005 | In the start date, the word "January" is misspelled.
The month and day fields in the end date are mixed up. |
To ensure that date and time fields are entered correctly, authors can be provided with a date-time picker control, instead of a text box. The date-time picker control includes a single-line, read-only text box that displays the selected date and time. As the text box is read-only, authors can't amend its contents—making it impossible for authors to enter invalid dates.
Dates are selected by clicking a Pick Date button, located next to the text box. When it is clicked, a dialog box for picking a date and time opens. The dialog box contains a calendar for selecting the date and two drop-down lists for choosing the time values.
.gif)
Figure 8-1. Dialog box for picking date and time
In presentation view, the selected date and time appears on the screen as shown in the following figure.
.gif)
Figure 8-2. Presentation view in dialog box
To make it almost impossible for invalid dates to be saved in the database, the contents of the date-time picker control are usually validated before being saved.
MCMS does not include a date-time picker placeholder control, so let's build one from scratch.
8.2.1 The DateTimePickerPlaceholderControl Class
Let's begin building the date-time picker placeholder control by adding a class file named DateTimePickerPlaceholderControl.cs to the UsefulPlaceholderControls project.
The control requires the use of methods from the namespaces highlighted below, so add them to the class file.
using System;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using Microsoft.ContentManagement.Publishing;
using Microsoft.ContentManagement.Publishing.Extensions.Placeholders;
using Microsoft.ContentManagement.WebControls.Design;
using Microsoft.ContentManagement.WebControls;
namespace UsefulPlaceholderControls
{
. . . code continues . . .
}
As the selected date and time is a single-line value, it will be easiest to store it as text within an HtmlPlaceholder (alternatively, you could store it in an XmlPlaceholder—however, that also means that you will have to extract the information from the saved XML later, which requires a little more code). The control will therefore support HtmlPlaceholderDefinitions. In addition, as none of the placeholder controls that are included with MCMS resembles a date-time picker, we will build the control from the ground up and have it inherit from the BasePlaceholderControl class:
namespace UsefulPlaceholderControls
{
[SupportedPlaceholderDefinitionType(typeof(HtmlPlaceholderDefinition))]
public class DateTimePickerPlaceholderControl : BasePlaceholderControl
{
public DateTimePickerPlaceholderControl()
{
}
}
}
8.2.2 Loading the Controls for Authoring
In authoring mode, the date-time picker placeholder will include:
- A text box for displaying the selected date and time.
- A button for picking the date. When the button is clicked, a dialog box appears for the author to pick the date and time.
We will use a table to arrange the TextBox and Button controls side by side. Below the table, we will add a Label for showing any exceptions that may occur during the loading process. Here's the completed control in authoring mode:
.gif)
Figure 8-3. Completed control shown in authoring mode
Let's start by adding the table to the authoring container. Override the CreateAuthoringChildControls() method as shown below. The table consists of a single row with two cells.
protected override void CreateAuthoringChildControls
(BaseModeContainer authoringContainer)
{
// Add a table to help in the layout of the controls
Table table = new Table();
TableRow tr = new TableRow();
table.Rows.Add(tr);
TableCell td1 = new TableCell();
TableCell td2 = new TableCell();
tr.Cells.Add(td1);
tr.Cells.Add(td2);
// Add the table to the authoring container
authoringContainer.Controls.Add(table);
}
Now, let's add the read-only TextBox control to the cell on the left side. The TextBox control will be given an ID of SelectedDateTime. We define the instance of the TextBox as a class-wide variable as we will be accessing it from other methods within the class.
private TextBox txtAuthoring;
protected override void CreateAuthoringChildControls
(BaseModeContainer authoringContainer)
{
. . . code continues . . .
// Add a Textbox
txtAuthoring = new TextBox();
txtAuthoring.ID = "SelectedDateTime";
txtAuthoring.ReadOnly = true;
td1.Controls.Add(txtAuthoring);
}
Next, we will add the Pick Date button. When the button is clicked, it calls up the dialog box for selecting the date and time. Attached to the dialog box's URL are a couple of query string parameters:
- CallingControl stores the name of the TextBox control to which the dialog box will write the selected date and time. This is the client ID of the TextBox, as dynamically generated by Microsoft ASP.NET when the page is displayed. If you actually run the control, you will find that its client ID is the concatenation of all the parent controls' IDs:
DateTimePickerPlaceholderControl1_
AuthoringModeControlsContainer_SelectedDateTime
- InitialValue stores the previously selected date and time values that will be used as default values by the dialog box.
The code snippet below assumes that the URL of the date-time picker dialog box is /tropicalgreen/dialogs/datetimepicker.aspx. If you plan to store the dialog box somewhere else, simply adjust this value accordingly.
protected override void CreateAuthoringChildControls
(BaseModeContainer authoringContainer)
{
. . . code continues . . .
// Add a Button that shows a pop-up when clicked
HtmlButton b = new HtmlButton();
b.ID = "SelectedDateTime";
b.InnerText = "Pick Date";
string url = "";
url = "/tropicalgreen/dialogs/datetimepicker.aspx?"
+ "CallingControl=" + txtAuthoring.ClientID + "&"
+ "InitialValue=\' + document.all. "
+ txtAuthoring.ClientID
+ ".value +\'";
b.Attributes.Add("onclick","window.open('" + url + "');");
td2.Controls.Add(b);
}
Finally, we add a Label control to the authoring container. The Label will be used to display error messages that may occur along the way. Like the TextBox, we will make it a class-wide variable as we will be using it within other methods.
private TextBox txtAuthoring;
private Label lblMessage;
protected override void CreateAuthoringChildControls
(BaseModeContainer authoringContainer)
{
. . . code continues . . .
// Add a label for displaying messages
lblMessage = new Label();
authoringContainer.Controls.Add(lblMessage);
}
8.2.3 Retrieving Saved Content
When the posting is opened for editing, the text box of the date-time picker control shows the previously saved date and time value. For new postings, there won't be a previous selection; we will just use the current date and time. Override LoadPlaceholderContentForAuthoring():
protected override void LoadPlaceholderContentForAuthoring
(PlaceholderControlEventArgs e)
{
EnsureChildControls();
try
{
if (WebAuthorContext.Current.Mode ==
WebAuthorContextMode.AuthoringNew)
{
// The default value of the control is the current date and
// time
txtAuthoring.Text =
DateTime.Now.ToString("dd MMM yyyy HH:mm");
}
else
{
txtAuthoring.Text =
((HtmlPlaceholder)this.BoundPlaceholder).Text;
}
}
catch(Exception ex)
{
lblMessage.Text = "Failed to load placeholder content: " +
ex.Message;
}
}
8.2.4 Saving the Date and Time
When the posting is saved, we read the value stored in the TextBox and write it to the underlying HtmlPlaceholder. Any exceptions are trapped using a try-catch block and the detailed error message appears on screen. Add the overridden SavePlaceholderContent() method to the code:
protected override void
SavePlaceholderContent(PlaceholderControlSaveEventArgs e)
{
EnsureChildControls();
try
{
((HtmlPlaceholder)this.BoundPlaceholder).Html =
txtAuthoring.Text;
}
catch(Exception ex)
{
lblMessage.Text = ex.Message;
}
}
8.2.5 Validating the Date and Time
To ensure that only valid dates are saved, we will override the OnSavingContent() method. We check to see if the value to be saved is a valid date by attempting to convert the string stored in the TextBox to a DateTime object. If it is a valid date, we won't get an exception. Otherwise, we catch the exception and the save operation can be canceled.
protected override void
OnSavingContent(PlaceholderControlSavingEventArgs e)
{
try
{
DateTime.Parse(txtAuthoring.Text.Trim());
base.OnSavingContent(e);
// Clear any messages on the screen
lblMessage.Text = "";
}
catch(Exception ex)
{
e.Cancel = true;
lblMessage.Text = ex.Message;
}
}
8.2.6 Presenting the Selected Date and Time
In presentation mode, the selected date and time are displayed on the screen in a Label control. Let's override the CreatePresentationChildControls() method and add a Label control to the presentation container.
private Label lblPresentation;
protected override void CreatePresentationChildControls
(BaseModeContainer presentationContainer)
{
lblPresentation = new Label();
presentationContainer.Controls.Add(lblPresentation);
}
The previously saved date and time value in the underlying HtmlPlaceholder is retrieved from the content repository and displayed in the Label control loaded earlier. Add the overridden LoadPlaceholderContentForPresentation() method as shown:
protected override void LoadPlaceholderContentForPresentation
(PlaceholderControlEventArgs e)
{
EnsureChildControls();
try
{
lblPresentation.Text =
((HtmlPlaceholder)this.BoundPlaceholder).Text;
}
catch(Exception ex)
{
lblPresentation.Text = ex.Message;
}
}
The date-time picker placeholder control is complete. Next, let's build the dialog box for selecting the date and time.
8.2.7 The Date and Time Picker Dialog Box
The Date and Time Picker dialog box contains:
- An ASP.NET Calendar control for choosing the date.
- Two DropDownList controls, one for selecting the hour, another for the minute.
- A Button control for setting the date and time value.
Add a Web Form named DateTimePicker.aspx to the MCMS Web project (in this example, we have added it to the Dialogs folder of the Tropical Green project). Ensure that the Page Layout property is changed to FlowLayout. Now toggle to HTML view and add the following headers to the form:
. . . code continues . . .
<form id="DateTimePicker" method="post" runat="server">
<b>Pick Date:</b>
<hr>
<b>Pick Time</b>
<hr>
</form>
. . . code continues . . .
Switch back to Design view. Drag and drop the controls in Table 8-2 onto the Web Form and arrange them as shown in Figure 8-4.
Table 8-2. Web Form controls
| Control | Property | Property Value |
| Calendar | ID | Calendar1 |
| DropDownList | ID | ddlHour |
| DropDownList | ID | ddlMinute |
| Button | ID | btnOK |
| | Text | OK |
| | Width | 100px |
.gif)
Figure 8-4. Controls arranged in Web Form
Double-click on the form to get to the code-behind file. Within the Page_Load() event handler, we retrieve the previously selected date and time from the InitialValue query string parameter. If for some reason (maybe the TextBox was empty), the parameter doesn't contain a valid date and time, we use the current date and time instead.
private void Page_Load(object sender, System.EventArgs e)
{
if(!Page.IsPostBack)
{
// Retrieve the previously saved date and time
DateTime initialDate;
try
{
initialDate =
DateTime.Parse(Request.QueryString["InitialValue"].ToString());
}
catch
{
// No previously saved date and time found.
// Use the current date and time.
initialDate = DateTime.Now;
}
}
}
The previously selected date and time is used to initialize the Calendar control and the two drop-down lists that make up the hour and minute of the selected time. In the Page_Load() event handler, we also fill in the hour and the minute drop-down lists.
private void Page_Load(object sender, System.EventArgs e)
{
if (!Page.IsPostBack)
{
. . . code continues . . .
Calendar1.SelectedDate = initialDate;
Calendar1.VisibleDate = initialDate;
// fill up the drop-down list for hours
for (int i=0; i <= 23; i++)
{
ListItem li = new ListItem();
li.Text = i.ToString("00");
if (initialDate.ToString("HH") == li.Text)
{
li.Selected = true;
}
ddlHour.Items.Add(li);
}
// fill up the drop-down list for minutes
for (int i=0; i <= 59; i++)
{
ListItem li = new ListItem();
li.Text = i.ToString("00");
if (initialDate.ToString("mm") == li.Text)
{
li.Selected = true;
}
ddlMinute.Items.Add(li);
}
}
}
Toggle back to Design view and double-click the OK button to get to the btnOK_Click() event handler. Here, we will generate the client-side script that passes the selected date and time value back to the calling control. The calling control is retrieved from the CallingControl query string parameter. Earlier, we have set this to contain the ID of the TextBox used by the date-time picker placeholder control.
To write the selected date and time, we retrieve the values of the Calendar control and of the hour and minute drop-down lists. Using a JavaScript call, we write the value to the TextBox.
private void btnOK_Click(object sender, System.EventArgs e)
{
string callingControl = Request.QueryString["CallingControl"];
System.Text.StringBuilder script =
new System.Text.StringBuilder();
script.Append("<script language=\"javascript\">");
script.Append("window.opener.document.all." + callingControl
+ ".value='"
+ Calendar1.SelectedDate.ToString("dd MMM yyyy")
+ " "
+ ddlHour.SelectedItem.Value + ":"
+ ddlMinute.SelectedItem.Value
+ "';");
script.Append("window.close();");
script.Append("</script>");
Page.RegisterClientScriptBlock("PickDate",script.ToString());
}
And that completes the dialog box. Save and build the solution.
If you need a more portable control that can be deployed across multiple projects without having to recreate the Web Form each time you use the control, consider one of the following options:
- Create a folder within the /IIS_CMS/ folder and store the dialog box there. As all MCMS Web projects have a virtual directory named CMS mapped to the IIS_CMS folder, the path of the dialog box would be /CMS/SubFolder/datetimepicker.aspx.
- You could follow the concept used by MCMS when deploying the /nr/ folder across all Web sites on the same computer by adding the folder that contains the DateTimePicker control to each virtual Web site that uses it.
- Alternatively, use the technique MCMS uses when deploying the /CMS/ folder to new MCMS template projects. Add the folder that contains the DateTimePicker control to each template project that requires it. When getting the path of the control from within the code, use the value returned from Page.Request.ApplicationPath. To automate this for new projects, you could create a new enterprise template for Visual Studio .NET. For more information about creating and editing enterprise templates, see Enterprise Templates for Distributed Applications.
- Rewrite the DateTimePickerPlaceholderControl so that the calendar, hour, and minute drop-down menus are generated on the control itself. You can embed them within a <div> tag and use client-side script to show them when the Pick Date button is clicked.
8.2.8 Adding the Placeholder Control to a Template File
To add the control to an MCMS project, you will have to add the component to the Toolbox:
- Right-click the tab you want to add the control to.
- Select Add/Remove Items and choose to add a .NET Framework Component.
- Select Browse and select UsefulPlaceholderControls.dll. Click Open.
- Click OK to close the dialog box and add the control(s) to the Toolbox.
After the control has been added to the Toolbox, you can drag and drop it onto a template file and set its properties as you would with regular placeholder controls.
.gif)
Figure 8-5. Drag control from Toolbox and set properties
Go ahead; add the date-time picker placeholder control to an existing template file. Set the PlaceholderToBind property to an appropriate HtmlPlaceholderDefinition. Navigate to a posting that uses the template and edit it. You should see a text box and a Pick Date button. Click the Pick Date button and select a date. Save the posting. Notice that the TextBox and Button controls are replaced by a single Label showing the date and time that you have selected.
Read Chapter 8, Part 2