This is a three part series, if you haven’t already read part one, you may want to start there (An Example of Writing Your Own Blog Engine (mini-version)), or part two (An Example of Writing Your Own Blog Engine (mini-version) Part Two).
Presentation Layer
Ok, well this will be the last piece to the puzzle and we’re going to start with the main thread which will house the featured blog post itself as well as a mechanism for handling comments. We want to make it in such a way that we can easily control the number of blog posts that are displayed. For example, if we wanted to display one featured post (the most recent in this example), or allow a user to select a month and display all blog posts for that month. It seems to me that a repeater sounds like a nice, flexible, .NET control to handle this situation.
Here is the following HTML I used, you can of course change this to however fits
your needs.
HTML and ASP.NET Controls
<asp:Repeater ID="rptMainList" runat="server">
<ItemTemplate>
<!-- Blog date -->
<div><asp:Literal ID="litPublishDate" runat="server" Text='<%#Eval("PubDate").ToLongDateString()%>'></asp:Literal></div>
<hr />
<h1><asp:Literal ID="litBlogTitle" runat="server" Text='<%#Eval("Title")%>'></asp:Literal></h1>
<asp:Literal ID="litBlogPost" runat="server" Text='<%#Eval("Description")%>'></asp:Literal>
<asp:HiddenField ID="hdnBlogID" runat="server" Value='<%#Eval("BlogID")%>' />
<hr />
<!-- Blog: Add a comment -->
<table cellpadding="2" cellspacing="4" width="100%">
<tr>
<td style="width: 35%;">Enter your name:</td>
<td style="width: 65%;">Have a comment? Post it here.</td>
</tr>
<tr>
<td><input type="text" id="tbName" runat="server" style="width: 100%;" /></td>
<td rowspan="5" style="vertical-align: top;"><textarea id="tbComment" runat="server" cols="1" rows="1"></textarea></td>
</tr>
<tr>
<td>Enter your email:</td>
</tr>
<tr>
<td><input type="text" id="tbEmail" runat="server" style="width: 100%;" /></td>
</tr>
<tr>
<td>Enter your URL here:</td>
</tr>
<tr>
<td><input type="text" id="tbURL" runat="server" style="width: 100%;" /></td>
</tr>
<tr>
<td colspan="2" style="text-align: right;">
<asp:LinkButton ID="btnPostComment" runat="server" Text="add comment" CommandName="PostComment"></asp:LinkButton>
</td>
</tr>
</table>
<hr />
<!-- Blog: Previous comments -->
<asp:UpdatePanel ID="upnlBlogComments" runat="server">
<Triggers>
<asp:AsyncPostBackTrigger ControlID="btnPostComment" EventName="Click" />
<asp:AsyncPostBackTrigger ControlID="rptMainList" EventName="ItemCommand" />
</Triggers>
<ContentTemplate>
<asp:Repeater ID="rptBlogComments" runat="server" OnItemDataBound="rptBlogComments_ItemDatabound">
<HeaderTemplate>
<table cellpadding="2" cellspacing="4" width="100%" class="comments">
<tr>
<td colspan="2"><h3 style="padding-bottom: 0px; margin-bottom: 0px;"><span style="color: Black;"><asp:Label ID="lblNumberOfComments" runat="server"></asp:Label></span> Comments</h3></td>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td>
<asp:Label ID="lblDate" runat="server"></asp:Label><br />
<asp:Label ID="lblPoster" runat="server"></asp:Label><br />
<asp:HyperLink ID="lnkURL" runat="server"></asp:HyperLink>
</td>
<td style="padding-left: 25px;">
<asp:Label ID="lblComment" runat="server" Text=""></asp:Label>
</td>
</tr>
</ItemTemplate>
<AlternatingItemTemplate>
<tr>
<td colspan="2"><hr /></td>
</tr>
<tr>
<td>
<asp:Label ID="lblDate" runat="server"></asp:Label><br />
<asp:Label ID="lblPoster" runat="server"></asp:Label><br />
<asp:HyperLink ID="lnkURL" runat="server"></asp:HyperLink>
</td>
<td style="padding-left: 25px;">
<asp:Label ID="lblComment" runat="server" Text=""></asp:Label>
</td>
</tr>
<tr>
<td colspan="2"><hr /></td>
</tr>
</AlternatingItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
</ContentTemplate>
</asp:UpdatePanel>
</ItemTemplate>
</asp:Repeater>
VB Code for Handling the Main Post
Now, we also need a bit of code to handle working with the blog post itself, we’ll reference what we created in the business layer to do this. Notice the use of a helper function to handle repetive code, this is good practice to reduce redundant code making maintenance easier. Also, I prefer to standardize the way I title these types of functions, it will also help in ease of maintenance.
Private _blogbll As BlogBLL
Public Property BlogAdapter() As BlogBLL
Get
If _blogbll Is Nothing Then
_blogbll = New BlogBLL
End If
Return _blogbll
End Get
Set(ByVal value As BlogBLL)
_blogbll = value
End Set
End Property
''' <summary>
''' Get's most recent blog post.
''' </summary>
''' <remarks></remarks>
Private Sub GetBlogPost()
Dim blogPostDT As Blog.BlogPostsViewDataTable = BlogAdapter.GetMostRecentBlogPost()
Try
GetBlogPost_Helper(blogPostDT)
Catch ex As Exception
'If this fails, it means there are no blog posts in database at all
'Should maybe redirect to a plain / non-blog page
Exit Sub
End Try
End Sub
''' <summary>
''' Get's specific blog post.
''' </summary>
''' <param name="BlogID"></param>
''' <remarks></remarks>
Private Sub GetBlogPost(ByVal blogID As Integer)
Dim blogPostDT As Blog.BlogPostsViewDataTable = BlogAdapter.GetBlogPost(blogID)
GetBlogPost_Helper(blogPostDT)
End Sub
''' <summary>
''' Get's all blog posts for a given month/year (archived posts)
''' </summary>
''' <param name="month"></param>
''' <param name="year"></param>
''' <remarks></remarks>
Private Sub GetBlogPost(ByVal month As Integer, ByVal year As Integer)
Dim blogPostDT As Blog.BlogPostsViewDataTable = BlogAdapter.GetBlogPosts(month, year)
GetBlogPost_Helper(blogPostDT)
End Sub
Private Sub GetBlogPost_Helper(ByVal blogPostDR As Blog.BlogPostsViewDataTable)
rptMainList.DataSource = blogPostDR
rptMainList.DataBind()
End Sub
Protected Sub rptBlogPosts_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles rptBlogPosts.ItemDataBound
Select Case e.Item.ItemType
Case ListItemType.Item
rptBlogPosts_Helper(sender, e)
Case ListItemType.AlternatingItem
rptBlogPosts_Helper(sender, e)
End Select
End Sub
Private Sub rptBlogPosts_Helper(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs)
Dim lnkBlogPost As HyperLink = DirectCast(e.Item.FindControl("lnkBlogPost"), HyperLink)
Dim title As String = e.Item.DataItem("Title").ToString().Replace(" ", "_").Replace(".", "").Replace(",", "")
lnkBlogPost.Text = e.Item.DataItem("Title").ToString()
'lnkBlogPost.NavigateUrl = String.Format("default.aspx?blogid={0}", e.Item.DataItem("BlogID").ToString())
lnkBlogPost.NavigateUrl = String.Format("{0}-{1}", e.Item.DataItem("BlogID").ToString(), title)
End Sub
VB Code for Handling Blog Comments
The same needs to be done with the blog comments.
Private Sub rptBlogComments_Helper(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs)
Dim lblDate As Label = DirectCast(e.Item.FindControl("lblDate"), Label)
Dim lblPoster As Label = DirectCast(e.Item.FindControl("lblPoster"), Label)
Dim lnkURL As HyperLink = DirectCast(e.Item.FindControl("lnkURL"), HyperLink)
Dim lblComment As Label = DirectCast(e.Item.FindControl("lblComment"), Label)
Dim dateCreated As DateTime
Dim pubDate As String = "Unknown"
Try
dateCreated = e.Item.DataItem("PubDate")
pubDate = dateCreated.ToLongDateString()
Catch ex As Exception
End Try
lblDate.Text = pubDate
Dim poster As String = e.Item.DataItem("Username").ToString()
If String.IsNullOrEmpty(poster) = True Then
poster = "anonymous"
End If
lblPoster.Text = poster
lblComment.Text = e.Item.DataItem("Comment").ToString()
Dim url As String = e.Item.DataItem("URL").ToString()
lnkURL.Text = url
lnkURL.NavigateUrl = url
End Sub
Protected Sub rptBlogComments_ItemDatabound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs)
Select Case e.Item.ItemType
Case ListItemType.Header
Dim blogID As Integer = hdnTempBlogID.Value
Dim lblNumberOfComments As Label = DirectCast(e.Item.FindControl("lblNumberOfComments"), Label)
lblNumberOfComments.Text = BlogAdapter.GetNumberOfBlogComments(blogID).ToString()
Case ListItemType.Item
rptBlogComments_Helper(sender, e)
Case ListItemType.AlternatingItem
rptBlogComments_Helper(sender, e)
End Select
End Sub
Protected Sub rptMainList_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.RepeaterCommandEventArgs) Handles rptMainList.ItemCommand
Select Case e.CommandName
Case "PostComment"
Dim hdnBlogID As HiddenField = DirectCast(e.Item.FindControl("hdnBlogID"), HiddenField)
Dim tbComment As HtmlTextArea = DirectCast(e.Item.FindControl("tbComment"), HtmlTextArea)
Dim tbName As HtmlInputText = DirectCast(e.Item.FindControl("tbName"), HtmlInputText)
Dim tbEmail As HtmlInputText = DirectCast(e.Item.FindControl("tbEmail"), HtmlInputText)
Dim tbURL As HtmlInputText = DirectCast(e.Item.FindControl("tbURL"), HtmlInputText)
Dim blogID As Integer = hdnBlogID.Value
Dim comment As String = tbComment.Value
Dim username As String = tbName.Value
Dim email As String = tbEmail.Value
Dim url As String = tbURL.Value
Try
BlogAdapter.CreateBlogComment(blogID, comment, username, email, url)
Catch ex As Exception
'FIX add error handling here
End Try
'Retrieve blog comments
Dim rptBlogComments As Repeater = DirectCast(e.Item.FindControl("rptBlogComments"), Repeater)
rptBlogComments.DataSource = BlogAdapter.GetBlogComments(blogID)
rptBlogComments.DataBind()
End Select
End Sub
Protected Sub rptMainList_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles rptMainList.ItemDataBound
hdnTempBlogID.Value = e.Item.DataItem("BlogID")
'Retrieve blog comments
Dim rptBlogComments As Repeater = DirectCast(e.Item.FindControl("rptBlogComments"), Repeater)
rptBlogComments.DataSource = BlogAdapter.GetBlogComments(e.Item.DataItem("BlogID"))
rptBlogComments.DataBind()
End Sub
Finally, the Trigger
All the trigger does (in this case on page load) is handle the possible incoming data variations as well as kick off the whole process. As you can see below we have a few variations of data that can be passed in (and more in the real blog and yours as you add to it).
First, no additional data can be passed in and the blog engine will default to the most recent post only. Alternatively, you could pass in the month and year and retrieve a list of blog posts for that month. And finally, you can pass in a blog id and retrieve a specific blog post to read.
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Page.IsPostBack = False Then
Dim month As Integer = 0
Try
month = Integer.Parse(Request.QueryString("month"))
Catch ex As Exception
End Try
Dim year As Integer = 0
Try
year = Integer.Parse(Request.QueryString("year"))
Catch ex As Exception
End Try
Dim blogID As Integer = 0
Try
blogID = Integer.Parse(Request.QueryString("blogid"))
Catch ex As Exception
End Try
If month > 0 And year > 0 Then
GetBlogPost(month, year)
ElseIf blogID > 0 Then 'Populate requested blog post
GetBlogPost(blogID)
Else 'Populate most recent blog post
GetBlogPost()
End If
End If
End Sub
And that’s it! In just three short installments we put together a basis for your very own blog engine which I encourage all of you to build upon, rewrite, improve, and generally play around with. Please leave comments on new ideas for the blog engine, updates to how you’ve enhanced it, or any bugs found within this base (there’s always a few).


What a truly fun writing..