October 2004 - Posts

Testing ASP.NET 2.0
31 October 04 05:56 PM | Scott Mitchell

Ever wonder what kind of testing occurs for software systems like ASP.NET? Scott Guthrie's latest blog entry, Testing ASP.NET 2.0 and Visual Web Developer, delves into the testing rigors that the ASP.NET team applies. Obviously, every software product strives to have zero bugs, but that's rarely, if ever possible due to the scope of large software projects. From Scott's blog entry:

For ASP.NET 2.0 and Visual Web Developer, we have to be able to deliver a super high quality product that is rock solid from a functional perspective, can run the world’s largest sites/applications for months without hiccups, is bullet-proof secure, and is faster than previous versions despite having infinitely more features (do a file size diff on System.Web.dll comparing V2 with V1.1 and you’ll see that it is 4 times larger).

Now doing all of the above is challenging. What makes it even harder is the fact that we need to deliver it on the same date on three radically different processor architectures (x86, IA-64, and x64 processor architectures), on 4 different major OS variations (Windows 2000, Windows XP, Windows 2003 and Longhorn), support design-time scenarios with 7 different Visual Studio SKUs, and be localized into 34+ languages (including BiDi languages which bring unique challenges).

In order to perform the sheer quantity of tests needing to be run - over 105,000 test cases and 505,000 test scenarios - the ASP.NET team employs 1.4 testers to every developer, and relies on automated testing techniques to offer the breadth of test cases executed. Specifically, a system called “Maddog” helps run these automated tests.

A tester can use Maddog within their office to build a query of tests to run (selecting either a sub-node of feature areas – or doing a search for tests based on some other criteria), then pick what hardware and OS version the tests should run on, pick what language they should be run under (Arabic, German, Japanese, etc), what ASP.NET and Visual Studio build should be installed on the machine, and then how many machines it should be distributed over.

Maddog will then identify free machines in the lab, automatically format and re-image them with the appropriate operating system, install the right build on them, build and deploy the tests selected onto them, and then run the tests. When the run is over the tester can examine the results within Maddog, investigate all failures, publish the results (all through the Maddog system), and then release the machines for other Maddog runs. Published test results stay in the system forever (or until we delete them) – allowing test leads and my test manager to review them and make sure everything is getting covered. All this gets done without the tester ever having to leave their office.

The lab Scott mentions is actually four labs, containing over 1,200 machines in total. Scott posts a picture of one of the many rows of computers in one of the test labs. During the MVP Conference in April Scott showed myself and a few other MVPs one of these test labs. If I'm remembering correctly, the room had about 20 or so rows like the one shown. It was pretty impressive, especially considering it was just one of four such labs.

For more on the testing process, be sure to read Scott's blog entry, there's a ton of information, Maddog screenshots, and so on. Definitely worth reading if you want a peak into how your favorite Web programming technology is tested.

Filed under:
Crystal Reports Book for .NET
29 October 04 10:41 AM | Scott Mitchell

Earlier I had blogged about my forays into Crystal Reports, so I ended up getting a copy of Brian Bischof's Crystal Reports .NET Programming book, which is 500+ pages on using Crystal Reports in a .NET environment. I've yet to have a chance to read the book, been swamped with non-CR tasks as of late, but expect the book to be invaluable once the priorities shift back to CR, especially with some of the more complicated CR reports that are in the pipeline. As I work through Brian's book, I'll be sure to post a review on my blog.

For those interested in writing, Brian's story is a unique one in the computer trade book industry. Brian originally started writing his CR book for a publisher but the book got nixed due to the publishing company going out of business, IIRC. Having invested the time into creating the majority of the book, Brian decided to self-publish the book. I met up with Brian about 13 months ago, back when he was living in San Diego, and learned about the economics of self-publishing, as shared in this blog entry. After talking a bit with Brian, I think I'm going to give self-publishing a try (eventually).

Of course, I shouldn't go on promising too much. I've already promised myself I'd start writing fiction before the year's end, and that's looking less and less likely with looming deadlines both on the consulting side and book-writing side. We'll see how the consulting gigs progress through the remainder of this year...

skmLinkButton - Easily Displaying Text in the Browser's Status Bar
21 October 04 10:36 AM | Scott Mitchell

