Sunday, June 15, 2008

I love toys an gadgets!  They are so much fun.  Most of us geeks are gadget guys too.  I just got a GPS for my car, it's so much fun to play with.  Toys that help me get my job done faster and better are really cool.  They help me pay for the other cool toys like GPS's, HD Camcorders, MP3 players, etc.  One of my favorite toys for work is ReSharper, a super Visual Studio add-on that helps out in many ways.  If you aren't using ReSharper, you should check it out.  It isn't really a toy but it makes coding so much more enjoyable and easier, it may as well be a toy.

I've been using ReSharper for a pretty long time, I blogged about it over a year ago.  But in Oct 2007 I started my current job and switched to VS 2008 (it was beta at the time) so I couldn't use ReSharper because it didn't work with LINQ and Lambdas and all of the other cool stuff in VS 2008.  I had hoped that ReSharper 4.0 would have been ready a little sooner since the VS 2008 was officially released quite some time ago, but unfortunately I had to wait.  But now it's here and packed full of cool enhancements.

One of the best features of ReSharper is that you don't need to compile your code to see all of the errors -- not that my code has any errors ;-).   But I wrote about that and some other old features already.  Here's some new features:

  • ReSharper fully supports LINQ and the other language enhancements.
  • ReSharper provides great code completion/intellisense in aspx pages (source view)! 
  • Also, ReSharper will create method stubs from an ASPX page source view - so if I type OnCommand="foo", ReSharper can create:

protected void foo(object sender, CommandEventArgs e)
{
     throw new NotImplementedException();
}

  • With "CamelHumps", you can abbreviate class names and method names and let ReSharper complete the word.  So if I have a method named GetAllDuplicates(), I can just Type GAD!

So far, I have only one complaint but it is really no big deal.  ReSharper wants to convert a lot of my local variables to the new variant type - var.  I use the var type in code where I want it, but I don't believe it should be over used like this.  If a variable is a string and I know it is a string, it should be typed as such, as a string.  ReSharper offers the suggestion to convert it to a var.  But the cool thing about ReSharper is that I can tell it I'm not interested in this and it won't bother me any more!

Believe me, there are a ton of other great features.  Just download a trial version or ReSharper 4.0 for yourself and check it out.

 

