In this article I will present how to build websites with
dynamic content. Content is saved in one XML file so you will not think about
database hosting problems.
Our website has sections, each section has content. We will
be able to change sections names, add new sections, and edit content of every
section. The project is separated into different business Backend ("the
responsible of administrating sections and their contents") and FrontEnd (the
final website). I have combined both in the same project but you can organize
files and projects as you like relevant to your business.
Figure 1
Figure 1 displays how the site looks at the end; the header
has sections that added from the backend. The header is DataList control. Of
course you can use whatever design you want, but it is bounded to our XML file.
Let us discuss XML file structure first; XML file has schema and data in the same
time. Schema is one table in sections and every section has an id, name, and
content. Id is auto increment to be used as the primary key afterwards in
retrieving data, see listing 1.
Listing 1
<xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xs:element name="NewDataSet" msdata:IsDataSet="true"
msdata:MainDataTable="Sections" msdata:UseCurrentLocale="true">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="Sections">
<xs:complexType>
<xs:sequence>
<xs:element name="Id" msdata:AutoIncrement="true" type="xs:int"
minOccurs="0" />
<xs:element name="Name" type="xs:string" minOccurs="0" />
<xs:element name="Content" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>
Now we will review the backend structure. I have one master
page to backend and another one for frontend. Backend master page is in Figure
2.
Figure 2
In the top menu we have three links: sections, content and
website (front-end). Sections page is used to add sections, remove old ones and
also update old section name.
Figure 3
In the page load the code checks if there is data in the XML
file or not, and if it is then it will be loaded in the listbox.
Listing 2
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
strFilePath = MapPath(@"~/xmls/Sections.xml");
Session["dtSections"] = ManageFile.LoadSections(strFilePath);
Session["strFilePath"] = strFilePath;
BindList((DataTable)Session["dtSections"]);
}
else if (Session["strFilePath"] != null)
strFilePath = (string)Session["strFilePath"];
}
First, we have folders inside the project for XML file named
xmls. I save the path in session and load sections names and content in the session
too. We use session to reduce reading from XML all the time.
In the save button we do two transactions. One in adding and
the other one is updating, see listing 3. Adding is very easy and it just adds
in datatable new row and writes in XML. But updating uses session to hold the
id of the selected section. Actually, you can use viewstate or query string; it
is up to you how to store data.
Listing 3
protected void btnAdd_Click(object sender, EventArgs e)
{
DataTable dtSections;
if (Session["Id"] == null)
dtSections = dataAccess.AddSection(txtSection.Text, (DataTable)Session[
"dtSections"], strFilePath);
else
dtSections = dataAccess.UpdateSection(txtSection.Text, (DataTable)Session[
"dtSections"], strFilePath, Session["Id"].ToString());
Session["dtSections"] = dtSections;
BindList(dtSections);
txtSection.Text = "";
}
Removing a section is not a big deal too, as in listing 4 we
will see that I just check if ListBox has the selected value or not and then
select the row from the datatable saved in session removing and finally saving
it on the XML file.
Listing 4
private void RemoveFromList()
{
if (listbSections.SelectedItem != null)
{
DataTable dtSections = (DataTable)Session["dtSections"];
DataRow[]drSections = dtSections.Select(" Id = " +
listbSections.SelectedValue);
if (drSections != null)
dtSections.Rows.Remove(drSections[0]);
Response.Write(
"<script>alert('Section has been removed successfully')</script>");
Session["dtSections"] = dtSections;
ManageFile.SaveSections(dtSections, strFilePath);
BindList(dtSections);
}
}
After adding sections then we are going to fill the content;
in the content page you will see a big textbox that the user can type whatever
he likes in HTML or normal text and it will presented in frontend as he saved
it. The user will have to select the section to begin adding or editing section
content and press save.
Figure 4
The Save button does the same as sections page save, but
this time it saves content.
Listing 5
private void SaveContent()
{
dataAccess.SaveContent(drpSections.SelectedValue, txtContent.Text,
(DataTable)Session["dtSections"], (string)Session["strFilePath"]);
Response.Write("<script>alert('Content saved')</script>");
}
Note that I use DataAccess class to deal with the XML file. Of
course when the section name is selected, I load content from session to
prepare for editing.
After we add sections and fill them with content, we are
going to see how we present them in the website.
The master page for the website has DataList as the menu
bounded to XML file, see figure 5. You will notice that DataList is horizontal,
and the table in itemtemplate holds the link to sections, see listing 6.
Figure 5
Listing 6
<asp:DataList ID="dlSections" Width=600px runat=server RepeatDirection=Horizontal
BackColor=Black ForeColor=white Font-Size=12px Font-Family=verdana>
<ItemTemplate>
<table>
<tr>
<td align=center>
<a href="FrontHome.aspx?PageId=<%# Eval("Id")%>"><%#Eval("Name") %></a>
</td>
</tr>
</table>
</ItemTemplate>
<SeparatorTemplate>
|
</SeparatorTemplate>
</asp:DataList>
During the load of the master page I bound the DataList to XML
sections names.
Listing 7
private void LoadSections()
{
if (Session["dtSections"] == null)
{
Session["dtSections"] = ManageFile.LoadSections(MapPath(@
"~/xmls/Sections.xml"));
}
dlSections.DataSource = (DataTable)Session["dtSections"];
dlSections.DataBind();
}
Then you will have the menu with sections and when you click
on any link, content is loaded relevant to the Id sent in query string.
Listing 8
private void LoadContent()
{
if (Request["PageId"] != null)
{
DataAccess dataAccess = new DataAccess();
if (Session["dtSections"] == null)
{
string strFilePath = MapPath(@"~/xmls/Sections.xml");
Session["dtSections"] = ManageFile.LoadSections(strFilePath);
}
LtrlContent.Text = dataAccess.GetSectionContent(Request["PageId"],
(DataTable)Session["dtSections"]);
}
}
After I finished the project I remembered that I do not have
a login page for backend so I added one. It is very simple; you can review two
lines of code to check username and password "admin." You also can
use whatever authentication you want.
Now that you have finished the simplest dynamic website
building, you can implement it all in 30 minutes. Notice that you can use other
controls for menus and labels in spite of literal control to the present
content. It is very flexible, but never forget set ValidateRequest="false"
in the content page directive to allow submitting HTML in XML file.