I was perusing the microsoft.public.dotnet.framework.aspnet.webcontrols newsgroup yesterday and stumbled upon this question by Brian Hoops:

Is there a way within asp to modify what the status bar text reads when the user hovers above the text for a link-button.

IE: http://www.sitename.com/pagename.aspx rather than javascript:__doPostBack(...)

or do I need to have a javascript onmouseover for every one?

I responded to Brian's question, informing him that there were no built-in properties of the LinkButton Web control that would provide this functionality, and that adding said functionality could be accomplished in one of two ways:

  1. By manually entering the needed JavaScript into the LinkButton's declarative syntax, or programmatically through the LinkButton's Attributes collection, or
  2. By creating a new custom control that derived from LinkButton and included a property like StatusBarText, that could be set to the text to display in the browser's status bar. This extended control, then, would inject the necessary JavaScript.

For the heck of it I whipped up a custom control called skmLinkButton that extended the LinkButton class and provided the desired behavior. (Additionally, skmLinkButton includes the ability to add a confirm dialog box, similar to Andy Smith's ConfirmedButtons set of controls.)

skmLinkButton, along with my other open-source ASP.NET server controls, is listed on my Code Projects page.

Filed under:
A FireFox Friendly MSDN
20 October 04 03:04 PM | Scott Mitchell

In a couple of previous blog entries of mine - Don't Make Me Use IE and Here's One Way to Win the Browser War - I expressed my frustration at MSDN Web pages not displaying code sections properly in FireFox. In Internet Explorer the code samples looked fine, but FireFox caused all of the code to be smushed together, without any whitespace between characters or lines of code.

Today I got some good news in the ol' InBox, as Lowell Meyer wrote in to tell me that MSDN's CSS files have been fixed to support FireFox. The following is Lowell's email (printed with permission from Lowell):

I know it’s been a couple months, but I wanted to let you know we’ve fixed the problem with the code samples in the library for non-IE browsers. The library (/library) is handled largely separately from the rest of the site, which is why it was still a problem. Code samples should now display correctly formatted, and they should word wrap when necessary. The developer center pages should still work as well, though they won’t have the nice text wrap when necessary like the library pages we’ve fixed here. We’re working the improved fix for the dev centers through the system as well, and everything should be perfect soon.

If you see any pages that still have the problem, please let us know. We’ve tried to find every CSS file that has this issue and fix it, but it is possible we might have missed one.

Here’s what we did: The fix was a pure CSS fix. The fix was to modify the <pre> tag style from the original (only part of the style shown for clarity):

pre
{
white-space:normal;
word-wrap:break-word;
}

To the new version:

pre
{
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}

If you check out the page you originally sent us as being broken, the fix is live and should be working correctly: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconcompositionvsrendering.asp

Looks good, Lowell, thanks for following up.

Existing FAQ Applications? FAQ Application Wish List?
20 October 04 11:02 AM | Scott Mitchell

Are there any existing ASP.NET applications designed out there for creating a FAQ site? One that has a set of categories with frequently asked questions in each category, along with answers? Over at ASPFAQs.com I have a FAQ application written in classic ASP, and am wondering if there already exists one for ASP.NET that I'm unaware of.

Also, what would be in your ideal FAQ application? At ASPFAQs.com there are two parts: the front-end, that anyone can see, and a back-end, which is reserved only for specified administrators. On the front-end, the FAQ categories are listed, and users can drill down to the FAQs in a specific category or jump to a particular FAQ if they know its ID. There's also a means to display the 10 most popular FAQs (i.e., those that have received the most views), as well as the 10 most recent FAQs.

From the back-end, user accounts can be created to allow folks to create, edit, approve, and delete FAQs. There are different permission levels that can be granted, such as one user can only create FAQs, but they must first be approved by a moderator before they appear on the live site. Others may be able to create, edit, and approve FAQs, but not delete existing FAQs. Creating a FAQ is pretty straightforward, and involves just selecting the category, the frequently asked question, and providing an answer.

Here are some potential additions I have thought up:

  • Ability to create “to-do“ FAQs. That is, anyone could add to a table a list of preferred frequently asked questions. From the back-end, approved users could choose one and answer it, or could remove ones from the list that might already be answered on the site or are off-topic.
  • Better reporting tools, showing folks on the back-end the number of times a FAQ has been viewed, the number of views plotted against the date, and so on...
  • Ability for an end user to ask a follow-up question to a FAQ. This question would get sent to the author of the FAQ, and he could then easily update the FAQ's answer including the user's question inline, along with his answer.
  • An RSS feed to the FAQs

Any other suggestions?

Filed under:
Computed Columns and Divide by Zero Errors
19 October 04 10:17 AM | Scott Mitchell

Problem: With SQL server you have a computed column that has the following formula: SomeColumnA / SomeColumnB, where SomeColumnA and SomeColumnB are non-NULL numeric columns. Clearly a problem can arise if there is a record in the database with SomeColumnB equal to 0.0, as that will result in a Divide by zero error. Assume that the business requirements require that SomeColumnB might, indeed, be 0.0 for some records, and for such records, the computed column should result in a value of 0.0. The problem - how do you do this, as computed columns don't allow conditional statements.

My Solution: Here's my solution (feel free to nitpick, suggest alternative approaches, lambast it as causing more detriment to the field of computer science than GOTOs, etc.). For the computed column's formula I used NULLIF to see if SomeColumnB was 0.0. If so, I have it return NULL, and the division will result in NULL. I then wrapped the whole thing in an ISNULL, so if the division resulted in NULL, the result was 0.0:

ISNULL(SomeColumnA / NULLIF(SomeColumnB, 0.0), 0.0)

I'm hardly a SQL buff, so if there are any pitfalls to this approach, share them in the blog's comments, please. On a closing note, let me quote from Sql-Server-Performance.com's comments on computed columns:

Generally, using computed columns in a table is not recommended because it does not follow the standard rules of normalization. But, it is sometimes more efficient overall if you use computed columns in a table than having to recompute the same data over and over in queries. [Empahsis their's.] This is especially true if you are running the same query over and over against your data that performs the same calculations over and over. By performing the calculations in the table, it can reduce the amount of work performed by a query each time it is run. You have to determine for yourself where the bottleneck in performance is, and act accordingly. If the bottleneck is in INSERTS and UPDATES, then using calculated columns may not be a good idea. But if your SELECT statements are the bottleneck, then using calculated columns may pay off.

UPDATE (Oct. 19th, 2004 - 1:05 PM PST): A helpful post from graz in the comments indicated that CASE statements can be used in a computed column, thereby providing conditional-like semantics for a computed column. To learn more about CASE be sure to check out graz's article The case for CASE.

Filed under:
My AggBug Experiment
13 October 04 09:58 AM | Scott Mitchell

This blog is powered by Scott Watermasysk's .Text blogging engine, but it's an older version of the software, one that doesn't support statistics. More recent versions of .Text provide a rough estimate as to how many folks have read your blog through the use of AggBugs. Prior to the popularization of syndicated content, determining how many folks read your content was a rather simple task of parsing the Web server's log files. But with syndicated content, examining the number of people who requested the RSS feed does not give a strong estimate. For example, my aggregator might slurp down a particular RSS feed 24 times over the course of the day, yet I may read a particular entry only once or twice, or maybe not at all.

To surmount these problems, AggBugs are commonly used. An AggBug is a tiny bit of HTML in the RSS feed that typically is an <img> tag that requests a 1x1 transparent GIF file. Each time someone loads up a particular entry in their aggregator (or visits the particular blog entry page), a request is made to that GIF file. To determine how many times your blog has been read, you can simply lookup in your Web server's log files to see how many times that GIF has been requested.

Since the version of .Text I use lacks the AggBug feature, I have never really had a good idea as to how many folks read my blog entries. The suspense has been killing me, so I decided to implement by own AggBug feature for a particular blog entry, Sample Chapter of my .NET Web Services DVD. (Rather than using just a simple 1x1 GIF, I created an ASP.NET page that returned a 1x1 GIF, but logged more information than just a simple hit (namely the user's IP, their User-Agent string, and so on.) It's now been 24 hours since that blog entry was posted, and here are the results:

  • 553 unique IP addresses requests
  • 860 total requests
  • Twice as many people (2) hit my site with Netscape 3 than did with IE 4 (1). Only 2 folks visited with Opera, which is as many people as visited with Safari.
  • The vast majority (95+%) use IE 6.0 (I believe the popular blog readers - RssBandit, FeedReader, FeedDemon, etc. - use IE as their browser by default, so this makes sense).

It'll be interesting to see how the readership for a particular blog entry shrinks or grows as the days progress. I'd wager something like 80% of the views occur in the first week (maybe two), and the remaining 20% occur over the rest of the year.

Update to My MSDN Article "Working with Client-Side Script"
12 October 04 05:27 PM | Scott Mitchell

This is an unofficial update to a recent MSDN Online article I wrote titled Working with Client-Side Script. In that article I examined a number of common client-side tasks, and looked at how to accomplish them easily with ASP.NET by extending the Page class. Specifically, I looked at how to do things like: display an alert dialog box; how to display a confirm dialog box when deleting or performing other “Are you sure you really, really want to do this?”-type of activities; how to display a popup window; and how to alert the user if they're about to leave a page from which they've made changes but have yet to save those changes.

In my article I accomplished the last common task by adding methods to the extended Page class that allowed a page developer to easily add a client-side onchange event to the data-entry Web controls that would set a flag indicating they had been modified. Then, when leaving the page through a button click, hyperlink, etc., the user would be prompted with a standard confirm dialog asking them if they were sure they wanted to leave the page (assuming that the dirty flag was true).

This approach had two drawbacks:

  1. It flagged the data as dirty from a simple change. That is, if a user changed a textbox value from “Scott“ to “Jisun“ and then back to “Scott,“ when leaving the page they'd be warned that they were leaving without saving. This warning would happen because the data had been altered at least once, even though it had been changed back to its original value.
  2. If the user left the page through some other means (i.e., not necessarily by clicking on some navigational Web control), then they could leave the page without the warning. Ideally the user would receive a warning if they closed their browser, typed in another URL in their browser's Address bar, or even clicked a link in, say, Outlook, that redirected that browser instance to another page.

Last week on 4Guys I wrote an article - Prompting a User to Save When Leaving a Page - that looked at how to use the client-side onbeforeunload event and some clever JavaScript. The onbeforeunload event fires whenever the document is unloading, and gives the developer a chance to prompt the user. The user can choose to either continue with the unloading of the document, or can stop it outright. This works even if they attempt to close the browser window. (One caveat is that there's not standard browser support for onbeforeunload. IE supports it, as does FireFox 0.9+.)

Today I wrote an article titled Using ASP.NET to Prompt a User to Save When Leaving a Page. This article looks at how to extend the Page class to include methods to easily add this onbeforeunload support to your ASP.NET data-entry forms. This article serves, in a roundabout way, as an update for the technique examined in the MSDN article. (Although the MSDN article's approach is more compatible with non-standard browsers.)

Filed under:
Sample Chapter of my .NET Web Services DVD
12 October 04 09:22 AM | Scott Mitchell

A few months ago I announced the release of a training DVD of mine, Beginners .NET XML Web Services 2004. This two-disc DVD covers Web Services from the ground up, showing how to create and consume Web services using Visual Studio .NET 2003 using both VB.NET and C# examples. In addition to covering the core features of Web Services - SOAP, XML serialization, WSDL, proxy classes, etc. - the DVD also explores the Web Service Enhancements (WSE), both from a high-level (Chapter 12), as well as two specific examples: UsernameToken authentication (Chapter 13) and sending attachments with DIME and WS-Attachments (Chapter 14).

If you are interested in learning more about this DVD, you can check out Chapter 14 in its entirety (all 30+ minutes), which is available along with some other sample chapters from DVPress's training DVDs at http://dvpress.com/ecom/Downloads/Sample_Clips.aspx.

One downside with the sample chapter is that in certain areas I refer to figures. Some are flashed up on the screen, but some are not. These figures - along with the complete source code for the projects I demoed - are all available on the DVDs. Speaking of which, if you enjoy the sample chapter, consider picking up a copy of the DVD, which can be purchased through DVPress's website (they accept PayPal) or Amazon.com.

Crystal Reports Tip for the Day
08 October 04 01:00 PM | Scott Mitchell

As I alluded to in a blog post earlier this week, I am embarking on the journey of learning Crystal Reports and integrating them into an ASP.NET site. While I have definitely learned a lot over the past 36 hours or so, it still feels a bit frustrating because sloshing through this stuff seems to take forever. The upside is that I find myself celebrating often, when accomplishing even the most trivial tasks. In any event, I've decided to add a few blog entries on my learning experiences with CR, in a hope that others who have to pick this up in the future might benefit from my hiccups along the way. (To group these CR-related posts, I added a Crystal Reports category to my blog.)

Today's Crystal Reports tip is how to display some piece of random data in a report. As you may know, Crystal Reports provides a designer that makes it (relatively) easy to create a report that is tightly bound to database data. But sometimes you have some odd bit of data floating out there that you need to have displayed, that doesn't really relate or fit in with the data being bound to the report. For example, in an invoice report, I might want to add a user-defined sign-off, like “Thank you for your business.” To accomplish this, here's what I did (and, yes, I'm more than happy to hear that I did it the wrong way, as long as you can provide a better way):

  1. I started by creating a new Formula Field named SignOffMessage and dragged it onto my designer precisely where I wanted it to appear (in the Report Footer, if you must know). (When creating a new Formula Field it will ask you for the field's formula. For just squirting in a value from the code-behind class, I simply left the formula blank.)
  2. Next, in my code-behind class, I have a reference to the actual report in a local variable named invReport (see my earlier blog post on CR for a code snippet that shows creating this invReport local variable). To set the formula field to a particular value, I used:

    invReport.DataDefinition.FormulaFields("SignOffMessage").Text = valueFromDatabase

That's it, worked like a charm.

Another nice thing about Formula Fields is that they can be used to perform formulas upon existing data-bound elements. For example, my invoice report binds a set of activities to an invoice, with each activity having fields like Description, Date, AmountDue, and so on. Using a Formula Field, I can have in my Report Footer a summation of the AmountDue fields. To accomplish this, I simply created a new Formula Field and set its Formula to Sum( {Invoice.AmountDue} ).

Filed under:
Control Building and ViewState Redux
08 October 04 08:35 AM | Scott Mitchell

Earlier this week I posted an entry titled Control Building and ViewState Lesson for the Day, in which I discussed when working with dynamic controls the importance of the order with which a control is added and its properties are set. My end recommendation was to use the following pattern:

  1. Create an instance of the control (i.e., TextBox tb = new TextBox();)
  2. Add the control to the appropriate Controls collection (i.e., PlaceHolderID.Controls.Add(tb);)
  3. Set the dynamic control's properties (i.e., tb.ForeColor = Color.Red;)

The key lesson was to make sure you do step 2 before step 3, or you may be plauged with view state issues. In that previous blog entry I mentioned that the reason this happens is because if the control is added after the TrackViewState stage in the page lifecycle (such as in the Load stage), then any changes to the control's properties are not recorded by view state, so nothing gets saved to view state and so nothing on postback gets populated back into the control's view state. However, I said that adding a control via the Controls.Add() method causes the controls TrackViewState() method to be called. This is correct, but it's not a complete answer.

Not only is the TrackViewState() method being called for the added control, but it goes through the entire control lifecycle, if needed. Using Reflector you can see that the control's InitRecursive() method is called, which fires the Init event for the added control and all of its children controls (this also is where the control's TrackViewState() method is invoked). If the control that is having the dynamic control added to it has a non-null ViewState, the control being added has it's LoadViewStateRecursive() method called, which loads the view state into the added control and its child controls. But things don't end there necessarily. If the control that had the dynamic control added to it has already fired its Load event, the added control's Load event is fired (recursively, again), and even the PreRender event is fired if needed.

One final point to clear up: in my previous blog entry I credited Alex Lowe with being the one whose words from 2001 had reminded me that I needed to add the control first and then set its properties. Memory's a funny thing, really, since I remembered Alex's words, but I had totally blanked on an email Wessam Zeidan had sent me in late September 2004 where he talked about the same thing, saying: “... some how when we add a dynamic control to the Controls collection, the TrackViewState method gets called, and that explains why the backcolor property of the textbox gets saved to the viewstate if we add the textbox to the controls collection before setting it.” And if that wasn't enough, I realized that I said the same thing myself in my MSDN article Understanding ASP.NET View State:

You may be able to get away with loading your controls in the Page_Load event handler and maintaining the view state properly. It all depends on whether or not you are setting any properties of the dynamically loaded controls programmatically and, if so, when you're doing it relative to the Controls.Add(dynamicControl) line. A thorough discussion of this is a bit beyond the scope of this article, but the reason it may work is because the Controls property's Add() method recursively loads the parent's view state into its children, even though the load view state stage has passed.

It's funny how our minds work, and how fluid memory can truly be.

Binding a Strongly-Typed Collection to a Crystal Report
07 October 04 04:56 PM | Scott Mitchell

Prior to today you could count the number of hours I've used Crystal Reports on one hand. Despite that, I'm working on a project now where my client needs to be able to print invoices based on the data collected through a Web application I've already created for him. Namely, he needs a PDF document that corresponds to some pretty specific dimensions, and already has bought Crystal Reports and wants to use that. (He also needs a number of Web-based reports through CR as well.)

To start off my journey into Crystal Reports, I started creating some simple reports through the templates provided by the Crystal Report Designer that ships with Visual Studio .NET. While it was pretty easy to create elegant reports based on database data, this application is designed into distinct layers, with an application logic layer and business objects used to represent the logical entities of the system (rather than direct SQL queries and whatnot). My challenge, then, was to figure out how to have CR let me bind a custom, strongly-typed collection to the report rather than going straight to the database.

Not having much luck, I popped over to the ASP.NET Crystal Reports Forum, which I hadn't used before, and asked my question. Richard Dudley was kind enough to answer my question by providing a number of great links, this one solving the problem at hand (see the (currently) second to last message in the thread, the one from rjdudley), in conjunction with the CR document Reporting Off ADO.NET DataSets. Essentially what I did was this:

  1. Create a strongly-typed DataSet, designing the schema to correspond to the particular invoice information I needed to display on the report (date, description of work performed, hours, rate, total, etc.). I called this InvoiceDataSet.
  2. Next, I created an ASP.NET page that would display the invoice in PDF format, called ViewInvoice.aspx. This page accepted a parameter through the querystring, indicating the ID of the invoice to display.
  3. Next I added a Crystal Report to my Project named InvoiceReport. In the wizard, I opted to base my data from an ADO.NET DataSet in the Project, selected the strongly-typed DataSet I had created in step 1, and added all rows. I then played with the Designer to get it to look like I wanted (added some fields that computed sums, added some boilerplate “Thank you for your business“ platitudes, etc., etc.).
  4. Now, to bind the custom strongly-typed collection to the Crystal Report, I used the following code:

    'Create an instance of the report (remember, InvoiceReport is the name of the report I created)
    Dim invReport As New InvoiceReport

    'Create an instance of the strongly-typed DataSet
    Dim ds As New InvoiceDataSet

    'Populate the DataSet - DAL is my data access layer class, GetInvoiceActivities gets all of the activities
    'for an invoice. invID is a local variable that contains the invoice ID passed in through the querystring
    Dim invoiceActivities As ActivityList = DAL.GetInvoiceActivities(invID)

    For Each act As Activity In invoiceActivities
    'Adds a new row to the strongly-typed DataSet. My DataSet was created having
    'columns for description, the date, the rate, the hours, and the total due (the reason I
    'have a total due and not just compute it via rate * hours is that some activities are billed
    'at a flat rate, so the actual amount due is just rate * 1, regardless of hours; this is all
    'done at the application layer level, though, not here in the presentation layer).
    ds.Invoice.AddInvoiceRow(act.Description, act.Date, act.Rate, act.Hours, act.TotalDue)
    Next

    'Finally, we assign the DataSet to the report!
    invReport.SetDataSource(ds)
  5. At this point if you want to display the report in HTML, simply add a CrystalReportViewer control to your page and assign the control's ReportSource property the invReport object. However, if you want to display the report as PDF, you can use the code over at this USENET post by Steve Volp to stream the PDF directly to the browser (no need to save the PDF locally on the Web server's file system).

Since I have a good deal of Crystal Reports in my future, I think I'll pick up a copy of Brian Bischof's Crystal Reports for .NET. On an aside, I had lunch with Brian a year or so ago and talked about, among other things, self-publishing. Interestingly, Crystal Reports for .NET is a self-published work by Brian. So you can buy it knowing that you're directly supporting the author, rather than just supporting him 10%. :-)

What Would Your Dream Online Survey Control / Application Entail?
07 October 04 08:57 AM | Scott Mitchell

UPDATE (Oct. 7th, 11:30 AM): After reviewing nSurvey I decided not to continue on with this article... why reinvent the wheel when those guys have already done a good job doing so?

I'm working on an online survey control/application for an upcoming MSDN Online article, and wanted to poll the six people who read this blog and ask, “What would your dream online survey control / application entail?” Here are my three main goals:

  1. Make it easy for a non-computer expert to create and edit surveys through a user-friendly Web interface.
  2. Make it easy for ASP.NET page developers to add the survey to a page. Namely, they should have to do nothing more than drag and drop a control from the Toolbox and set a couple of properties.
  3. Provide a decent set of reports that allow the higher-ups to view the results of the survey, either in a summary view or on a per-user view.

With those three high-level goals in mind, here are the more implementation-specific features my survey app currently supports:

  • Ability to have survey questions of varying types: currency, text, memo, numeric, yes/no, a drop-down list from a lookup table, etc.
  • Question-level constraints, such as required fields, or for numeric/currency fields, an optional low/high value (such as when asking for their age, it must be between 0 and 125, inclusive).
  • Ability to have surveys composed of multiple questions, spanning multiple pages.
  • Ability to have the “flow“ of the survey be based upon the survey-taker's responses. That is, if page 1 includes a question like, “What is your annual household salary?“, the user could be taken down a different path if their answer was greater than $100,000.

The main feature that is lacking, IMO, that I'd like to implement, but don't see a clear, easy way to implement is providing a high degree of customization of the survey user interface. Sure, the page developer can customize the appearance with stylesheets and control-specific styles, but I can't fathom a good way to allow them to customize the markup on a question-by-question basis. That is, saying, “Question 4 should be in bold and underlined, while question 6 should be offset to the right in a

. While typically templates are used in controls to allow the page developer more freedom on the markup, I don't see how templates could be used to allow question-specific customization, since the number and types of questions for a particular survey can differ per page and are specified not by the page developer, but through a Web interface.

Are there any other features you'd definitely want to see in an online survey control / application? To date, I've created the data model and a bare-bones survey control, so there's still time to get in your features! :-)

UPDATE (Oct. 7th, 11:30 AM): After reviewing nSurvey I decided not to continue on with this article... why reinvent the wheel when those guys have already done a good job doing so?

Filed under:
Control Building and ViewState Lesson for the Day
06 October 04 03:17 PM | Scott Mitchell | 1 comment(s)

Whenever I am working on a problem and hit a perplexing roadblock that impedes my process, my initial emotional state is calm. “I'll figure out this problem soon,” I tell myself, as I explore workarounds or examine the code base to try to understand why I can't do what I know I should be able to do. If this search goes on for five or more minutes, I start to get a little flustered. Off to Google!” I declare, hoping there has been some other unfortunate soul who has experienced the same problem and has shared his solution. I usually hit the Google Web search first and, if I have no luck there, I delve into the USENET postings on Google Groups. If 15 minutes have passed with no hits from Google, I start to get angry at myself. “I SHOULD KNOW HOW TO FIX THIS,” I say, as I pour back over the material I examined in the first five minutes, knowing there must be somethinig I overlooked. What's interesting is that if this search continues into, say, the half hour mark, my emotions start to sway from frustration and despair to glee. “Wait until I figure out what went wrong,” I reason, “and I can share it on my blog, and it will help others down the road.” And that's what keeps me going.

Today's lesson - which will hopefully save you the 40 minutes it cost me - centers around composite controls, the DropDownList Web control, and ViewState. Let's say you want to create a composite control that contains a DropDownList that already is bound to database data (namely, it does not allow the control developer to specify the DropDownList's DataSource as would normally be the case: for a discussion on the pros and cons of this approach check out Composite Controls That Do Their Own DataBinding by Andy Smith). If you are at all like me, you're composite control's code would look like this (WARNING: this is wrong):

public class MyControl : WebControl, INamingContainer
{
...

protected override void CreateChildControls()
{
DropDownList ddl = new DropDownList();
if (!Page.IsPostBack)
// do data binding to some database...

Controls.Add(ddl);
}
}

This, as aforementioned is the WRONG WAY to do it. Why? Well, give the control a whirl by creating an ASP.NET Web page with this control and a Button Web control. When you visit the page the first time, the DropDownList is displayed, populated with the database data. However, if you click the Button and the page posts back, the items in the DropDownList disappear - the DropDownList is not saving its view state.

If you're like me, the first thing you do is crack open Developing Microsoft ASP.NET Server Controls and Components, thumb through to Chapter 12: Composite Controls, and read the section titled “State and Child Controls.” The gist of the section says that composite controls don't need to employ extra effort to save the view state of their children since it's saved automatically when the page recursively walks the control tree to generate the entire view state. Opening Reflector, I poured through the Page and Control classes, and saw that, yes, the control hierarchy should be walked and the view state saved, so when my composite control's SaveViewState() method was called, it should save not only its own view state, but the view state of its child(ren): namely, the DropDownList. But, evidently it was not.

This got me to scratching my head. Why isn't the DropDownList's view state being saved? EnableViewState for both the Web page, composite control, and DropDownList were all set to True (the default). I looked at the SaveViewState() method for the DropDownList and confirmed that it should be saving its view state. So why wouldn't the DropDownList be saving its view state?

And then it occurred to me - check to make sure that the DropDownList's IsTrackingViewState property is True - if it isn't, then the DropDownList won't be recording changes to its state, and therefore won't produce a ViewState. A control begins tracking its view state when its TrackViewState() method is called, and this is handled after the Initialization stage of the page lifecycle. So, if the DropDownList is being added after that stage, then it wouldn't be tracking its view state, and therefore its state would be lost on postbacks.

But wait a minute, if you add a child control via the Controls.Add() method, the added child control's TrackViewState() method is automatically called. But wait! Only those items added to the view state after the view state has started being tracked will be recorded. So binding the items to the DropDownList prior to adding the DropDownList to the Controls collection causes those additions to not be saved in the view state for the DropDownList. Rather, we need to first add the DropDownList to the Controls collection and then bind the database data to the list. The order matters because we don't want to bind the data to the DropDownList until after we've started tracking view state.

To summarize, the correct code should look like:

public class MyControl : WebControl, INamingContainer
{
...

protected override void CreateChildControls()
{
DropDownList ddl = new DropDownList();
Controls.Add(ddl); // Add the control FIRST

if (!Page.IsPostBack) // Bind data AFTER
// do data binding to some database...
}
}

Partial credit goes to (IIRC) Alex Lowe. If I'm remembering correctly, way back in (yikes!) 2001 he tech reviewed ASP.NET: Tips, Tutorials, and Code for which I authored some chapters, and on one of the chapters I had an example of adding dynamic controls to a page and - again, if my memory doesn't fail me - he noted that you wanted to first add the control to the page prior to setting properties that needed to be maintained in the view state.

RssFeed Bug Found and Squashed
01 October 04 06:11 PM | Scott Mitchell

Alert RssFeed user Kenneth Ekman identified a bug in RssFeed when displaying RSS feeds whose <pubDate> dates were not given in GMT. My mistake was to parse the <pubDate> string using the DateTimeFormatInfo's “r” format specifier, which looks deceptively similar to the RFC 822's specification for dates. One oversight on my part, though, was that the “r” format specifier requires that the date be in GMT, whereas RFC 822 allows for GMT (or UT) along with common U.S. time zones (EST, EDT, CST, CDT, MST, MDT, PST, PDT) and military time to boot. Problem was, feeds using one of these other time zone monikers was causing parsing the date to barf, which would default the date displayed to the current date and time.

Anywho, I fixed the problem by using the <pubDate> parser in RssBandit. Props to Dare Obasanjo (and the other contributors) for the code and for the great program that is RssBandit. The latest version of RssBandit can be downloaded from the RssFeed GotDotNet Workspace. Thanks to those who have used RssFeed, there's been over 1,200 downloads to date!

For those who don't know, RssFeed is an open-source, custom ASP.NET server control for displaying RSS feeds. Check out a live demo!

Filed under:
More Posts Next page »

Archives

My Books

  • Teach Yourself ASP.NET 4 in 24 Hours
  • Teach Yourself ASP.NET 3.5 in 24 Hours
  • Teach Yourself ASP.NET 2.0 in 24 Hours
  • ASP.NET Data Web Controls Kick Start
  • ASP.NET: Tips, Tutorials, and Code
  • Designing Active Server Pages
  • Teach Yourself Active Server Pages 3.0 in 21 Days

I am a Microsoft MVP for ASP.NET.

I am an ASPInsider.