Sunday, June 15, 2008 9:09:56 PM (Eastern Standard Time, UTC-05:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Wednesday, January 23, 2008

We had a great Code Camp on Jan 12.  I've got a longer blog post planned to summarize the event.  I'm gonna work on that next.  But I promised Marc that I'd post his slides from his presentation "Building ASP.NET Dynamic Data Web Sites using LINQ for SQL".  And to be fair, he sent me this stuff on Jan 14 and I'm finally posting it today.  Sorry to those of you who were waiting for it.  I sat in on this presentation and it was a lot of fun and full of content.  Thanks Marc!

 

He was kind enough to create two versions of the slides:

Slide Deck for Power Point 2003 (.ppt)

Slide Deck for Power Point 2007 (.pptx)

Wednesday, January 23, 2008 9:42:21 PM (Eastern Standard Time, UTC-05:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Monday, January 07, 2008

From now on I will take my own advice.  I spent a good part Friday afternoon and Monday morning trying to figure out what was wrong with my code and I don't want to do that again!  The problem I was having was that I had an ASP.Net control that would not postback, no matter what I tried.

I have a page with several user controls on it.  One of these user controls includes an asp:Textbox that is hooked to some JavaScript so that when the use clicks "enter" in that control a postback will occur.  The postback is accomplished by some JavaScript that calls the click event on a button that is hidden (style="display:none").  But the postback wouldn't work.  I put a few alerts in the JavaScript and I knew it was getting called, but the postback still never occurred.  The problem was not so easy to see.  That's because I had a second user control on the page.  That second user control has an asp:RequiredFieldValidator in it.  To complicate things, that validation control was tucked in a modal popup (using the Ajax Control Toolkit's Modal Popup Extender).  But, I forgot to include the ValidationGroup property on the validator.  So any control on the page would cause validation to occur which will block the postback if validation fails.  But since the validator was in the popup (which of course, was not currently popped up), I didn't see the error message that was trying to tell me there was a problem.

The problem was easily fixed (if not easily found) by simply adding the ValidationGroup property to the validator and of course, matching it up on the button that is supposed to call that validation group.  Since the hidden button on my other control was not included in the same validation group, it was free to postback any time it wants.

Monday, January 07, 2008 10:59:52 PM (Eastern Standard Time, UTC-05:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Friday, December 07, 2007

Yesterday at Orbius we upgraded from Visual Studio 2008 Beta 2 to RTM.  For the most part things went pretty well.  This isn't something we can do without a plan. We've been using VS2008 since May.  Our entire code base would need to migrate smoothly.  By the end of the day we were happily running VS2008 RTM! 

Things went pretty smoothly, but this was an interesting warning from the .Net Framework 3.5 installer.  It was good for a laugh and we were able to move past it.

DotNetInstallWarning

One of the guys  migrated his laptop to RTM and compiled our VS2008 solution.  There weren't too many compiler errors, although we did have some problems with some of the LINQ code our projects.  I didn't get all of the specifics, but I think it had to do with the fact that we use our DataContexts in a very disconnected way.  We get data, pass it around, modify it and eventually update the database using a different DataContext. Anyway, he came up with a work around.   There might have been some other problems but I figure if he didn't mention them, they couldn't have been too bad.  He checked the solution into SVN.

Once running the solution (web application), this was a popular message:

An item placeholder must be specified on ListView 'membersView'. Specify an item placeholder by setting a control's ID property to "itemPlaceholder". The item placeholder control must also specify runat="server".

That is because they changed the ListView (the ListView rocks, by the way) a bit.  It used to be you could put ListView content in any old Server Control.  Now it must be within an asp:PlaceHolder. It's an easy change, although we have lots of ListViews in our Solution.  Luckily, Matt on our team made all those changes. That's about it.  I don't recall too many other issues.

When I upgraded my laptop to RTM it took a while.  We followed ScottGu's advice and completely uninstalled all Beta 2 applications and components first.  I uninstalled VS2008 Beta 2 and then followed the list of programs to uninstall (the list is in the links below).  Out of about 20 items on the list, I found about 7 that I needed to manually remove.  One catch...remember to uninstall accessories first (such as ReSharper and GhostDoc), just in case.  There were a few glitches that I wouldn't expect.  Such as a warning to close an application that was blocking the install - only the blocking application was the installer itself!   Dean got a similar warning while installing the .Net Framework 3.5 on our server and he gave me the image from the top of this post.

Here are some links that may help:

Friday, December 07, 2007 7:48:14 PM (Eastern Standard Time, UTC-05:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, December 03, 2007

I had a great time at the Central Penn .Net's Code Camp.  Judy and her team put together a great event.  I did my presentation on Creating Custom Server Controls.  The crowd out there was great.  They seemed really enthusiastic.  If you are interested in my code sample, you can get the VS 2005 solution file here (zipped).

Monday, December 03, 2007 6:38:06 PM (Eastern Standard Time, UTC-05:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, November 20, 2007

Philly.Net's November meeting is a return to the "15 Minutes of Fame" format.  We had 10 speakers each providing a 15 minute presentation.  Sort of a geek sampler.  In addition to my duties as Swag Man (if you don't know, I gather all of the prizes), I'm doing a 15 minute presentation on LINQ. 

With 15 minutes, I'm not getting into too much detail.  Just a bunch of cool code sample to show what LINQ is capable of.  Mostly, LINQ to SQL.  If you are interested, here is the code sample solution.

If you want to learn more about LINQ, here are some site to check out:

Scott Guthrie's Blog:  http://weblogs.asp.net/scottgu/  (Of Course!)

Wes Dyer's Blog: http://blogs.msdn.com/wesdyer/  (this guy knows his stuff on LINQ!)

Check out this great video by Wes.  It explains a lot, it is definitely worth watching:  http://wm.microsoft.com/ms/msdn/visualcsharp/wes_dyer_2007_01/WesDyer_0002.wmv

LINQDemo.zip (94.67 KB)
Tuesday, November 20, 2007 5:50:50 PM (Eastern Standard Time, UTC-05:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, November 15, 2007

I learned something new today, thought I'd share it.  Actually, I think I relearned it, it sounded familiar.  There are so many features to .Net these days it is tough to remember everything.  Anyway, here we go.

Did you know that you can put content directly inside a ContentPlaceHolder of a MasterPage?  I'm not talking about in a page's asp:Content control that matches up to the Master's asp:ContentPlaceHolder.  That is what we typically do.  But you can put content in the asp:ContentPlaceHolder control on the master.  I like to think of it as default content.  In the content page, if you put content inside the asp:Content control, it will override the "default" value from the Master.  If not, the Master's content will be shown.  Content, Content Pages, asp:Content, asp:ContentPlaceholders...confused?  Here's a simple example:

Here is some code from body of our MasterPage.  I'm only including the body to save space.  We have 2 contentplaceholders.  The first has some "default text"

<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
            By Default, show this.
        </asp:ContentPlaceHolder>
    </div>
    <div>
        <asp:ContentPlaceHolder ID="ContentPlaceHolder2" runat="server">
        </asp:ContentPlaceHolder>
    </div>
    </form>
</body>

Now let's look at our first ContentPage.  It puts text in each of the Content areas:

<%@ Page Language="C#" MasterPageFile="~/Site1.Master" AutoEventWireup="true" 
CodeBehind="WebForm1.aspx.cs" Inherits="MasterPageSample.WebForm1" 
Title="Untitled Page" %>

<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
    content in placeholder 1
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder2" runat="server">
    content in placeholder 2
</asp:Content>

Next comes our second ContentPage.  This one only has one asp:Content control to match the second ContentPlaceHolder from the MasterPage:

<%@ Page Language="C#" MasterPageFile="~/Site1.Master" AutoEventWireup="true" 
CodeBehind="WebForm2.aspx.cs" Inherits="MasterPageSample.WebForm2" 
Title="Untitled Page" %>

<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder2" runat="server">
    content in placeholder 2
</asp:Content>

The last ContentPage has 2 asp:Content controls but only the second one has actual content. The first one is empty:

<%@ Page Language="C#" MasterPageFile="~/Site1.Master" AutoEventWireup="true" 
CodeBehind="WebForm3.aspx.cs" Inherits="MasterPageSample.WebForm3" 
Title="Untitled Page" %>

<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder2" runat="server">
    content in placeholder 2
</asp:Content>

So what are the results?  The first page renders both pieces of content:

image

The second one shows it's content and since the first ContentPlaceholder wasn't matched up with a Content control, it shows the content from the MasterPage:

image

The last page has both ContentPlaceHolders matched up, only the first has no content at all.  So in this case, it overrides the "default" content from the master with nothing!  All you get is the content from the second Content control:

image

Pretty cool, huh?

Thursday, November 15, 2007 8:51:40 PM (Eastern Standard Time, UTC-05:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Thursday, October 25, 2007

I haven't mentioned this recently, there is a NoDeNUG meeting tonight.  As one of the founders, I'm supposed to help promote the group but for some reason I forgot to mention this meeting on my blog.  What makes that even stranger is that I am the presenter tonight!  So if you are in the area, come out tonight to learn about Creating Custom Server Controls.  It's free, and we'll have pizza.

If you want to know more about these meetings, don't rely on my blogging them.  Go to NoDeNUG.org and sign up.  There's no obligation of any kind and you'll get our monthly meeting reminders via email.

Thursday, October 25, 2007 6:05:48 AM (Eastern Standard Time, UTC-05:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, October 17, 2007

 

Tonight's meeting was sponsored by RDA Corporation.  They provided our dinner (pizza) as well as a $50 Gift Card to Circuit City as a give away!  Making it's first appearance at a Philly.Net meeting was our new coffee maker!  We bought it for Code Camp but now we'll have coffee for our regular meetings.  Enjoy it!  Thanks to Rob for running tonight's meeting.  Bill was out at Microsoft in Redmond (supposedly he's working) so Rob stepped up to run the show and make announcements.

Our next meeting is November 20th.  We are bringing back the "15 minutes of Fame" format that we used last November.  That means we'll have about 9 presenters, each doing a brief 15 minute presentation.  Last year this worked out really well and we got a great response.  If you are interested, please contact Bill or myself. 

UPDATE:  Both of the presenters are from RDA Corporation.  Check out their Technology Corner for information on these presentations and more.  Or click directly to Building an Extranet with Forms-Based Authentication using WSS 3.0/MOSS 2007 or Visual Studio 2005 Tips and Treats

Presenter:  Deepak Gupta, RDA Corporation
Topic:  Building an Extranet with Forms-Based Authentication using WSS 3.0/MOSS 2007

Sorry, I can't summarize this session.  I was busy with Philly.Net business and I missed the whole thing.  It's too bad because I was told that Deepak really knows his stuff on Sharepoint!

Presenter:  Steve Andrews, RDA Corporation
Topic:  Visual Studio 2005 Tips and Treats

Steve started by promising that these tips would be posted here on his blog.  It's a good thing too because he jumped right in with a ton of tips and I can't remember them all.  He started by showed how to customize VS menus and toolbars with a variety of features that aren't there by default.  Then he showed all kinds of shortcuts and settings that I plan on using tomorrow at work.  I've been using Visual Studio since 2002 and I didn't know many of these little tidbits.  The crowd contributed a few tips too and I'm certain that everyone learned something tonight.  As a bonus, Steve did a quick demo of Lutz Roeder's awesome Reflector tool.

We wrapped up with prizes from RDA, O'Reilly, Wrox, Sams, and SourceGear!

Wednesday, October 17, 2007 7:20:14 PM (Eastern Standard Time, UTC-05:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Wednesday, October 03, 2007

Note:  I'm having some problems formatting the code samples inserted into this post.  I write my posts with LiveWriter.  The first few samples are inserted with a pluggin, VSPaste which always worked great for me.  But today the line breaks keep getting removed.  For the larger sample below, I tried to use another pluggin called Code Snippet.  It kept the line breaks but now my indentations are removed! What a pain.  Not sure if the problem is LiveWriter or the addins.  I'll update this when I figure it out.  Anyway...

 

Yesterday I wrote my first extension method and I thought it would make for a good post.  Ironically, yesterday ScottGu posted an extension method sample on his blog.  Are we thinking alike?  Wishful thinking by me, I guess!  Extension methods are one of the cool features you get with .Net 3.0.  You can use them to add functionality to an existing class without extending the class itself, even if the class is sealed.

Before I show my real extension method, here's a simple example for fun.  Extension methods are pretty easy to create.  This method that returns a bool if a date is my birthday:

 public static bool IsMyBirthday(this DateTime date) { if (date.Month == 9 && date.Day == 21) return true; return false; }

The unusual part is  (this DateTime date).  This parameter is the part that hooks this extension method with the Object, in this case a DateTime.  And it acts like a regular parameter because inside the method I can refer to date to get the value.

It gets called as follows:

 DateTime myDate; if (myDate.IsMyBirthday()) return true;
or even:
 if (DateTime.Now.IsMyBirthday()) return true;

The extension method automatically attaches itself to instances of DateTime!  Of course, you have to include a reference to the namespace where the method lives.

OK, how about some real code.  My TimeAgo extension method extends DateTime and returns a string that represents the DateTime compared to Now as "5 minutes ago" or "2 weeks ago", etc.  You could do a lot more with this method.  I'm not getting too carried away with the logic for what is "yesterday" or "last month" and I am not considering time zone differences.  I'm keeping it simple, hopefully you get the idea.  Here is the code and also a test console app:

namespace Utilities
{
 public static class DateExtension
 {
 public static string TimeAgo(this DateTime date)
 {
 TimeSpan timeSince = DateTime.Now.Subtract(date);
 
 if (timeSince.TotalMilliseconds < 1)
 return "not yet";
 
 if (timeSince.TotalMinutes < 1)
 return "just now";
 if (timeSince.TotalMinutes < 2)
 return "1 minute ago";
 if (timeSince.TotalMinutes < 60)
 return string.Format("{0} minutes ago", timeSince.Minutes);
 if (timeSince.TotalMinutes < 120)
 return "1 hour ago";
 if (timeSince.TotalHours < 24)
 return string.Format("{0} hours ago", timeSince.Hours);
 if (timeSince.TotalDays == 1)
 return "yesterday";
 if (timeSince.TotalDays < 7)
 return string.Format("{0} days ago", timeSince.Days);
 if (timeSince.TotalDays < 14)
 return "last week";
 if (timeSince.TotalDays < 21)
 return "2 weeks ago";
 if (timeSince.TotalDays < 28)
 return "3 weeks ago";
 if (timeSince.TotalDays < 60)
 return "last month";
 if (timeSince.TotalDays < 365)
 return string.Format("{0} months ago", Math.Round(timeSince.TotalDays / 30));
 if (timeSince.TotalDays < 730)
 return "last year";
 //last but not least...
 return string.Format("{0} years ago", Math.Round(timeSince.TotalDays / 365));
 }
 }
 class Program
 {
 static void Main(string[] args)
 {
 DateTime date;
 
 date = DateTime.Now.AddSeconds(1);
 Console.WriteLine(date.ToLongDateString() + " Time ago: " + date.TimeAgo());
 date = DateTime.Now.AddSeconds(-5);
 Console.WriteLine(date.ToLongDateString() + " Time ago: " + date.TimeAgo());
 date = DateTime.Now.AddMinutes(-1);
 Console.WriteLine(date.ToLongDateString() + " Time ago: " + date.TimeAgo());
 date = DateTime.Now.AddMinutes(-5);
 Console.WriteLine(date.ToLongDateString() + " Time ago: " + date.TimeAgo());
 date = DateTime.Now.AddMinutes(-59);
 Console.WriteLine(date.ToLongDateString() + " Time ago: " + date.TimeAgo());
 date = DateTime.Now.AddHours(-1);
 Console.WriteLine(date.ToLongDateString() + " Time ago: " + date.TimeAgo());
 date = DateTime.Now.AddHours(-2);
 Console.WriteLine(date.ToLongDateString() + " Time ago: " + date.TimeAgo());
 date = DateTime.Now.AddHours(-23);
 Console.WriteLine(date.ToLongDateString() + " Time ago: " + date.TimeAgo());
 date = DateTime.Now.AddDays(-1);
 Console.WriteLine(date.ToLongDateString() + " Time ago: " + date.TimeAgo());
 date = DateTime.Now.AddDays(-2);
 Console.WriteLine(date.ToLongDateString() + " Time ago: " + date.TimeAgo());
 date = DateTime.Now.AddDays(-7);
 Console.WriteLine(date.ToLongDateString() + " Time ago: " + date.TimeAgo());
 date = DateTime.Now.AddDays(-14);
 Console.WriteLine(date.ToLongDateString() + " Time ago: " + date.TimeAgo());
 date = DateTime.Now.AddDays(-21);
 Console.WriteLine(date.ToLongDateString() + " Time ago: " + date.TimeAgo());
 date = DateTime.Now.AddMonths(-1);
 Console.WriteLine(date.ToLongDateString() + " Time ago: " + date.TimeAgo());
 date = DateTime.Now.AddMonths(-11);
 Console.WriteLine(date.ToLongDateString() + " Time ago: " + date.TimeAgo());
 date = DateTime.Now.AddYears(-1);
 Console.WriteLine(date.ToLongDateString() + " Time ago: " + date.TimeAgo());
 date = DateTime.Now.AddYears(-2);
 Console.WriteLine(date.ToLongDateString() + " Time ago: " + date.TimeAgo());
 
 Console.Read();
 
 }
 }
 
}

 

 

Wednesday, October 03, 2007 8:46:24 PM (Eastern Standard Time, UTC-05:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, September 06, 2007

It's been in the works for a while but now it is official.  John Baird, Mitch Ruebush, Doug White and myself have formed NoDeNUG - the Northern Delaware .Net User Group.  We've also got the support of Dani Diaz, our local Microsoft Developer Evangelist.

When:  

  • We'll meet on the last Thursday of the month at 5:30. 

Where: 

Speakers:

  • September 27 - Kevin Goff
  • October 25 - Andy Schwam
  • November 29 - Dani Diaz
  • December - We'll skip that because of the holidays.
  • January - Mitch Ruebush

Sponsors: 

More information will be coming soon. 

Thursday, September 06, 2007 7:21:22 PM (Eastern Standard Time, UTC-05:00)  #    Disclaimer  |  Comments [2]  |  Trackback

I've got some speaking engagements coming up.  It almost sounds like an official tour is planned.

Presentation Topic: Developing Custom Controls for ASP.Net

Description:  In this demo I'll show you how to get started creating your own custom server controls for asp.net.  I'll show how to create controls, expose public properties to make them easier for other developers to implement, and handle events from the control.  We'll  create a ControlDesigner so Visual Studio can render the control at design time.  I'll even build some extra features into the control using Ajax.

Where:

I'm really excited to get out to some of these other groups for these presentations.  If you live near any of these groups, check them out.  They've all got regular, monthly meetings with great .Net content.  I've also got calls in to Madison Square Garden, LA Coliseum, Wembley Stadium and others.  But I haven't heard back from them yet.

Please, no brown M&M's.

Update:  When I get some time I'll be writing the demo out as a tutorial blog post.  In the mean time, if you are interested in the source code for this demo, here it is:

ControlsDemo.zip (1.05 MB)

Thursday, September 06, 2007 11:59:22 AM (Eastern Standard Time, UTC-05:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Friday, August 24, 2007

I recently started playing around with WatiN (Web Application Testing in .Net) and WatiN Test Recorder.  While these are cool tools that can greatly improve the testing process, I found that there isn't a lot of information out there to help get started.  I have put together a brief tutorial on some of the basics.  I hope it helps.  I'm including code samples in this post, plus you can download the entire VS Solution and a short video below.

WatiN, pronounced "What-in", is a library that allows you to create tests for web applications.  It is based in WatiR which was created for Ruby.  Basically, you can test your web pages just like any other classes.  In my case, I'll use NUnit to control the actual tests but they are written with WatiN.  The tests run in the browser via a class named "IE".  That immediately points out one of the downsides to WatiN -- it only works with Internet Explorer.  But the tests run in a real browser so it is very accurate.

Here's a quick code sample:
            IE ie = new IE("http://localhost/somewebsite/default.aspx");
            ie.Link(Find.ById("LinkButton1")).Click();

If you run that in a test, the browser will actually open, navigate to the default page, find the LinkButton and click it.  Seems pretty simple right?  It isn't too complicated but you'll have to get used to the WatiN classes and methods.  The fact is, the tests are a pain to write.  That's where WatiN Test Recorder comes in.  All you have to do is start the recorder and the tool will begin to record your actions in the browser (there is a browser built into WatiN Test Recorder) and translate your actions into code.  If you click LinkButton1, the recorder outputs "ie.Link(Find.ById("LinkButton1")).Click();" for you!  This is a big help but it is not exactly perfect.  I have found that it doesn't get the code right and some tweaking is needed.  I'm starting to get the hang of it so the changes to the code are getting easier. 

Sample Code and Downloads:

Download my VS 2005 solution:  WatiNDemo.zip  167KB
Download my Video of WatiN Test Recorder in action:  WatiNTestRecorderDemo.avi (Right click and "save target as")  2.22MB

I created a simple data entry web application with 2 pages that pretends to register users to a web site for outdoor enthusiasts.  The first page lists the registered users, the second page is a form to register:

image image

I want to make sure my data entry page works, so I fire up WatiN Test Recorder.  The video (see the link above) shows what happens in the recorder as I step through the page  and enter the data into the web form.  Here is the code that the recorder generated for me, with line numbers added here for reference:

          1   IE ie = new IE("http://localhost:49573/RegisteredList.aspx");
          2   ie.Link(Find.ById("LinkButton1")).Click();
          3   ie.TextField(Find.ById("txtFirst")).Click();
          4   ie.TextField(Find.ById("txtLast")).Click();
          5   ie.TextField(Find.ById("txtLast")).TypeText("andy");
          6   ie.TextField(Find.ById("txtEmail")).TypeText("schwam");
          7   ie.TextField(Find.ById("txtEmail")).Click();
          8   ie.TextField(Find.ById("txtEmail")).TypeText("andy@email.com");
          9   ie.RadioButton(Find.ByName("rdoSendInfo_0") && Find.ByValue("yes")).Checked = true;
          10  ie.TableCell(Find.ByCustom("innertext", " Choose New Jersey Pennsylvania")).Click();
          11  ie.SelectList(Find.ById("ddlState")).SelectByValue("Pennsylvania");
          12  ie.SelectList(Find.ById("ddlCounty")).Click();
          13  ie.SelectList(Find.ById("lstHobbies")).Click();
          14  ie.SelectList(Find.ById("lstHobbies")).Click();
          15  ie.SelectList(Find.ById("lstHobbies")).Click();
          16  ie.SelectList(Find.ById("lstHobbies")).Click();
          17  ie.Button(Find.ById("btnSubmit")).Click();
          18  ie.Link(Find.ByUrl("javascript:__doPostBack('GridView1','Select$0')")).Click();
          19  ie.Link(Find.ById("LinkButton1")).Click();

Fixing the Generated Code:

  • Line 1 and 2 are good:  Open Internet Explorer and navigate to my page.
  • Line 3, 4, 7, and 10 are not really needed but it won't cause any trouble.  The clicks seem to be irrelevant.
  • Line 5 and 6 are just wrong.  I put the text "andy" in the textbox named txtFirst, and "schwam" in txtLast.  I can easily fix them:  
    • ie.TextField(Find.ById("txtFirst")).TypeText("andy");
    • ie.TextField(Find.ById("txtLast")).TypeText("schwam");
  • Line 9 throws this exception if you let it run: "WatiN.Core.Exceptions.ElementNotFoundException: Could not find a 'INPUT (radio)' tag containing attribute name with value 'rdoSendInfo_0'".  It should be Find.ById, not Find.ByName.  This works instead:
    • ie.RadioButton(Find.ById("rdoSendInfo_0")).Checked = true;
  • Line 11 is good
  • Line 12.  Notice no county is selected. Try this:
    • ie.SelectList(Find.ById("ddlCounty")).SelectByValue("Delaware");
  • Lines 13, 14, 15, and 16 are just clicks but nothing is selected.  Try this:
    • ie.SelectList(Find.ById("lstHobbies")).SelectByValue("Hiking");
    • ie.SelectList(Find.ById("lstHobbies")).SelectByValue("Camping");
    • ie.SelectList(Find.ById("lstHobbies")).SelectByValue("Mountain Biking");
    • ie.SelectList(Find.ById("lstHobbies")).SelectByValue("Kayaking");
  • Line 18 is correct but worth mentioning.  This selects the first row in the grid which causes the application to display the users detail in the grid.

That doesn't seem to good for a first shot!  But even with all of the mistakes, I'd rather start with the generated code then nothing at all, especially if this was a longer, more complicated test.  Plus, I admit there are features of the recorder that I am not familiar with so maybe it would work better if I actually knew what I was doing! 

AJAX Issues:

Technically, the changes I made are correct but it still won't work right.  I am using ASP.Net Ajax to call back to the server when the state (ddlState) changes.  I'm populate a list of counties (ddlCounty) on the server.  When you run the test it goes so fast that it tries to select the county before the drop down list is loaded!  I found some documentation about a WaitUntil() method but I couldn't get it to work.  So I did it the old fashioned way and put the thread to sleep:

            ie.SelectList(Find.ById("ddlState")).SelectByValue("Pennsylvania");
            System.Threading.Thread.Sleep(100);
            ie.SelectList(