A short while ago I got this idea to build a message board for the site.
Now, I know that there are butt-TONS of message board apps available on
the web for me to integrate with my app; but I wanted to build something
on my own, and use the opportunity to learn something new. I recently
converted the site to ASP.NET 3.5 from 2.0 (and upgraded my IDE to Visual
Studio 2008 from VS2K5). I know that 3.5 offers several refinements to
explore.
Among these refinements is the next step in the evolution of the DataGrid.
- its cousins, the GridView and the DetailsView. At present, I chose to
implement these controls in my message board, to give users a row-by-row
summary of posts on top (in a GridView). When a particular post is selected,
the details of that post - it's contents - are displayed below (in the
DetailsView or another GridView).
Because I failed to find a suitable explanation of how these two controls
should be wired to work together, I thought I'd offer the deets on how I
got them to work. Note that at this point, the message board is still very
much a naked prototype. I'm going to introduce you to some background on
how the code is shaping up overall, in the hope that it'll give you a better
grip on what's happening with the two controls -- just keep in mind this
may not be how things will actually be implemented (if at all).
1. SCHEMA
Like everything else currently running in this application, the data is
stored as XML. Currently, my schema looks like this:
0
I decided on this design so that forums and topics will mirror the organization
of the web application itself, but posts can take on whatever form a visitor may
fancy. My hope is the reply-associated nodes will power code that will handle
proper indentation of the display of replies.
2. MESSAGEBOARD.vb
I wanted to try to handle as much as possible in a class under App_Code, with a
messageboard class responsible for reading the XML into a dataset. Information to
help shape those datasets is reported to the class via the Web User Control which
hosts the actual controls.
3. MESSAGEBOARD.ASCX
The Message and Details GridViews live here. Here's how the Messages GridView
currently looks:
We'll get to the Details grid shortly.
The User Control currently has five public properties
which mirror the schema: Forum, Topic, Subject, Poster, and PostDate. These
"getters and setters" are set by any Web Form (.aspx) which hosts the UC.
Public Property Forum() As String
'e.g., "Interests"
Get
Return strForum
End Get
Set(ByVal value As String)
strForum = value
End Set
End Property
The hosting Web Form sets the Forum and Topic properties in the Web Form's Load
event handler.
By way of overview, here's how things flow:
(1) The Web Form (books.aspx) is initialized.
(2) The messageboard User Control (messageboard.ascx) is initialized.
(3) The Web Form loads, setting the Forum and Topic properties on the User Control.
'set values for message board control
With grdMessages
.Forum = "interests"
.Topic = "books"
End With
(4) The User Control loads, calling the getPosts() method of the class, passing in
the values from each of its properties.
Sub Control_GetData()
'populate the main messages grid
With grdMessages
'point to the messageboard class, pass in the properties
.DataSource = messageboard.getPosts(Me.Forum, Me.Topic, . . .
'bind the datasource to the grid
.DataBind()
End With
By the way, it's important to call Control_GetData from the User Control's
Load event handler and only upon initial load. A better way to say this is
DO NOT CALL Control_GetData() AFTER THE FORM HAS POSTED BACK TO ITSELF:
Sub Control_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
If Not IsPostBack Then
' Load this data only once.
Call Control_GetData()
End If 'Not IsPostBack
End Sub
I suspect if you forget to do this, you'll keep overwriting your event handling
later.
(5) The class' getPosts() function loads the dataset, filters it using the values
from the properties, and passes a dataview back to the User Control
Public Shared Function getPosts(Optional ByVal strForum As String = "", Optional ByVal strTopic As String = "", Optional ByVal strFilter As String = "", Optional ByVal strSort As String = "") As DataView
'feed grid.
'called from messageboard.ascx
dsDataSet = LoadDataSet()
If Not dsDataSet Is Nothing AndAlso dsDataSet.Tables.Count > 0 Then
'create a dataview from the dataset
Dim dvDataView As New DataView(dsDataSet.Tables(0))
With dvDataView
'filter
.RowFilter = "forum = '" & strForum & "' AND topic = '" & strTopic & "' "
If strFilter.Length > 0 Then
.RowFilter = "AND " & strFilter
End If
'sort on date (most recent at top)
.Sort = strSort
End With
Return dvDataView
(6) The User Control consumes the dataview as the DataSource for the messages
GridView control, and binds it to the grid.
(7) The User Control at this point sets the .Visible property of the details grid
to FALSE.
4. MAGIC HAPPENS
The real mojo happens when a user selects one of the rows of the Messages GridView.
Note from the code above that the GridView's AutoGenerateSelectButton property is
set to "true." This creates a grid that resembles this:
|-------------------------------------------------------------------------------|
| | Subject | Date | Posted By |
|-------------------------------------------------------------------------------|
| Select | The Santaland Diaries | 11/19/2009 1:00:00 PM | The Joy Luck Club |
|-------------------------------------------------------------------------------|
| Select | Re: The Santaland Dia | 11/19/2009 1:00:01 PM | halfgk |
|-------------------------------------------------------------------------------|
| Select | Re: Re: The Santaland | 11/19/2009 1:00:02 PM | The Joy Luck Club |
|-------------------------------------------------------------------------------|
Okay. Hold that thought on the magic. Now seems a good time to show you what the
Details grid looks like:
<%#XPath("forum")%>
<%#XPath("topic")%>
<%#XPath("postSubject")%>
<%#XPath("poster")%>
<%#XPath("postDate")%>
<%#XPath("posterEmail")%>
<%#XPath("postBody")%>
It seems both of these controls are GridViews at the moment. Notice the grid uses
Xpath to populate the values in the grid. Also note -- and this is important --
that the Details grid is powered by an XmlDataSource control with the ID "dscPost".
The ID assigned to the XmlDataSource control is reflected in the "DataSourceID"
property of the GridView.
Awright. Back to the magic. Here's that grid again:
|-------------------------------------------------------------------------------|
| | Subject | Date | Posted By |
|-------------------------------------------------------------------------------|
| Select | The Santaland Diaries | 11/19/2009 1:00:00 PM | The Joy Luck Club |
|-------------------------------------------------------------------------------|
| Select | Re: The Santaland Dia | 11/19/2009 1:00:01 PM | halfgk |
|-------------------------------------------------------------------------------|
| Select | Re: Re: The Santaland | 11/19/2009 1:00:02 PM | The Joy Luck Club |
|-------------------------------------------------------------------------------|
Were the visitor to select one of those rows in the Messages grid (by clicking
"Select"), the event would be handled by the SelectedIndexChanged() event handler
on the User Control. This handler stores in the remaining properties (Subject(),
Poster(), and PostDate()) the values from the cells in the selected row:
Sub grdMessages_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs)
Handles grdMessages.SelectedIndexChanged
Dim sb As New StringBuilder
'set our properties with data from the selected message
'note: cells 1 and 2 are STILL in the grid - they're just not visible
Me.Subject = grdMessages.SelectedRow.Cells(3).Text
Me.Poster = grdMessages.SelectedRow.Cells(5).Text
Me.PostDate = grdMessages.SelectedRow.Cells(4).Text
...
The event handler also shapes the data the Details grid will display by using
XPath. Notice the StringBuilder object Dim'd at the top of the sub. Here's what
that's for:
'use XPath() to filter the data being fed to the details grid.
'Because the path is given in the XPath method HERE,
'DO NOT include it in arguments being evaluated in the GridView
With sb
.Append("/posts/post")
.Append("[forum='" & Me.Forum & "']")
.Append("[topic='" & Me.Topic & "']")
.Append("[postSubject='" & Me.Subject & "']")
.Append("[poster='" & Me.Poster & "']")
.Append("[postDate='" & Me.PostDate & "']")
dscPost.XPath = .ToString()
End With
...
I decided to use a StringBuilder object because I'm filtering on five conditions
here, and felt it'd be more efficient than simply concatenating a string. Note
that the sb is converted to a string and fed directly to the XPath method of the
XmlDataSource "dscPost".
From here, all that's left is to bind the data to the XmlDataSource, then to bind
the XmlDataSource to the Details grid, and set the grid's visibility to TRUE.
'bind the data to the XmlDataSource
dscPost.DataBind()
'bind the XmlDataSource to the details grid
grdDetails.DataBind()
'set the visibility to TRUE
grdDetails.Visible = "True"
End Sub
What the user sees is roughly this:
|-------------------------------------------------------------------------------|
| | Subject | Date | Posted By |
|-------------------------------------------------------------------------------|
| Select | The Santaland Diaries | 11/19/2009 1:00:00 PM | The Joy Luck Club |
|-------------------------------------------------------------------------------|
| Select | Re: The Santaland Dia | 11/19/2009 1:00:01 PM | halfgk | <-- (SELECTED)
|-------------------------------------------------------------------------------|
| Select | Re: Re: The Santaland | 11/19/2009 1:00:02 PM | The Joy Luck Club |
|-------------------------------------------------------------------------------|
|-------------------------------------------------------------------------------|
| Subject | Post |By | Date |
|-------------------------------------------------------------------------------|
| Re: The Santaland Diaries | I've started it! | halfgk | 11/19/2009 1:01:00 PM |
|-------------------------------------------------------------------------------|
That should be it for now! Contact me using the site's contact form if
you have questions. Feel free to use the code in your projects. A shout
out in your project would be thoughtful. Also, drop me a line and let me
know how you might have tweaked things to better suit your needs.
Finally, I wouldn't profess to be THE expert on matters represented in
my code -- so drop me a line if you have constructive suggestions, too.
I'd like to hear from you!
Best,
halfgk
copyright 2009 halfgk.com