5. Saving the List of Attachments
When the posting is saved, the SavePlaceholderContent()
method is called. Here, we will loop through all rows in the table to
look for attachments. If an attachment is found, we create an XML node
consisting of the attachment’s URL, display text, and icon. At the end
of the routine, the list of attachments is saved.
For example, if we have chosen to display attachments as icons, the saved XML would be:
<Attachments>
<Attachment>
<DisplayText>Aloe Vera</DisplayText>
<Url>/NR/rdonlyres/73C94F98-1424-41EE-B4FA-D2301314A7FA/0/AloeVera.JPG
</Url>
<Icon><img alt="AloeVera.JPG" src="/NR/rdonlyres/73C94F98-1424-41EE-B4FA-
D2301314A7FA/0/AloeVera.JPG?thumbnail=true&label=AloeVera.JPG" border="0">
</Icon>
</Attachment>
</Attachments>
Add the overridden SavePlaceholderContent() method directly below the LoadPlaceholderContentForAuthoring() method.
protected override void SavePlaceholderContent(PlaceholderControlSaveEventArgs e)
{
string sPhName = this.PlaceholderToBind;
EnsureChildControls();
// Save the list of attachments as XML
XmlPlaceholder xph = (XmlPlaceholder) base.BoundPlaceholder;
XmlDocument xd = new XmlDocument();
XmlNode xnRoot = xd.CreateElement("Attachments");
for (int i = 0; i < m_maxAttachments; i++)
{
if (Page.Request.Form["MAPH_AttLink_" + sPhName + "_"+i.ToString()] != "")
{
XmlNode xAttachment = xd.CreateElement("Attachment");
// Store the attachment's display text
XmlNode xDisplayName = xd.CreateElement("DisplayText");
xDisplayName.InnerText = Page.Request.Form["MAPH_AttName_" + sPhName
+ "_" + i.ToString()];
xAttachment.AppendChild(xDisplayName);
// Store the attachment's URL
XmlNode xUrl = xd.CreateElement("Url");
xUrl.InnerText = Page.Request.Form["MAPH_AttLink_" + sPhName + "_"
+ i.ToString()];
xAttachment.AppendChild(xUrl);
// Store the icon
XmlNode xIcon = xd.CreateElement("Icon");
xIcon.InnerText = Page.Request.Form["MAPH_AttIconUrl_" + sPhName + "_"
+ i.ToString()];
xAttachment.AppendChild(xIcon);
// Add the node to the XML Doc
xnRoot.AppendChild(xAttachment);
}
}
xph.XmlAsString = xnRoot.OuterXml;
}
6. Retrieving Saved Content
To display the stored content, we first have to retrieve it. Here’s the plan:
First prepare three arrays for storing the attachment URLs, icons, and display text.
Next, retrieve the saved XML from the underlying XmlPlaceholder.
Once
we have the XML, we extract information about the attachment from the
tags and store them in the arrays that we have prepared.
Finally, we load the content back into the table of all uploaded attachments.
Preparing Arrays for Storing Information about the Attachments
Let’s prepare three string arrays for storing a
list of attachment URLs, icons, and display text. Add the following code
to the start of the LoadPlaceholderContentForAuthoring() method:
// Stores the URL of the attachment
private string[] attUrl = new string[m_maxAttachments];
// Stores the icon
private string[] attIcon = new string[m_maxAttachments];
// Stores the Display text
private string[] attName = new string[m_maxAttachments];
protected override void LoadPlaceholderContentForAuthoring
(PlaceholderControlEventArgs e)
{
. . . code continues . . .
}
Retrieving Previously Saved XML
We retrieve the saved XML from the XmlPlaceholder.XmlAsString property. If the string is empty, as will be the case for a new posting, we add the root element to make it valid:
protected override void
LoadPlaceholderContentForAuthoring(PlaceholderControlEventArgs e)
{
// Get saved content
XmlPlaceholder xph = (XmlPlaceholder) base.BoundPlaceholder;
string xmlContent = xph.XmlAsString;
if (xmlContent == "")
{
xmlContent = "<Attachments />";
}
XmlDocument xd = new XmlDocument();
xd.LoadXml(xmlContent);
. . . code continues . . .
}
Extracting Information about the Attachments from the XML
Now that we have the saved XML, we need to
extract the attachment’s URL, icon, and display text from it. The
attachment’s display text is taken from the <DisplayText> element; its URL is retrieved from the <URL> element, and the image of the entire icon from the <Icon> element.
protected override void
LoadPlaceholderContentForAuthoring(PlaceholderControlEventArgs e)
{
. . . code continues . . .
xd.LoadXml(xml);
// Extract URLs and AltText
int index = 0;
foreach (XmlNode xn in xd.DocumentElement.ChildNodes)
{
attUrl[index] = xn.SelectSingleNode("Url").InnerText;
attIcon[index] = xn.SelectSingleNode("Icon").InnerText;
attName[index] = xn.SelectSingleNode("DisplayText").InnerText;
index++;
}
. . . code continues . . .
}
We are now ready to populate the table with the saved content.
Populating the Table with the Saved Attachments
There are four fields in the table that require information about the attachments:
The cell with an ID beginning with MAPH_AttIcon for holding the attachment’s icon
The hidden form field with an ID beginning with MAPH_AttIcon for holding the attachment’s icon
The textbox with an ID beginning with MAPH_AttName for storing the display text
The read-only textbox with an ID beginning with MAPH_AttLink for storing the URL of the attachment
Let’s start with the icons. The attachment’s
icon is displayed in the first cell of each row. We will get the icon
stored in the attIcon[] array and add it to the cell. Add the code highlighted below:
protected override void LoadPlaceholderContentForAuthoring(
PlaceholderControlEventArgs e)
{
. . . code continues . . .
// Add the cell for the attachment's icon
htmlCode.Append("<td align=center id=\"MAPH_AttIcon_" + sPhName
+ "_" + i.ToString() + "\">" + attIcon[i] + "</td>" + crlf;
. . . code continues . . .
}
The icon is also stored in a hidden field in the second column of the table. The hidden field has a name starting with MAPH_AttIcon and, like the cell in the first column, will be populated with the contents of the attIcon[] array.
protected override void
LoadPlaceholderContentForAuthoring(PlaceholderControlEventArgs e)
{
. . . code continues . . .
// Add a hidden form field to store the icon
htmlCode.Append("<input type=\"hidden\" name=\"MAPH_AttIconUrl_" + sPhName
+ "_"+i.ToString() + "\" value='" + attIcon[i] + "'>");
. . . code continues . . .
}
The display text of the attachment is shown in a textbox with a name that starts with MAPH_AttName. We will set it to hold the contents of the attName[] array.
protected override void
LoadPlaceholderContentForAuthoring(PlaceholderControlEventArgs e)
{
. . . code continues . . .
// Add the Text box to display the attachment's display text
htmlCode.Append("<input type=\"text\" name=\"MAPH_AttName_" + sPhName
+ "_" + i.ToString() + "\" value='" + attName[i] + "'>");
. . . code continues . . .
}
The last item to fill in is the URL of the attachment. These are displayed in the textboxes that begin with MAPH_AttLink. The contents of the attUrl[] array will be used to fill in their values.
protected override void LoadPlaceholderContentForAuthoring(
PlaceholderControlEventArgs e)
{
. . . code continues . . .
// Add the read-only Text box to display the attachment's URL
htmlCode.Append("<input readonly type=\"text\" name=\"MAPH_AttLink_"
+ sPhName + "_" + i.ToString()+"\" value='"+attUrl[i]+"'>");
. . . code continues . . .
}
7. Displaying the Attachments
In presentation view,
we’ll display the attachments in HTML. Let’s use the same trick as we
did earlier for the authoring control and use a Literal control to store
the generated HTML code.
private Literal basePresentationContainer;
protected override void CreatePresentationChildControls(
BaseModeContainer presentationContainer)
{
basePresentationContainer = new Literal();
presentationContainer.Controls.Add(basePresentationContainer);
}
With the underlying data in XML, you can format
the layout of the attachments any way you like. For example, you could
display them vertically, horizontally, or even within a table. The LoadPlaceholderContentForPresentation()
method below displays the attachments side by side. Basically, for each
attachment in the list, an anchor tag is created. At the end of the
routine, we get a series of anchor tags, with single spaces between
them.
protected override void LoadPlaceholderContentForPresentation(
PlaceholderControlEventArgs e)
{
// The output HTML
StringBuilder html = new StringBuilder();
// Retrieve previously saved content
string xml = ((XmlPlaceholder)this.BoundPlaceholder).XmlAsString;
// Load the XML into an XmlDocument
XmlDocument xd = new XmlDocument();
xd.LoadXml(xml);
// Display the list of attachments
foreach(XmlNode xn in xd.DocumentElement.ChildNodes)
{
// Get the Display Text
string displayText = xn.SelectSingleNode("DisplayText").InnerText;
// Get the URL
string url = xn.SelectSingleNode("Url").InnerText;
// Get the icon
string icon = xn.SelectSingleNode("Icon").InnerText;
// Create the opening anchor tag
html.Append("<a href=\"" + url + "\">");
if (m_displayAsIcon)
{
// Display attachments as icons
html.Append(icon);
}
else
{
// Display attachments as text
html.Append(displayText);
}
// Create the closing anchor tag
html.Append("</a>");
// Add a space
html.Append(" ");
}
basePresentationContainer.Text = html.ToString();
}
Here,
we used the XML DOM to format the content. You could also consider
using an Extensible Stylesheet (XSL) to perform the transformation.
Using XSL is probably a more flexible solution, as it makes changing the
format a lot easier if you need to do so.
The Multiple Attachment placeholder control is complete. Save and compile the solution.
8. Using the MultipleAttachmentPlaceholderControl
Follow the steps outlined in the Adding the Placeholder Control to a Template File section to add the MultipleAttachmentPlaceholderControl to a template file of your choice. Set the DisplayAsIcon property to true or false depending on whether you would like to display the attachments as icons or text. Add an XmlPlaceholderDefinition to the template. Set the PlaceholderToBind property of the control to the XmlPlaceholderDefinition that you have just created, and you are ready to roll!