Real - World ASP .NET—Building a Content Management System - StephenR. G. Fraser
.pdf
But first, you need to create the User Control.
HeadlineTeaser User Control Design
There is nothing special about HeadlineTeaser's design (see Listing 13-13). It is simply five labels, an image, and a hyperlink.
Listing 13-13: The HeadlineTeaser User Control Design Code
<P>
<STRONG>
<asp:Label id=lbHeadline runat="server"></asp:Label>
</STRONG>
<BR>
<asp:Label id=lbSource runat="server"></asp:Label>
<BR>
<EM>
<FONT size=2>
<asp:Label id=lbBy runat="server" >by</asp:Label>
</FONT>
<asp:Label id=lbByline runat="server" Font-Size="X-Small">
</asp:Label>
</EM>
<BR>
<asp:Label id=lbTeaser runat="server" ></asp:Label>
</P>
<P>
<asp:Image id=imgPlus runat="server"
Height="11px" Width="11px" ImageUrl="Images/plus.gif">
</asp:Image>
<asp:HyperLink id=hlReadMore runat="server">Read More</asp:HyperLink>
</P>
<HR align="center" width="60%" SIZE=1>
<br>
The HeadlineTeaser Codebehind
The Codebehind for HeadlineTeaser starts by defining two properties: ContentID and Version. If these properties are not set before accessing the User Control, a "No Stories" message is displayed instead of the expected header, source, author, teaser, and hyperlink.
When valid ContentID and Version properties are set, the HeadlineTeaser grabs the content out of the Content database table and displays it. It also creates a hyperlink to a Story Web form so that the user can read the entire story if she chooses. There's no rocket science here, so let's move on.
Listing 13-14: The HeadlineTeaser User Control Codebehind
private int m_contentid = 0; private int m_version = 0;
public int ContentID
{
get
{
return m_contentid;
}
set
{
m_contentid = value;
}
}
public int Version
{
get
{
return m_version;
}
set
{
m_version = value;
}
}
private void Page_Load(object sender, System.EventArgs e)
{
if (!IsPostBack)
{
AppEnv appEnv = new AppEnv(Context);
Content content = new Content(appEnv.GetConnection());
AccountProperty property = new AccountProperty(appEnv.GetConnection());
DataRow dr = content.GetContentForIDVer(m_contentid, m_version);
if (dr != null)
{
lbHeadline.Text = dr["Headline"].ToString();
lbSource.Text = dr["Source"].ToString();
lbByline.Text = property.GetValue(
Convert.ToInt32(dr["Byline"]), "UserName").Trim();
lbTeaser.Text = dr["Teaser"].ToString();
hlReadMore.NavigateUrl = "StoryPg.aspx?ID=" + m_contentid +
"&Ver=" + m_version;
}
else
{
lbHeadline.Text = "No Stories"; hlReadMore.Visible = false; lbBy.Visible = false; imgPlus.Visible = false;
}
}
}
The Default Content-Domain Web Form
Like all the default Web forms, the Content -Domain Web form (from now on just called the Content Web form) has a header, a NavBar, and a footer (see Figure 13-12). The code is identical to that of the home page, which you have already covered and will not cover again here.
Figure 13-12: The Content Web form
The Content Web Design
The only difference between the Content Web design and the Home Page Web design is found in the main body section of the Web form, which contains a label to hold the domain name and a two-column table. You should note that the two columns or TableCells have IDs, which you will use in the Codebehind.
Listing 13-15: The Content Page Design Code
<H1>
<asp:Label id=lbDomain runat="server" ForeColor="DarkSlateGray"></asp:Label>
</H1>
<P>
<asp:Table id=tblDomHeadlines runat="server" CellPadding="8" Width="100%">
<asp:TableRow>
<asp:TableCell Width="50%" ID="tcLeft"></asp:TableCell>
<asp:TableCell Width="50%" ID="tcRight"></asp:TableCell>
</asp:TableRow>
</asp:Table>
</P>
The Content Codebehind
Like most Codebehinds in ASP.NET, all the action happens in the Page_Load() method (see Listing 13-16). The first thing the method does is grab the domain that is sent along with the Request, select the Domain name using this value out of the Domain database, and then place the value on the lbDomain. Next, it selects out of the Zone database all zones for the domain. This is pretty standard stuff.
Listing 13-16: The Content Web Page Codebehind
protected System.Web.UI.WebControls.TableCell tcLeft;
protected System.Web.UI.WebControls.TableCell tcRight;
...
private void Page_Load(object sender, System.EventArgs e)
{
int curDomain = Convert.ToInt32(Request.QueryString["Domain"]);
if (curDomain == 0)
Page_Error("No domain specified");
MainNavBar.Domain = curDomain;
Domain domain = new Domain(appEnv.GetConnection());
DataTable dt = domain.GetDomainForID(curDomain);
lbDomain.Text = dt.Rows[0]["Title"].ToString();
Zone zone = new Zone(appEnv.GetConnection());
dt = zone.GetZonesForDomain(curDomain);
Distribution dist = new Distribution(appEnv.GetConnection());
int i;
HyperLink link;
for (i = 0; i < (int)Math.Ceiling((float)(dt.Rows.Count) / 2.0); i++)
{
link = new HyperLink();
link.Text = dt.Rows[i]["Title"].ToString();
link.NavigateUrl = "ZonePg.aspx?zone=" + dt.Rows[i]["ZoneID"]; link.Font.Size = new FontUnit(FontSize.Large); tcLeft.Controls.Add(link);
DataTable dtd = dist.GetOrdered(Convert.ToInt32(dt.Rows[i]["ZoneID"])); HeadlineTeaser hlt =
(HeadlineTeaser) LoadControl("HeadlineTeaser.ascx");
if (dtd.Rows.Count > 0)
{
hlt.ContentID = Convert.ToInt32(dtd.Rows[0]["ContentID"]); hlt.Version = Convert.ToInt32(dtd.Rows[0]["Version"]);
}
tcLeft.Controls.Add(hlt);
}
for (; i < dt.Rows.Count; i++)
{
link = new HyperLink();
link.Text = dt.Rows[i]["Title"].ToString();
link.NavigateUrl = "ZonePg.aspx?zone=" + dt.Rows[i]["ZoneID"]; link.Font.Size = new FontUnit(FontSize.Large); tcRight.Controls.Add(link);
DataTable dtd = dist.GetOrdered(Convert.ToInt32(dt.Rows[i]["ZoneID"])); HeadlineTeaser hlt =
(HeadlineTeaser) LoadControl("HeadlineTeaser.ascx");
if (dtd.Rows.Count > 0)
{
hlt.ContentID = Convert.ToInt32(dtd.Rows[0]["ContentID"]); hlt.Version = Convert.ToInt32(dtd.Rows[0]["Version"]);
}
tcRight.Controls.Add(hlt);
}
}
Now comes the fun stuff. The method counts the number of zones and divides it in half. The first half of the zones will be placed into the tcLeft TableCell and the remainder into the tcRight TableCell. Note that you have to manually add to the code for the declaration of the TableCells because the design tool does not generate them.
On the Content Web page, only the lead story of each zone is displayed, so what gets placed in each row is a hyperlink to the Zone Web page and a HeadlineTeaser to the lead story. To do this, you need to be able to create the HeadlineTeaser control dynamically.
Creating a User Control Dynamically
Creating a User Control dynamically, though easy, is far from obvious. First, you have to load a copy of the User Control for every instance you place on the Web page using the Page.LoadControl() method, as shown here:
HeadlineTeaser hlt = (HeadlineTeaser) LoadControl("HeadlineTeaser.ascx");
Next, you need to set all the properties of the User Control before it is used. I like to do this right after it is loaded, but you can choose to do it later in the Page_Load() method if you want. The following is how the Content Web page sets the properties:
hlt.ContentID = Convert.ToInt32(dtd.Rows[0]["ContentID"]);
hlt.Version = Convert.ToInt32(dtd.Rows[0]["Version"]);
Finally, you have to add the control to the Web page. In the Content Web page's case, it is added to the TableCell as follows:
tcRight.Controls.Add(hlt);
You might be wondering why you don't just create an instance using the following:
HeadlineTeaser htl = new HeadlineTeaser(); // ERROR do not do this!!!
The reason is that User Controls are abstract classes, which cannot be created independently. Because they can only be called from the page that contains them, they need to be loaded first through the Web page.
The Default Zone Web Form
The Zone Web form (see Figure 13-13) provides the users of CMS.NET with the capability to see a list of all the stories that fall within a single zone. Code-wise, this Web page is very similar to the Content Web page discussed previously.
Figure 13-13: The Zone Web form
The unique part of this Web page is that it shows that a User Control can be used statically and dynamically within the same page. Not that this should come as any big surprise. Listing 13-17 shows the ASP.NET design code for the main body of the Zone Web page.
Listing 13-17: The Zone Page Design Code
<%@ Register TagPrefix="cmsnet" TagName="HeadlineTeaser"
Src="HeadlineTeaser.ascx" %>
...
<TD width="80%">
<H1>
<asp:Label id=lbZone runat="server" ForeColor="DarkSlateGray">
</asp:Label>
</H1>
<cmsnet:HeadlineTeaser id=htLead runat="server" >
</cmsnet:HeadlineTeaser>
<P>
<asp:Table id=tblDomHeadlines runat="server" CellPadding="8" Width="100%">
<asp:TableRow>
<asp:TableCell Width="50%" ID="tcLeft"></asp:TableCell>
<asp:TableCell Width="50%" ID="tcRight"></asp:TableCell>
</asp:TableRow>
</asp:Table>
</P>
</TD>
You have probably noted that it is very similar to the Content Web page. In the design code, I added a statically declared HeadlineTeaser User Control because all zones will have at least one story or the message "No Stories," both of which
HeadlineTeaser can display.
The Codebehind has nothing new in it. As Listing 13-18 shows, the Page_Load() method gets the ZoneID from the Request and then gets all the content for that zone, which it displays using the HeadlineTeaser User Control. It is very similar to the Content Codebehind, except that it gets all the content from one zone instead of getting one piece of content from many zones.
Listing 13-18: The Zone Web Page Codebehind
protected System.Web.UI.WebControls.TableCell tcLeft;
protected System.Web.UI.WebControls.TableCell tcRight;
protected CMSNET.CDA.HeadlineTeaser htLead;
private void Page_Load(object sender, System.EventArgs e)
{
int curZone = Convert.ToInt32(Request.QueryString["Zone"]);
if (curZone == 0)
Page_Error("No zone specified");
Zone zone = new Zone(appEnv.GetConnection());
DataTable dt = zone.GetZone(curZone);
lbZone.Text = dt.Rows[0]["Title"].ToString();
Distribution dist = new Distribution(appEnv.GetConnection());
DataTable dtd = dist.GetOrdered(Convert.ToInt32(dt.Rows[0]["ZoneID"]));
if (dtd.Rows.Count > 0)
{
htLead.ContentID = Convert.ToInt32(dtd.Rows[0]["ContentID"]); htLead.Version = Convert.ToInt32(dtd.Rows[0]["Version"]);
int i;
for (i = 0; i < (int)Math.Ceiling((float)(dtd.Rows.Count-1)/2.0); i++)
{
HeadlineTeaser hlt =
(HeadlineTeaser) LoadControl("HeadlineTeaser.ascx"); hlt.ContentID = Convert.ToInt32(dtd.Rows[i+1]["ContentID"]);
hlt.Version = Convert.ToInt32(dtd.Rows[i+1]["Version"]); tcLeft.Controls.Add(hlt);
}
for (; i < dtd.Rows.Count-1; i++)
{
HeadlineTeaser hlt =
(HeadlineTeaser) LoadControl("HeadlineTeaser.ascx"); hlt.ContentID = Convert.ToInt32(dtd.Rows[i+1]["ContentID"]); hlt.Version = Convert.ToInt32(dtd.Rows[i+1]["Version"]); tcRight.Controls.Add(hlt);
}
}
}
One thing to note is that you have to manually add the declaration of both TableCells and the HeadlineTeaser classes.
The Default Story Web Form
The Story Web form (see Figure 13-14) is one of the most important Web pages, from the user's perspective, in all of CMS.NET because it is where the actual content is displayed. Believe it or not, it is also one of the easiest Web pages to create.
Figure 13-14: The Story Web form
As you can see from the Story design code (see Listing 13-19), the Web page is simply a set of labels of the database columns you want to display. You've done similar things since way back in Chapter 6.
Listing 13-19: The Story Page Design Code
<TD width="80%">
<FONT color=darkslategray>
<P>
<asp:label id=lbHeadline runat="server" Font-Size="Large">
</asp:label>
</P>
<P>
<STRONG>
<asp:Label id=lbSource runat="server" >
</asp:Label>
</STRONG>
</P>
</FONT>
<P>
<asp:label id=lbBy runat="server" Font-Size="Smaller">by </asp:label>
<asp:label id=lbByline runat="server" Font-Size="Smaller" ></asp:label>
<asp:label id=lbDashes runat="server" Font -Size="Smaller" >--</asp:label>
<asp:label id=lbDate runat="server" Font -Size="Smaller" ></asp:label>
</P>
<P>
<asp:label id=lbTeaser runat="server" ></asp:label>
</P>
<P>
<asp:label id=lbBody runat="server" ></asp:label>
</P>
<P>
<EM>
<FONT size=2>
<asp:label id=lbTagline runat="server" ></asp:label>
</FONT>
</EM>
</P>
</TD>
The Codebehind for the Story Web page (see Listing 13-20) simply gets the ContentID and Version. With that, it selects the story out of the Content database and then matches up each column with its appropriate label.
Listing 13-20: The Story Web Page Codebehind
private void Page_Load(object sender, System.EventArgs e)
{
int curId = Convert.ToInt32(Request.QueryString["ID"]);
int curVer = Convert.ToInt32(Request.QueryString["Ver"]);
