<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:copyright="http://blogs.law.harvard.edu/tech/rss" xmlns:image="http://purl.org/rss/1.0/modules/image/">
    <channel>
        <title>ASP.NET</title>
        <link>http://weblogs.sqlteam.com/jeffs/category/239.aspx</link>
        <description>ASP.NET 1.1 and 2.0 are covered here.  A great improvement from ASP, and a natural fit with SQL Server of course. </description>
        <language>en-US</language>
        <copyright>Jeff Smith</copyright>
        <managingEditor>smith_jeffreyt@yahoo.com</managingEditor>
        <generator>Subtext Version 1.9.4.0</generator>
        <item>
            <title>Minimize a DropDownList's ViewState</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2008/04/17/minimize-dropdownlist-viewstate.aspx</link>
            <description>Let's say you have a very large DropDownList with lots of values and text.  We need to maintain ViewState in this DropDownList so that we can retrieve the selected value on a post back.   Of course, this means that now the ViewState contains the data for every single value in the list, both values and text included.   Even though the page itself may be fairly simple and lightweight, the result of having this simple DropDownList on the page is that the page size is quite large and the amount of data passed back and forth on a postback is very large as well.&lt;br /&gt;
&lt;br /&gt;
If you have an efficient database, and/or if you are caching the data anyway, you might not mind re-loading the list items each time the page posts back to eliminate the need for the page itself to hold all of this ViewState data.  &lt;br /&gt;
&lt;br /&gt;
However, if you turn off ViewState on the DropDownList, you will notice that it now does not remember the selected value on post backs.  The solution is very simple -- just manually set the DropDownList after re-loading it to the value from the HTML form post.&lt;br /&gt;
&lt;br /&gt;
To do this, first we disable ViewState on the DropDownList (EnableViewState="False").  Then, instead of a typical PageLoad() method like this:
&lt;div style="background: white none repeat scroll 0% 50%; font-family: Verdana; font-size: 8pt; color: black; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;
&lt;p style="margin: 0px;"&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;protected&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; Page_Load(&lt;span style="color: blue;"&gt;object&lt;/span&gt; sender, &lt;span style="color: rgb(43, 145, 175);"&gt;EventArgs&lt;/span&gt; e)&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        {&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            &lt;span style="color: blue;"&gt;if&lt;/span&gt; (!IsPostBack)&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            {&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;               &lt;span style="font-weight: bold;"&gt; loadList();   &lt;/span&gt;            &lt;br /&gt;
&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;                &lt;span style="color: green;"&gt;// other stuff here&lt;/span&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            }&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        }&lt;/p&gt;
&lt;/div&gt;
        &lt;br /&gt;
we would load the list every time the page posts back:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background: white none repeat scroll 0% 50%; font-family: Verdana; font-size: 8pt; color: black; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;protected&lt;/span&gt; &lt;span style="color: blue;"&gt;void&lt;/span&gt; Page_Load(&lt;span style="color: blue;"&gt;object&lt;/span&gt; sender, &lt;span style="color: rgb(43, 145, 175);"&gt;EventArgs&lt;/span&gt; e)&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        {&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            &lt;span style="font-weight: bold;"&gt;loadList();    &lt;/span&gt;  &lt;/p&gt;
&lt;p style="margin: 0px;"&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            &lt;span style="color: blue;"&gt;if&lt;/span&gt; (!IsPostBack)&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            {&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;                 &lt;span style="color: green;"&gt;// other stuff here&lt;br /&gt;
&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            }&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        }&lt;br /&gt;
&lt;/p&gt;
&lt;/div&gt;
        &lt;br /&gt;
And, in that loadList() method, instead of simply doing this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background: white none repeat scroll 0% 50%; font-family: Verdana; font-size: 8pt; color: black; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;void&lt;/span&gt; loadList()&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        {&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            dlTest.DataSource = &lt;span style="color: green;"&gt;// get data from DB&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            dlTest.DataBind();&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        }&lt;br /&gt;
&lt;/p&gt;
&lt;/div&gt;
        &lt;br /&gt;
you also would check to see if it has a posted value:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="background: white none repeat scroll 0% 50%; font-family: Verdana; font-size: 8pt; color: black; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial;"&gt;
&lt;p style="margin: 0px;"&gt;        &lt;span style="color: blue;"&gt;void&lt;/span&gt; loadList()&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        {&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            dlTest.DataSource = &lt;span style="color: green;"&gt;// get data from DB&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;            dlTest.DataBind();&lt;/p&gt;
&lt;p style="margin: 0px;"&gt; &lt;/p&gt;
&lt;p style="margin: 0px;"&gt;          &lt;span style="font-weight: bold;"&gt;  &lt;/span&gt;&lt;span style="color: blue; font-weight: bold;"&gt;string&lt;/span&gt;&lt;span style="font-weight: bold;"&gt; r = Request[dlTest.UniqueID];&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin: 0px; font-weight: bold;"&gt;            &lt;span style="color: blue;"&gt;if&lt;/span&gt; (r != &lt;span style="color: blue;"&gt;null&lt;/span&gt;)&lt;/p&gt;
&lt;p style="margin: 0px; font-weight: bold;"&gt;                dlTest.SelectedValue = r;&lt;/p&gt;
&lt;p style="margin: 0px;"&gt;        }&lt;/p&gt;
&lt;/div&gt;
&lt;br /&gt;
That loads the DropDownList each time and ensures that the value set is what was posted back.  Thus, the control will now "remember" the selected value on each postback, but without requiring any data stored in the ViewState at all.  &lt;br /&gt;
&lt;br /&gt;
It would also be easy to implement this logic in a control that inherits from DropDownList; say, a LightDropDownList.  Techniques like this will of course work on other controls as well, such as a ListBox or a CheckedList.&lt;br /&gt;
&lt;br /&gt;
With fast database connections and performance, as well as data caching, sometimes persisting data on the page is not the optimal way to go.  Like always, there are different ways to skin a cat and sometimes simple little tweaks like this can have huge benefits on performance.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;Update:  &lt;/span&gt;See the comments for other options are that even easier/better.  Also in the comments, Richard has provided a link to this &lt;a target="_blank" href="http://weblogs.asp.net/infinitiesloop/archive/2006/08/03/Truly-Understanding-Viewstate.aspx"&gt;excellent post on ViewState&lt;/a&gt; that I highly recommend checking out.  It's long, but very informative and quite entertaining as well.   Thanks for the great feedback.&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60569.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2008/04/17/minimize-dropdownlist-viewstate.aspx</guid>
            <pubDate>Thu, 17 Apr 2008 16:21:16 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60569.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2008/04/17/minimize-dropdownlist-viewstate.aspx#feedback</comments>
            <slash:comments>6</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60569.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60569.aspx</trackback:ping>
        </item>
        <item>
            <title>Splitting a single DataTable into Parent/Child DataTables for Hierarchical Processing (e.g., nested ASP.NET Repeater controls)</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2007/11/02/parent-child-datatable-nested-repeaters.aspx</link>
            <description>In ASP.NET, we often would like to output "grouped" data on our web pages, like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;Customer Product     Sales&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;-------- ----------  -----&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;ABC Foundation&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         Product 1   $200&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         Product 2   $437&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         Product 3   $523&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;The XLZ Company&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         Product 1   $240&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         Product 2   $892&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;         Product 3   $395&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
   &lt;br /&gt;
The easiest way to do this is with nested &lt;a href="http://msdn2.microsoft.com/en-us/library/system.web.ui.webcontrols.repeater(VS.80).aspx" target="_blank"&gt;Repeater&lt;/a&gt; controls; one for the outer group (Customers, this case), and within that Repeater's ItemTemplate we'd have another Repeater control for the details (Products).  &lt;br /&gt;
&lt;br /&gt;
To use nested repeaters, you would return two separate result sets from SQL:  &lt;br /&gt;
&lt;ol&gt;
    &lt;li&gt;A "Customers" result set, listing one row per Customer to display on the report&lt;/li&gt;
    &lt;li&gt;A "Products" result set, listing one row per Product to display.&lt;/li&gt;
&lt;/ol&gt;
You would put both of those results into a single DataSet as DataTables, and then create a relation between the two (on the CustomerID column).   Now, in our DataSet, we have a parent-child relation and each row in the parent Customers DataTable will have related child rows in the Products DataTable.&lt;br /&gt;
&lt;br /&gt;
Then, in ASP.NET, we create nested repeaters:  the outer repeater is bound to the Customers DataTable, and the inner repeater gets bound for each Customer row to the child rows in the Products DataTable.    There are lots of good examples detailing this technique &lt;a target="_blank" href="http://www.gridviewguy.com/ArticleDetails.aspx?articleID=185"&gt;here&lt;/a&gt; and &lt;a target="_blank" href="http://msdn2.microsoft.com/en-us/library/aa478959.aspx"&gt;here&lt;/a&gt;, so I won't get into specifics on that in this space.&lt;br /&gt;
&lt;br /&gt;
However -- what if you already have a stored procedure that returns this data in a single result set? The nested Repeater controls won't do you much good, since we need two separate (but related) DataTables.  Or, what if it is inefficient to return these results separately in SQL?  For example, suppose we want to output only the products for customers that have a total sales of greater than $200 for a specified time period.  We'd need to process the sales transactions table &lt;span style="font-style: italic;"&gt;twice&lt;/span&gt; in SQL to generate the list of Customers separate from the list of Products.  Thus, our SQL becomes twice as inefficient as it needs to be to use this technique.&lt;br /&gt;
&lt;br /&gt;
To accommodate those possibilities, I recently wrote a simple function in C# that takes a single DataTable and creates a related parent DataTable in the same DataSet based on specified parameters.  This is useful if you only have a single result set from SQL (or any other data source) and you'd like to break that data up into two separate parent-child DataTables in order to use nested repeaters or any other type of parent-child processing on the data.&lt;br /&gt;
&lt;br /&gt;
As an example, if you start with a single DataTable like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;Products:&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;CustomerID  CustomerName    Product     Sales&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------  --------------- ----------  -----&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           ABC Foundation  Product 1   $200&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           ABC Foundation  Product 2   $437&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           ABC Foundation  Product 3   $523&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           The XLZ Company Product 1   $240&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           The XLZ Company Product 2   $892&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           The XLZ Company Product 3   $395&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
We can pass that DataTable to the CreateParentTable() function and you would end up with two related DataTables in the DataSet, like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;Customers:&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;CustomerID  CustomerName    &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------  --------------- &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           ABC Foundation  &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           The XLZ Company&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;Products:&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;CustomerID  Product     Sales&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;----------  ----------  -----&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           Product 1   $200&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           Product 2   $437&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;1           Product 3   $523&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           Product 1   $240&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           Product 2   $892&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;2           Product 3   $395&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Then we can simply bind the outer Repeater to the Customers DataTable, and then bind the inner Repeater to a call to each DataRow's GetChildRows() method to get the related products.  All from a single SQL result set.&lt;br /&gt;
&lt;br /&gt;
Also, notice that the function removes the CustomerName column from the Products DataTable, but leaves the CustomerID column for the relation.  &lt;br /&gt;
&lt;br /&gt;
Here's the function:&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(51, 153, 102);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;///   Creates a parent DataTable within the DataSet using distinct rows from&lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(51, 153, 102);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;///   an existing "source" DataTable, based on the column(s) specified.&lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(51, 153, 102);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;///   The source table then becomes the "child" table of the newly created &lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(51, 153, 102);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;///   parent. A DataRelation is also created between the parent table and the &lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(51, 153, 102);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;///   child table, using the column(s) specified.&lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(51, 153, 102);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(51, 153, 102);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;/// &amp;lt;param name="sourceTable"&amp;gt;&lt;br /&gt;
///    The source DataTable, which must be within a DataSet.   &lt;br /&gt;
///    This source table will become the "child" table.&lt;br /&gt;
/// &amp;lt;/param&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(51, 153, 102);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;/// &amp;lt;param name="parentTableName"&amp;gt;&lt;br /&gt;
///    This name will be assigned to the parent table once it is created.&lt;br /&gt;
/// &amp;lt;/param&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(51, 153, 102);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;/// &amp;lt;param name="relationColumns"&amp;gt;&lt;br /&gt;
///    Specify the columns used to relate the parent table to the child table.&lt;br /&gt;
/// &amp;lt;/param&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(51, 153, 102);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;/// &amp;lt;param name="additionalColumns"&amp;gt;&lt;br /&gt;
///    Any additional column(s) in the source table that will be extracted to the &lt;br /&gt;
///    parent table.  These columns will be removed from the source table.&lt;br /&gt;
/// &amp;lt;/param&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(51, 153, 102);" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;/// &amp;lt;param name="relationName"&amp;gt;&lt;br /&gt;
///    The name of the relation that will be created between the parent and &lt;br /&gt;
///    the child table.&lt;br /&gt;
/// &amp;lt;/param&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New; color: rgb(51, 153, 102);" /&gt;
&lt;span style="font-family: Courier New;"&gt;&lt;span style="color: rgb(51, 153, 102);"&gt;/// &lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;span style="font-family: Courier New;"&gt;public static void AddParentTable(DataTable sourceTable, string parentTableName, &lt;br /&gt;
     string[] relationColumns, string[] additionalColumns, string relationName)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;{&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    DataSet dataSet = sourceTable.DataSet;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    if (dataSet == null)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        throw new Exception("The source DataTable must be contained in a DataSet");&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    }&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;    // generate the set of columns to use to create the Parent table:&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    string[] cols = new string[relationColumns.Length+additionalColumns.Length];&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    relationColumns.CopyTo(cols,0);&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    additionalColumns.CopyTo(cols,relationColumns.Length);&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;    // create the parent table, copying unique rows from the Child table:&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    DataTable parent = sourceTable.DefaultView.ToTable(parentTableName, true, cols);&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;    // add the parent table to the DataSet:&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    dataSet.Tables.Add(parent);&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;    // remove the additional columns from the child table that were &lt;br /&gt;
    // copied to the parent table:&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    foreach (string s in additionalColumns)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        sourceTable.Columns.Remove(s);&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;    // create the relation between the new parent table and the child table:&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    DataColumn[] parentColumns = new DataColumn[relationColumns.Length];&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    DataColumn[] childColumns = new DataColumn[relationColumns.Length];&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    for (int i=0; i&amp;lt; relationColumns.Length;i++)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    {&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        parentColumns[i] = parent.Columns[relationColumns[i]];&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        childColumns[i] = sourceTable.Columns[relationColumns[i]];&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    }&lt;br /&gt;
&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New; color: rgb(51, 153, 102);"&gt;    // And, finally, add the relation to the parent table:&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    parent.ChildRelations.Add(relationName, parentColumns, childColumns);&lt;/span&gt;&lt;span style="font-family: Courier New;"&gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;}&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
The parameters are all explained in the XML documentation.  Feel free to make modifications or overloads of this function.  Some good overloads might be one that accepts a single relation column name, or that accepts a parameter that determines whether or not columns are removed from the child table.&lt;br /&gt;
    &lt;br /&gt;
Using our example, we'd call it like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;SqlConnection cn = new SqlConnection(&lt;span style="font-style: italic;"&gt;&amp;lt;... your connection string ...&amp;gt;&lt;/span&gt;)&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;SqlCommand cm = new SqlCommand(&lt;span style="font-style: italic;"&gt;&amp;lt;... the name of the stored proc ...&amp;gt;&lt;/span&gt;, cn);&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;cm.CommandType= CommandType.StoredProcedure;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;cn.Open();&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;SqlDataAdapter da = new SqlDataAdapter(cm);&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;DataSet ds = new DataSet();&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;da.Fill(ds, "Products");&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;CreateParentTable(ds.Tables["Products"], "Customers", new string[] {"CustomerID"}, &lt;br /&gt;
    new string[] {"CustomerName"}, "CustomerProducts")&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
After that is executed, the DataSet now contains an additional "Customers" table, and a relation called "CustomerProducts" to the "Products" table.   The relation is on "CustomerID", and the "Customers" table also includes the "CustomerName" column.  &lt;br /&gt;
&lt;br /&gt;
Thus, if we had nested repeaters in our ASP.NET code like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt; &amp;lt;table&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    &amp;lt;tr&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        &amp;lt;th&amp;gt;Customer&amp;lt;/th&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;       &amp;lt;th&amp;gt;Product&amp;lt;/th&amp;gt; &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;       &amp;lt;th&amp;gt;Sales&amp;lt;/th&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    &amp;lt;/tr&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;&amp;lt;asp:Repeater runat="server" ID="rpt1"&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    &amp;lt;ItemTemplate&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;       &amp;lt;tr&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;            &amp;lt;td colspan="3"&amp;gt;&amp;lt;%# ((System.Data.DataRowView) Container.DataItem)["CustomerName"] %&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;       &amp;lt;/tr&amp;gt; &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;       &amp;lt;asp:Repeater runat="server" ID="rpt2"  &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;          DataSource='&amp;lt;%# ((System.Data.DataRowView) Container.DataItem).Row.GetChildRows("CustomerProducts") %&amp;gt;'&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;            &amp;lt;ItemTemplate&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;            &amp;lt;tr&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                &amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                &amp;lt;td&amp;gt; &amp;lt;%# ((System.Data.DataRow) Container.DataItem)["Product"] %&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;                &amp;lt;td&amp;gt; &amp;lt;%# ((System.Data.DataRow) Container.DataItem)["Sales"] %&amp;gt;&amp;lt;/td&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;            &amp;lt;/tr&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;        &amp;lt;/ItemTemplate&amp;gt; &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;       &amp;lt;/asp:Repeater&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    &amp;lt;/ItemTemplate&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;&amp;lt;/asp:Repeater&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;&amp;lt;/table&amp;gt; &lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;span style="font-family: Courier New;"&gt;&lt;br /&gt;
&lt;/span&gt; we can simply assign rpt1's DataSource to our DataSet, set its DataMember as "Customers", call DataBind(), and we are good to go:&lt;span style="font-family: Courier New;"&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;rpt1.DataSource = ds;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;rpt1.DataMember = "Customers";&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;rpt1.DataBind();&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;/span&gt; A couple of more notes:
&lt;ol&gt;
    &lt;li&gt;The outer Repeater's DataItems are DataRowView objects, but the inner Repeater's DataItems are DataRows.  This is because we called the DataRowView's DataRow's GetChildRows() method, and not the DataRowView's CreateChildView() method.  I have found that this provides much better performance.  For small results, you may wish to call CreateChildView().&lt;br /&gt;
    &lt;br /&gt;
    &lt;/li&gt;
    &lt;li&gt;Instead of databinding in the HTML markup, you may wish to bind the inner Repeaters during the outer Repeater's DataBinding() event.  This keeps your HTML markup a little cleaner and shorter.&lt;br /&gt;
    &lt;br /&gt;
    &lt;/li&gt;
    &lt;li&gt;This function only works in .NET 2.0 or above, since it uses ToTable() method of the DataRowView class. &lt;/li&gt;
&lt;/ol&gt;
For more on nested repeaters, see:&lt;br /&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a target="_blank" href="http://support.microsoft.com/default.aspx/kb/326338"&gt;http://support.microsoft.com/default.aspx/kb/326338&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a target="_blank" href="http://msdn2.microsoft.com/en-us/library/aa478959.aspx"&gt;http://msdn2.microsoft.com/en-us/library/aa478959.aspx&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a target="_blank" href="http://www.gridviewguy.com/ArticleDetails.aspx?articleID=185"&gt;http://www.gridviewguy.com/ArticleDetails.aspx?articleID=185&lt;/a&gt;&lt;br /&gt;
    &lt;/li&gt;
&lt;/ul&gt;
Note that all of those example use two separate SQL result sets.  Using the function provided, we can do the same with a single result.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-style: italic;"&gt;see also:&lt;/span&gt;&lt;br /&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl02_HyperLink1" title="View Entry" href="../../../../jeffs/archive/2007/10/17/60375.aspx"&gt;Some HTML / ASP.NET Thoughts ...&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a title="View Entry" href="../../../../jeffs/archive/2007/10/09/csv-strings-database-or-presentation.aspx"&gt;Creating CSV strings in SQL: Should Concatenation and Formatting Be Done at the Database Layer?&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a title="View Entry" href="../../../../jeffs/archive/2007/03/28/60144.aspx"&gt;ASP.NET 1.1 -- Appsettings in Web.config&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl06_HyperLink1" title="View Entry" href="../../../../jeffs/archive/2006/07/11/10572.aspx"&gt;Minimizing the ViewState for an ASP.NET 1.1 DataGrid&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60391.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2007/11/02/parent-child-datatable-nested-repeaters.aspx</guid>
            <pubDate>Fri, 02 Nov 2007 16:16:21 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60391.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2007/11/02/parent-child-datatable-nested-repeaters.aspx#feedback</comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60391.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60391.aspx</trackback:ping>
        </item>
        <item>
            <title>Some HTML / ASP.NET Thoughts ...</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2007/10/17/60375.aspx</link>
            <description>On a particular real estate website, we have a "Property Summary" section that contains each property's name, the address, a phone number (if it exists), and a contact email address (if it exists).&lt;br /&gt;
&lt;br /&gt;
The information is data bound to properties in the page's code-behind, and the ASP.NET code basically looks like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;  &amp;lt;%# Property Name%&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;  &amp;lt;%# AddressLine1 %&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;  &amp;lt;%# AddressLine2 %&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;  &amp;lt;%# AddressLine3 %&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;  &amp;lt;%# CityStateZIP %&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;  &amp;lt;%# Phone %&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;  &amp;lt;%# Email %&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
Because lots of these elements are optional, all of these properties have all been written to work like this in the code-behind:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;Protected Readonly Property AddressLine1 as String&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;  Get&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;     If _AddressLine1 = "" Then&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;      Return ""&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    Else&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;      Return _AddressLine1 + "&amp;lt;br&amp;gt;"&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    End if&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;  End Get&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;End Property&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;/div&gt;
&lt;br /&gt;
It was written this way so that there will not be extra empty lines where the data is missing, and there will be a line break if data exists.  Typically, AddressLine2 and AddressLine3 are blank, but they may be present.  The Email property, since it is a link, is even worse:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt; Protected Readonly  Property Email as String&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;   Get&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;      If _Email = "" Then&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;       Return ""&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;     Else&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;       Return String.Format("&amp;lt;a href='mailto:{0}'&amp;gt;{0}&amp;lt;/a&amp;gt;&amp;lt;br&amp;gt;", _Email) &lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;     End if&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;   End Get&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; End Property&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;/div&gt;
&lt;br /&gt;
That a lot of HTML in the code-behind that ideally we'd have in the markup.  My first instinct to make this a little cleaner/simpler was to create a function like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;Protected Function CheckBR(TextToCheck as string) as String&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;  If Text.Trim() = "" Then&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    Return ""&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;  Else&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    Return "&amp;lt;br&amp;gt;"&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;  End if&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;End Function&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
That way, I could remove the empty check and the append of the &amp;lt;BR&amp;gt; tag in my properties, and then use my function on the Page like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;   &amp;lt;%# Property Name%&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;   &amp;lt;%# AddressLine1 %&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;   &amp;lt;%# AddressLine2 %&amp;gt;&amp;lt;%# CheckBR(AddressLine2)%&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;   &amp;lt;%# AddressLine3 %&amp;gt;&amp;lt;%# CheckBR(AddressLine3)%&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;   &amp;lt;%# CityStateZIP %&amp;gt;&amp;lt;br&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;   &amp;lt;%# Phone %&amp;gt;&amp;lt;%# CheckBR(Phone)%&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;   &amp;lt;%# Email %&amp;gt;&amp;lt;%# CheckBR(Email)%&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt; &amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;/div&gt;
&lt;br /&gt;
That way, all optional lines will only have breaks if they exist; only non-optional lines will have &amp;lt;br&amp;gt; tags specified.  This still seemed sloppy.   I thought adding "runat=server" to the "&amp;lt;br&amp;gt;" tags and then specifying the Visible property like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;&amp;lt;br runat="server" visible='&amp;lt;%# Email.Trim() &amp;lt;&amp;gt; "" %&amp;gt;' /&amp;gt;&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
but that really seemed like overkill and didn't appear to make things any cleaner. Isn't there an easier way?  I wondered if I should create a custom ASP.NET "conditional line break" control ...&lt;br /&gt;
&lt;br /&gt;
Then I remembered how browsers render empty tags:  Even if the tag is a block element, and the tag is empty, nothing is rendered -- including the line break!  For example, this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;&amp;lt;div&amp;gt;Hello&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;&amp;lt;div&amp;gt;Bye&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
displays as:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;Hello&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;Bye&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
That is because the middle &amp;lt;div&amp;gt; contains no text (not even a single space) or any other content that is visible.  Thus, all we need to do is use &amp;lt;div&amp;gt; tags, not &amp;lt;br&amp;gt; tags, for our line breaks, like this:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    &amp;lt;div&amp;gt;&amp;lt;%# Property Name%&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    &amp;lt;div&amp;gt;&amp;lt;%# AddressLine1 %&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    &amp;lt;div&amp;gt;&amp;lt;%# AddressLine2 %&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    &amp;lt;div&amp;gt;&amp;lt;%# AddressLine3 %&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    &amp;lt;div&amp;gt;&amp;lt;%# CityStateZIP %&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    &amp;lt;div&amp;gt;&amp;lt;%# Phone %&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;    &amp;lt;div&amp;gt;&amp;lt;a href='mailto:&amp;lt;%# Email %&amp;gt;'&amp;gt;&amp;lt;%# Email%&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;  &amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;/div&gt;
&lt;br /&gt;
Notice that our Email property can simply return the email address and not worry about creating a link, and all of our properties can simply return raw data. We always output the &amp;lt;a&amp;gt; tag without the need to check if the email address is present; if the containing text is empty, neither the link nor the &amp;lt;div&amp;gt; will render in the browser.  That is,&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;&amp;lt;div&amp;gt;Hello&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;&amp;lt;div&amp;gt;&amp;lt;a href="someurl"&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;&amp;lt;div&amp;gt;Bye&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
still displays as:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;Hello&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;Bye&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
in the browser.&lt;br /&gt;
&lt;br /&gt;
This is a little off topic and has nothing to do with SQL Server directly, but it may be a handy tip or reminder for some.  It's nice to get rid of the embedded HTML in the code behind and to eliminate some extra code. This same concept also applies to &amp;lt;p&amp;gt; tags, though in this case we are not using them because the spacing would be too much.  (You could alter this via CSS, of course).&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-weight: bold;"&gt;UPDATE: &lt;/span&gt;In the comments, JD suggests an alternative which I like better.  The only negative is that html tags are being "hidden" in your code-behind, and you'd have to adjust your function (or create a new one) to handle hyperlinks or more complicated output.&lt;br /&gt;
&lt;br /&gt;
But, if you can and would like to create additional functions to help with the formatting, that is a good option.  If you cannot create additional functions or want to eliminate as much code-behind as possible, then the "empty block element" trick can be useful, especially if you already have the tag on your page.&lt;br /&gt;
&lt;br /&gt;
For example, instead of using server-side logic&lt;br /&gt;
&lt;br style="font-family: Courier New;" /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;&amp;lt;p runat="server" visible='&amp;lt;%# &lt;/span&gt;&lt;span style="font-family: Courier New;"&gt;SomeText.Trim() &lt;/span&gt;&lt;span style="font-family: Courier New;"&gt;&amp;lt;&amp;gt; "" %&amp;gt;'&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;   &amp;lt;%# SomeText %&amp;gt;&lt;/span&gt;&lt;br style="font-family: Courier New;" /&gt;
&lt;span style="font-family: Courier New;"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
or using a function that wraps non-blank text in &amp;lt;p&amp;gt; tags:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;&amp;lt;%# PrintAsParagraph(SomeText) %&amp;gt;&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;br /&gt;
you can simply write:&lt;br /&gt;
&lt;br /&gt;
&lt;div style="margin-left: 40px;"&gt;&lt;span style="font-family: Courier New;"&gt;&amp;lt;p&amp;gt;&amp;lt;%# &lt;/span&gt;&lt;span style="font-family: Courier New;"&gt;SomeText&lt;/span&gt;&lt;span style="font-family: Courier New;"&gt;.Trim() %&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
By eliminating the spacing, the paragraph and all of the associated spacing will not display at all if SomeText is blank. &lt;br /&gt;
&lt;br /&gt;
Thanks for the input, JD!&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-style: italic;"&gt;see also:&lt;br /&gt;
&lt;/span&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2007/11/02/parent-child-datatable-nested-repeaters.aspx" title="View Entry"&gt;Splitting a single DataTable into Parent/Child DataTables for Hierarchical Processing (e.g., nested ASP.NET Repeater controls)&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2007/10/09/csv-strings-database-or-presentation.aspx" title="View Entry"&gt;Creating CSV strings in SQL: Should Concatenation and Formatting Be Done at the Database Layer?&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2007/03/28/60144.aspx" title="View Entry"&gt;ASP.NET 1.1 -- Appsettings in Web.config&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="../../../../jeffs/archive/2006/07/11/10572.aspx" title="View Entry" id="ctl00_pageContent_Editor_Results_rprSelectionList_ctl06_HyperLink1"&gt;Minimizing the ViewState for an ASP.NET 1.1 DataGrid&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60375.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2007/10/17/60375.aspx</guid>
            <pubDate>Wed, 17 Oct 2007 15:13:10 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60375.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2007/10/17/60375.aspx#feedback</comments>
            <slash:comments>2</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60375.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60375.aspx</trackback:ping>
        </item>
        <item>
            <title>Creating CSV strings in SQL: Should Concatenation and Formatting Be Done at the Database Layer?</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2007/10/09/csv-strings-database-or-presentation.aspx</link>
            <description>A question I see very often in the SQLTeam forums is how to return data in a summarized form by concatenating a list of values into a single CSV column.  This can be done fairly easily in T-SQL, but as the formatting and concatenation requirements becomes more elaborate, be sure to ask yourself: Am I forcing presentation code into the database layer?  &lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2007/10/09/csv-strings-database-or-presentation.aspx"&gt;read more...&lt;/a&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60359.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2007/10/09/csv-strings-database-or-presentation.aspx</guid>
            <pubDate>Tue, 09 Oct 2007 19:02:57 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60359.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2007/10/09/csv-strings-database-or-presentation.aspx#feedback</comments>
            <slash:comments>18</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60359.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60359.aspx</trackback:ping>
        </item>
        <item>
            <title>Using LINQ with SQL (link)</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2007/07/13/60256.aspx</link>
            <description>There's a great series of posts over at &lt;a href="http://weblogs.asp.net/scottgu/default.aspx" target="_blank"&gt;Scott Guthrie's Blog&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style="text-decoration: underline;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/span&gt; covering &lt;a href="http://weblogs.asp.net/scottgu/archive/2007/05/19/using-linq-to-sql-part-1.aspx" target="_blank"&gt;LINQ&lt;/a&gt;, a new feature in the upcoming version of Visual Studio ("Orcas"). Check it out; I have not had a chance to play around with it yet, but it certainly looks very interesting.  The articles are very well done and explain the concept very clearly with lots of examples.&lt;br /&gt;
&lt;br /&gt;
From the article:&lt;br /&gt;
&lt;br /&gt;
&lt;p style="margin-left: 40px; font-style: italic;"&gt;&lt;font size="2" face="arial"&gt;The above language features help make &lt;em&gt;querying data &lt;/em&gt;a first class programming concept.  We call this overall querying programming model "LINQ" - which stands for &lt;em&gt;.NET Language Integrated Query&lt;/em&gt;.  &lt;/font&gt;&lt;/p&gt;
&lt;p style="margin-left: 40px; font-style: italic;"&gt;&lt;font size="2" face="arial"&gt;Developers can use LINQ with any data source.  They can express efficient query behavior in their programming language of choice, optionally transform/shape data query results into whatever format they want, and then easily manipulate the results.  LINQ-enabled languages can provide full type-safety and compile-time checking of query expressions, and development tools can provide full intellisense, debugging, and rich refactoring support when writing LINQ code. &lt;/font&gt;&lt;/p&gt;
Link: &lt;a href="http://weblogs.asp.net/scottgu/archive/2007/05/19/using-linq-to-sql-part-1.aspx"&gt;Using LINQ to SQL (Part 1)&lt;/a&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60256.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2007/07/13/60256.aspx</guid>
            <pubDate>Fri, 13 Jul 2007 20:27:44 GMT</pubDate>
            <wfw:comment>http://weblogs.sqlteam.com/jeffs/comments/60256.aspx</wfw:comment>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2007/07/13/60256.aspx#feedback</comments>
            <slash:comments>1</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60256.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60256.aspx</trackback:ping>
        </item>
        <item>
            <title>ASP.NET 1.1 -- Appsettings in Web.config</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2007/03/28/60144.aspx</link>
            <description>It's great to be able to put settings in the Web.Config file for my ASP.NET projects.  The problem for me, though, is that when I use System.Configuration.ConfigurationSettings.AppSettings(name)
to return a setting that doesn't exist in the file ...
&lt;br&gt;&lt;br&gt;
&lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2007/03/28/60144.aspx"&gt;read more&lt;/a&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/60144.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2007/03/28/60144.aspx</guid>
            <pubDate>Wed, 28 Mar 2007 16:09:16 GMT</pubDate>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2007/03/28/60144.aspx#feedback</comments>
            <slash:comments>4</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/60144.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/60144.aspx</trackback:ping>
        </item>
        <item>
            <title>Minimizing the ViewState for an ASP.NET 1.1 DataGrid</title>
            <link>http://weblogs.sqlteam.com/jeffs/archive/2006/07/11/10572.aspx</link>
            <description>By default, the ViewState for ASP.NET DataGrids can be quite large, as it normally stores enough information to recreate the grid completely after a postback. Often, I have found that I need the grid to display a list of items with only paging, sorting, and some buttons that let you delete or edit (via another page) individual items.&lt;br&gt;&lt;br&gt;&lt;a href="http://weblogs.sqlteam.com/jeffs/archive/2006/07/11/10572.aspx"&gt;read more...&lt;/a&gt;&lt;img src="http://weblogs.sqlteam.com/jeffs/aggbug/10572.aspx" width="1" height="1" /&gt;</description>
            <dc:creator>Jeff Smith</dc:creator>
            <guid>http://weblogs.sqlteam.com/jeffs/archive/2006/07/11/10572.aspx</guid>
            <pubDate>Wed, 12 Jul 2006 02:22:00 GMT</pubDate>
            <comments>http://weblogs.sqlteam.com/jeffs/archive/2006/07/11/10572.aspx#feedback</comments>
            <slash:comments>4</slash:comments>
            <wfw:commentRss>http://weblogs.sqlteam.com/jeffs/comments/commentRss/10572.aspx</wfw:commentRss>
            <trackback:ping>http://weblogs.sqlteam.com/jeffs/services/trackbacks/10572.aspx</trackback:ping>
        </item>
    </channel>
</rss>