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