Working with Nested Repeaters in ASP.NET

ASP.NET, Programming No Comments »

Sometimes I miss the option that you have in classic ASP, PHP or Ruby on Rails to just put a nested loop into the html page (view, presentation layer) to display nested data. Of course this is, strictly speaking, against ASP.NET best practices where you’re supposed to use web controls for everything. Then again one could make the argument that the loop actually belongs in the view. This would be .aspx page, and not in the controller or the model (code behind file in ASP.NET).

The task at hand is to iterate through parent categories and child items. The technique suggested by Microsoft is outlined here, and this article uses a nested repeater with a self-join. Starting point is the following DataSet with two related tables and a repeater in the page:

DataSet myDS = new DataSet("NestedRepeaterDemo"); … //fill the dataset myDS.Tables[0].TableName = "ParentCategories"; myDS.Tables[1].TableName = "ChildItems"; DataRelation relation = new DataRelation("myRelation", myDS.Tables["ParentCategories"].Columns["CatID"], myDS.Tables["ChildItems"].Columns["CatID"], true); relation.Nested = true; myDS.Relations.Add(relation); parentRepeater.DataSource = myDS; parentRepeater.DataBind();

So why not bind the child repeater in the page, just for the heck of it:

<table> <asp:Repeater id="parentRepeater" runat="server"> <itemtemplate> <tr class="categoryInTable"> <td colspan="2"> <%# DataBinder.Eval(Container.DataItem, "CategoryName") %> </td> </tr> <asp:Repeater ID="childRepeater" runat="server" datasource=’<%# ((DataRowView)Container.DataItem).Row.GetChildRows("myRelation") %>‘ > <ItemTemplate> <tr><td> <%# Eval("ItemName")%> </td> <td> <%# Eval("ItemDescription")%> </td> </tr> </ItemTemplate> </asp:Repeater> </itemtemplate> </asp:Repeater> </table>

 

Sure enough with this code you will run into a DataBinding: ‘System.Data.DataRow’ does not contain a property with the name x error. 

As a quick explanation for this error the following might suffice:

When the nested repeater binds to its datasource calling the GetChildRows command of the parent DataRowView.Row object the latter returns an array of DataRows (the “child rows”, an object of type DataRow[]) which each repeater Item will be bound to now. However, Eval with the above syntax will only bind to named fields of a DataView object that exposes a name property for each field. The DataRow returned by GetChildRows is merely a row in a table without the name properties so that each field can be accessed only with an indexer (DataRow["ItemName"]).

This is why you can either change the binding expressions above like so:

 

<td> <%# Eval("[\"ItemName\"]")%> </td> <td> <%# Eval("[\"ItemDescription\"]")%> </td>

or you can change the datasource to use the CreateChildView of the DataRowView directly like this:

<asp:Repeater ID=”childRepeater” runat=”server” datasource=’<%# ((DataRowView)Container.DataItem).CreateChildView(”myRelation”) %>’ >

and use old data binding syntax <%# Eval (”ItemName”) %> because now you have a DataView again (just like in the parentRepeater) which supports binding to the data field by name. Of course you have the option to do all the databinding in the code-behind file (usually the ItemDataBound event of each repeater) which gives you better debugging support than the approach above.

I find this “nested data” stuff overly complicated in ASP.NET, especially compared with how easy it is with Ruby on Rails. The plethora of objects which each behave differently is difficult to work with and the Repeater itself is limited in its built-in functionality and designer-support.

Sure enough I also ran into the

Compiler Error Message: CS0246: The type or namespace
name ‘DataRowView’ could not be found (are you missing a
using directive or an assembly reference?)

which IMHO is clearly a bug in ASP.NET as a reference to System.Data exists in the code-behind file that the .aspx web form inherits from. This issue can be fixed by either having an <%@ Import Namespace=”System.Data” %> tag in your aspx file or declaring DataRowView with the full namespace System.Data.DataRowView in the page each time.

For an in-depth view of the binding issues see this article. You might also find this post helpful.

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Validating Windows Forms Text Input

Programming No Comments »

Unlike ASP.NET (which I’m more familiar with), Windows Forms don’t have any “Validation Controls”, so validating input has to be wired into the event model of your app. After some experimenting I found this procedure to work best for validating text box input:

1. Drop an ErrorProvider component onto your form. This component appears to have designer support when you drag it, however, it is not a control but a container-level component (like ImageList and ToolTip among others). The ErrorProvider component will display an error icon (ErrorProvider icon) next to the control that you specify programmatically during validation.

2. Handle the TextChanged Event
For TextBox validation, the preferred event is TextChanged as it lets you validate the contents of the control after they have been entered. Here’s an example:

 The field of view can’t be greater than a full circle (360 degrees):
image

 

 

 

 

 

If the user enters a letter, the error icon is displayed:

 

image

 

 

 

 

 

 

If the user enters a number too large, an error icon with a different message is displayed:

image

 

 

 

 

 

Here’s the event handler for the TextChanged event:

private void textBox1_TextChanged(object sender, EventArgs e) { if (!System.Text.RegularExpressions.Regex.IsMatch(textBox1.Text, @"^\d{1,3}$")) { errorProvider1.SetError(textBox1, "Please enter only up to three digits."); } else { if (float.Parse(textBox1.Text) > 360) { errorProvider1.SetError(textBox1, "FOV cannot be larger than 360°"); } else { errorProvider1.SetError(textBox1, ""); } } }

Regular expressions come to the fore here. The beginning of line (^) and end of line ($) characters in the pattern “^\d{1,3}$” are very important as otherwise the IsMatch function will return true as long as there is at least one decimal digit in the TextBox.Text.

Regular expression are cryptic which makes memorizing them hard. This article which accompanies the Expresso utility is great to refresh your memory. Hope it helps.

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Synchronizing Local Data with a Remote Web Hosting Database

Programming No Comments »

Many webmasters of smaller web sites use the services of web hosting companies located in locations around the globe. Here in Thailand running your own web server is not feasible, as connections from the important markets of Europe, the Americas and even other parts of Asia might be too slow and unreliable. Using the services of a web hosting company usually involves running a local development database and an identical database running on the web hosting company’s servers.

To keep the contents in sync, it is desirable to have an easy-to-use system of uploading refreshed data to the remote database, ideally from the desk of every employee on the floor via an ASP.NET web app on the local intranet. There are several options available to achieve this:

Programming against SQL Server 2005 Integration Services (SSIS)

The successor of DTS is far more flexible and powerful than its predecessor, with great enhancements of the programming model in terms of extensibility and flexibility. How to call SSIS from C# is detailed in this article. However, SSIS are only installed with advanced versions (Standard upwards) of SQL Server and are not included with the free Express Edition nor the Workgroup edition (with the latter you apparently can open and save SSIS packages). I found this option not feasible, mainly because debugging is almost impossible and the Dts.Runtime.Package.Execute () method only returns very terse result code.

Using SQL

Of course you can issue INSERT queries that transfer your data to a remote database. If your remote server is a linked server you can simply issue a query like the following:

INSERT INTO [my.linked.server].myremotedb.user.mytable

SELECT * FROM localdb.user.mytable WHERE id = @ID

GO

However, if id is an identity column (as the name suggests), you’re out of luck as you have trouble with this query:

SET IDENTITY_INSERT [my.linked.server].myremotedb.user.mytable ON

This query issued to the linked server from your local db will usually fail in a hosting environment due to a lack of pertinent permissions.

An OPENROWSET ad-hoc connection won’t help you either because the KEEPIDENTITY table hint provided by an OPENROWSET query can only be used when inserting from a data file, while reading data from an OLEDB data source also might fail due to permission restraints.

SqlBulkCopy to the Rescue

ADO.NET 2.0 offers a fast and efficient way to transfer data to a remote database with the SqlBulkCopy class.

The following code illustrates this feature:

private void transferWidgetData(int widgetID) { //tables in the local and remote database are identical string[] myTables = { "widgets", "widgetDetails", "widgetPrice" }; string connectionStringLocal = ""; string connectionStringRemote = ""; using (SqlConnection conLocal = new SqlConnection(connectionStringLocal)) { foreach (string s in myTables) { //create the source command SqlCommand commandSourceData = new SqlCommand( "SELECT * " + "FROM " + s + " WHERE WidgetID = " + widgetID, conSource); SqlDataReader reader; //connection has to be re-opened for each datareader conSource.Open(); reader = commandSourceData.ExecuteReader(); using (SqlBulkCopy bcp = new SqlBulkCopy( connectionStringRemote, SqlBulkCopyOptions.KeepIdentity)) { bcp.DestinationTableName = "dbo." + s; try // Write from the source to the destination. { bcp.WriteToServer(reader); } catch (Exception ex) { //handle error here } finally { conSource.Close(); } } } } }

Note here the extremely useful SqlBulkCopyOptions.KeepIdentity hint which enables inserting identity values into the remote table. This code can easily be called from ASP.NET, enabling fast and flexible content management and synchronization with the remote database from your local intranet.

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Packaging and Deployment of Windows Mobile Applications (VS 2005)

Programming No Comments »

While the basic cab creation is outlined here, this article describes the entire rather involved process of packaging the cab with a windows installer. This procedure takes some practice to master. No fewer than 3 major steps have to be taken outlined in this little cheat sheet:

Step 1. Package the output of your project (project type: Smart Device Cab Project)

    • Add project output of your main project (which is usually a smart device app) and create a link to it for the mobile device’s programs menu.
    • Set registry values your new project will need to create on the mobile device. 

Step 2. Create a custom installer package (project type: Windows Class Library).

    • Put an .ini text file into the custom installer project, put the relevant info into this file and reference the file in the event handlers.

Step 3. Create the setup package (project type: Setup Project).

    • Add the cab file from the cab project and the .ini and  the dll created by the custom installer project.
    • Add a custom action (right click project, View>Custom Actions) with a reference to the custom installer dll.

There are several software packages that help you with this process. However, some practice will soon make you an expert and arguably give you more control than a prefab software package.

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

Seconds Passed since Point in Time

Programming No Comments »

After not finding a simple app online calculating the seconds since a past point in time, I made this little web form.

It uses the rather obscure DateTime.ParseExact () function which provides an unforgiving way to parse a date string according to a specific date format string. The date string has to match the format string exactly, otherwise an exception will be thrown. Also, don’t forget to escape slashes in your date format string.

Here’s some sample code:

string theDate = tbDateInput.Text; System.IFormatProvider format = System.Globalization.CultureInfo.InvariantCulture; string expectedformat = "yyyyMMddHHmmss"; DateTime startDate = DateTime.ParseExact(theDate, expectedformat, format, System.Globalization. DateTimeStyles.AllowWhiteSpaces ); TimeSpan sp = DateTime.Now - startDate; lblResult.Text = String.Format("{0} seconds have passed since {1}", sp.TotalSeconds, startDate.ToLongDateString () + " " + startDate.ToLongTimeString ());
[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]
WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS Log in