Friday, June 19, 2009

C# Html Screen Scraping Part 2 / Performing POST with Cookies

Source code: http://www.box.net/shared/r7u052y507

I just want to point out, that I purposely didn't break this out into separate classes and methods, and I know I'm duplicating ALOT of code. I simply wanted to demonstrate each technique on its own.


In my previous post, I demonstrated how to connect to a website, and download the HTML (aka Screen Scraping). That all works nicely if it's a simple site you need to connect to. Sometimes however, you can't simply connect to the site and download the HTML, rather you need to first login, or maybe you need to enter some kind of search term first into a text box. In the HTML world, generally the page will have a simple form that posts to the server which queries the database or something based on the info supplied in the post, and then dynamically builds up the page. How do you do that in your C# app? How do you pass the values on to the Form Post that the server is expecting?

Throughout this post, I'll be referring to the code that's attached to this post. It basically has two projects. One's a simple ASP.NET MVC website, and the other is a winforms app. In order to run this properly, you'll need to first launch the web app, and then launch the windows app. Here's a simple screen shot of what the windows app looks like so you can get an idea:



First, a little note. In order to demonstrate this, I needed a site that had a simple Form with cookies. Since I couldn't find anything that was really simple and that would be easy to demo, I decided to create my own little "Website". It's written in ASP.NET MVC, so if you want to be able to run the code sample supplied in the link, head over to the ASP.NET MVC Website and download it (if you don't already have it.)

The site basically has two URL's that are of interest. The first is ../Home/SimplePost. If you navigate to that page, you'll see a simple textbox with a button. When you click the button, it simply posts the text in the textbox back to the server, and then it just outputs it back to the browser. Here's the HTML rendered for that form:

<form action="/Home/SimplePost" method="post">
<input type="text" id="text" name="text" />
<input type="submit" value="submit" />
</form>

The Form will Post to a site on the server /Home/SimplePost. We also can see in the form, that the server is expecting a parameter that's called "text". It's safe to assume, that anything with an input field (except for the button) is needed by the server. So, now we have enough info to write our C# function:



private void PostWithoutCookies()
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(
String.Format("http://localhost:{0}/Home/SimplePost", port));
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
string postData = String.Format("text={0}", String.IsNullOrEmpty(textBoxPost.Text)
? "somerandomemail@address.com" : this.textBoxPost.Text);
byte[] bytes = Encoding.UTF8.GetBytes(postData);
request.ContentLength = bytes.Length;

Stream requestStream = request.GetRequestStream();
requestStream.Write(bytes, 0, bytes.Length);

WebResponse response = request.GetResponse();
Stream stream = response.GetResponseStream();
StreamReader reader = new StreamReader(stream);
stream.Dispose();
reader.Dispose();
this.richTextBox1.Text = reader.ReadToEnd();
}


(This is all part of the app that's attached at the top of this post. It basically outputs all the results to a richtextbox.)

A bit more complicated than last time, but not as bad. The main difference here is that we'll actually be writing TO the Request stream. This will insert the form values into the headers, which will allow the server to receive this data and process it. The trick is that for each value that you need to add, you use this syntax:

field1=value1&field2=value2&field3=value3

where field is the name of the input field, and the value is the value you want to send over to the server (ie. the text that would be entered into the textbox).

So if we were to run this method now, we'd see the text that we posted to the server (the text that was in the textbox of the app) in the richtextbox.

There is one more thing that we can do with this. Very often, in order to access certain areas of a site, you need to first log in. When you login, the server sends a cookie to the browser, and then for each subsequent request that is for authenticated users only, the browser send the cookie back to the server so that you can access those parts of the site. Here, we're acting like a browser, so we need to have the ability to get the cookie, retain it somehow, and then pass that on to the next request.

To demonstrate this, in the web app of this demo, there's a page called "..Home/PostWithCookie". When you access this page, it sends a cookie to the browser. Then, on that page there's a form identical to the first one. When you post back to the server though, it checks if the cookie is there. If it is, it outputs "Cookie Found" along with the cookie value, if not, it outputs "Cookie not found."

So back in our Windows App, we need a way to first access that first page that gets us our cookie, then we need to GET the cookie, and finally we need to pass the cookie on with the form post. Here's the code:



private CookieCollection GetCookies()
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(
String.Format("http://localhost:{0}/Home/PostWithCookie", port));
request.CookieContainer = new CookieContainer();
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
return response.Cookies;
}

private void PostWithCookies()
{
CookieCollection cookies = this.GetCookies();
var request = (HttpWebRequest)WebRequest.Create(
String.Format("http://localhost:{0}/Home/PostWithCookie", port));
request.CookieContainer = new CookieContainer();
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
string postData = String.Format("text={0}", String.IsNullOrEmpty(textBoxPost.Text)
? "somerandomemail@address.com" : this.textBoxPost.Text);
byte[] bytes = Encoding.UTF8.GetBytes(postData);
request.ContentLength = bytes.Length;
if (cookies != null)
{
request.CookieContainer.Add(cookies);
}

Stream requestStream = request.GetRequestStream();
requestStream.Write(bytes, 0, bytes.Length);

var response = request.GetResponse();
var stream = response.GetResponseStream();
var reader = new StreamReader(stream);
this.richTextBox1.Text = reader.ReadToEnd();
}


The first bit of code, the GetCookies method looks JUST like the original Screen Scrape method, however here we're actually grabbing the cookies. The trick is to new up a new CookieContainer before we do the request. Once we have a container, and we execute the request, we can get the cookies out of the response.

Now we have the cookie, but we aren't done. We want to pass this cookie back to the server when we post to the form. The only difference again here is that we have to new up a CookieContainer on the request, and add the cookies to that container. Once it's there, when you execute the POST, the cookies will get sent over as well.

The only way to really understand this all, is to download the sample, and mess with it. It's not very complicated, but you need to just mess with it a bit to understand. Once you do grasp it though, you'll see just how powerful this is. You can access many websites straight from within your app, and get the data right into your application.

C# Html Screen Scraping Part 1

This is the first post of a two part series.

Source Code: http://www.box.net/shared/i2p7t9kxkt

Very often, when a particular website has some information you'd like to use in your application, you'd see if they have some kind of API which you can use to query their data. However, it's very common for a website either to not have an API altogether, or not have that little bit of info you need made available in their API. What's done generally to get around this is a technique knows as "Screen Scarping". (Screen Scraping is a general term, not just for the web, but for the purpose of this blog, when I say Screen Scarping, I mean HTML Screen Scraping).

The general gist of it is this: when a browser contacts a site, an HTML document is sent back to the browser. The browser then has the (tedious) task of parsing out that HTML and rendering it out to the screen. End of the day though, the HTML is just a text file. What screen scraping basically is, you write an app that "acts" like a web browser, meaning it contacts the web site, downloads the HTML file into memory, at which point you're free to parse it out any which way you like and extract the data you need. In .NET this is incredibly easy to do, and I'll demonstrate a simple sample:


private string GetWebsiteHtml(string url)
{
WebRequest request = WebRequest.Create(url);
WebResponse response = request.GetResponse();
Stream stream = response.GetResponseStream();
StreamReader reader = new StreamReader(stream);
string result = reader.ReadToEnd();
stream.Dispose();
reader.Dispose();
return result;
}


Yup, that's pretty much it. First, you create a WebRequest object with the given URL. Then, you get a Response object out of that Request. Finally you get the response stream and read it with a StreamReader.

I attached a simple app so you can give it a whirl. Basically, it's a simple windows app with a textbox and a button. Enter any url in the textbox (make sure to write the full url including http://....) and hit the Go button. That will get you the entire HTML of that site, and display it in the richtextbox.

This is obviously a rough sample, make sure to add proper error handling, but other than that, it's pretty straightforward and real simple! The only thing to watch out for when scraping, is that your parsing code will rely on the HTML being formatted a VERY specific way. If the site changes in any way, your code WILL break.

This was a very simple post; in the next post, I'll take this much further, and demonstrate how we can actually POST to a server, and even get the cookies.

Monday, June 15, 2009

INotifyPropertyChanged - How to and when to?

Source Code: http://www.box.net/shared/nx8uj1rm1b

Databinding has always been fascinating to me; at first it really all looks like magic, how controls just track changes, and keep everything in sync. So much so, that very often I try to use it as little as possible, because I don't like using things I don't fully understand how they work. However, it does often save you from a lot of extra code, so I think it's very useful to explore it a bit, in particular the INotifyPropertyChanged interface.

Let's first start with a quick demo. Say you have a class, let's call is MyClass:



public class MyClass
{
public int MyProperty { get; set; }
}


Now let's say we have a Windows Form that has a DataGridView that we're using to bind to a BindingList of this type:



BindingList<MyClass> bindingList = new BindingList<MyClass>();
private void Bind()
{
bindingList.Add(new MyClass { MyProperty = 10 });
bindingList.Add(new MyClass { MyProperty = 20 });
bindingList.Add(new MyClass { MyProperty = 30 });
bindingList.Add(new MyClass { MyProperty = 40 });
this.dataGridView1.DataSource = bindingList;
}


This will work, and you'll have a DataGridView with one column called MyProperty with the rows 10,20,30,40. Very nice. Suppose however, that somewhere in the application, the contents of this list change (new values from database, or user input updated data, whatever..). You'll notice, that if your code applies changes directly to the BindingList, it will NOT reflect in the DataGridView:



public void UpdateList()
{
foreach (MyClass mc in this.bindingList)
{
mc.MyProperty = 100;
}
}


Just some simple code that changes all the MyProperty's to 100. You'll notice that these changes will NOT be reflected in the DataGridView. Well, the question becomes, how then do we have the changes reflected in the DataGridView? You COULD set the DataGridView's datasource to null and then rebind, but that's hacky and clumsy.

Enter the INotifyPropertyChanged interface. The interface has one property on it, and it's actually an event:



public interface INotifyPropertyChanged
{
// Summary:
// Occurs when a property value changes.
event PropertyChangedEventHandler PropertyChanged;
}


Looks quite simple, let's implement this now properly with a Person class. I've attached the source code to this post so you can download it and see for yourself. Here's the entire Person class:



public class Person : INotifyPropertyChanged
{

private string firstName;
private string lastName;
private int age;

public Person(string firstName, string lastName, int age)
{
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}

public Person()
{

}

protected virtual void OnPropretyChanged(string propertyName)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}


public string FirstName
{
get
{
return this.firstName;
}
set
{
if (this.firstName != value)
{
this.firstName = value;
this.OnPropretyChanged("FirstName");
}
}
}

public string LastName
{
get
{
return this.lastName;
}
set
{
if (this.lastName != value)
{
this.lastName = value;
this.OnPropretyChanged("LastName");
}
}
}

public int Age
{
get
{
return this.age;
}
set
{
if (this.age != value)
{
this.age = value;
this.OnPropretyChanged("Age");
}
}
}

#region INotifyPropertyChanged

public event PropertyChangedEventHandler PropertyChanged;

#endregion
}


The first thing you'll notice is that I implemented INotifyPropertyChanged. Then, in each setter of the properties, I call a method called "OnPRopertyChanged" which actually raises the event. The only downside of this, is that you can't use auto properties because the setters need to have logic. Also, since we'll be raising an event every time the value changes, it's worth putting in the extra if check:


if (this.firstName != value)
{
this.firstName = value;
this.OnPropretyChanged("FirstName");
}


just to make sure that the value actually changed.

I then created a simple Form that has a DataGridView that's bound to a BindingList<Person>. It also has a button, that when clicked, adds 5 years to everyones age.



public partial class Form1 : Form
{

private BindingList<Person> people;

public Form1()
{
InitializeComponent();
this.people = new BindingList<Person>();
this.PopulatePeople();
this.dataGridView1.DataSource = people;
}

private void PopulatePeople()
{
this.people.Add(new Person("Alex", "Friedman", 27));
this.people.Add(new Person("Jack", "Bauer", 45));
this.people.Add(new Person("Tony", "Almeda", 39));
this.people.Add(new Person("Chloe", "O'Brien", 37));
this.people.Add(new Person("Bill", "Buchanan", 50));
}

private void ChangeAges()
{
foreach (Person p in this.people)
{
p.Age += 5;
}
}

private void buttonChange_Click(object sender, EventArgs e)
{
this.ChangeAges();
}
}


If you run this, you'll notice that DataGridView does in fact get updated, even though I just manipulated the underlying list. I never actually touch the DataGridView itself. Internally, the BindingList checks to see if the class of type T (remember, BindingList is generic) implements INotfiyPropertyChanged. If it does, it hooks into that event, and when the event is raised, it updates itself.

The one thing to be careful is that when raising this event, you actually hardcode the name of the property. Personally, I wish there was a better way, but anything I've seen online so far had some serious overhead (like using the StackFrame to figure out which property is changing) so if someone knows of a better way, please let me know!

UPDATE: I recently blogged about a better approach to dealing with the INotifyPropertyChanged event that takes care of the hardcoded strings issue. Check it out here.

Thursday, June 11, 2009

Tweaking a DataGridView ComboBoxColumn to allow editing.

Let me first say this. I love the DataGridView and I think it's an awesome and very versatile control. It's highly customizable, and you can create a damn near excel-like application around it. Having said that though, anyone who's ever used a DataGridView knows just how "quirky" the stupid thing can be. Things that look like they should be simple, require clever hacks to implement.

Recently, I was creating an in-house app here at work, in which I needed a DataGridView with a ComboBoxColumn but with one wrinkle. Out of the box, the ComboBoxColumn doesn't support editing. Meaning, it acts like a ComboBox that has the DropDownStyle set to DropDownList (which doesn't allow the user to enter new values). We, however, did need to have the ability for the user to enter new values. I thought that this would be a simple property that I'd be able to set on the ComboBoxColumn. Yea, well there is no such property, so queue clever hack!

The first thing you need to do is hook into the DataGridView's EditingControlShowing event. This event fires when the actual ComboBox is "dropped down". The interesting thing is that the EventArgs has a property on it "Control" that can be cast to a standard WinForms ComboBox.

To demonstrate, I've created a simple Windows App that has a button on it and a DataGridView. It also has a method that just returns a list of strings that contains all the days of the week (needed something just for testing). When the button is clicked, the DataGridView gets populated with a ComboBoxColumn with all the days. Here's the entire code:



using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;

namespace DataGridViewComboBoxTesting
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.dataGridView1.EditingControlShowing += HandleEditShowing;
}

private void HandleEditShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
var cbo = e.Control as ComboBox;
if (cbo == null)
{
return;
}

cbo.DropDownStyle = ComboBoxStyle.DropDown;
cbo.Validating -= HandleComboBoxValidating;
cbo.Validating += HandleComboBoxValidating;
}

private void HandleComboBoxValidating(object sender, CancelEventArgs e)
{
var combo = sender as DataGridViewComboBoxEditingControl;
if (combo == null)
{
return;
}
if (!combo.Items.Contains(combo.Text)) //check if item is already in drop down, if not, add it to all
{
var comboColumn = this.dataGridView1.Columns[this.dataGridView1.CurrentCell.ColumnIndex]
as DataGridViewComboBoxColumn;
combo.Items.Add(combo.Text);
comboColumn.Items.Add(combo.Text);
this.dataGridView1.CurrentCell.Value = combo.Text;
}
}

private void button1_Click(object sender, EventArgs e)
{
var cboColumn = new DataGridViewComboBoxColumn
{
Name = "ComboBox",
HeaderText = "Combo Box Column"
};

foreach (var day in this.GetListOfStrings())
{
cboColumn.Items.Add(day);
}

this.dataGridView1.Columns.Add(cboColumn);
}

public IEnumerable<string> GetListOfStrings()
{
foreach (var day in Enum.GetValues(typeof(DayOfWeek)))
{
yield return day.ToString();
}
}
}
}


As you can see, in the HandleEditShowing method, we can get access the the underlying ComboBox itself, and set the DropDownStyle property right there. We then do a little more hackery so that when the user enters a new value, we can add it to all other ComboBox's in that column. We do that by hooking into the ComboBox's Validating event (first we unhook it in case we're already hooked into it, so that our event handler doesn't get called more than once).


private void HandleEditShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
var cbo = e.Control as ComboBox;
if (cbo == null)
{
return;
}

cbo.DropDownStyle = ComboBoxStyle.DropDown;
cbo.Validating -= HandleComboBoxValidating;
cbo.Validating += HandleComboBoxValidating;
}


The event handler then looks like this:



private void HandleComboBoxValidating(object sender, CancelEventArgs e)
{
var combo = sender as DataGridViewComboBoxEditingControl;
if (combo == null)
{
return;
}
if (!combo.Items.Contains(combo.Text)) //check if item is already in drop down, if not, add it to all
{
var comboColumn = this.dataGridView1.Columns[this.dataGridView1.CurrentCell.ColumnIndex] as DataGridViewComboBoxColumn;
combo.Items.Add(combo.Text);
comboColumn.Items.Add(combo.Text);
this.dataGridView1.CurrentCell.Value = combo.Text;
}
}


Simple. We just check if it's already in the list, if it's not then add it to the combo box as well as add it to all other combo boxes.

Here's a link to the source code. In the code I also created a class called DataGridViewComboBoxColumnEx which basically inherits from DataGridViewComboBoxColumn and encapsulates all this hackery nicely. You just use it as you normally would but it allows editing.

Source code: http://www.box.net/shared/73bmzmv1r6

Tuesday, June 9, 2009

How do closures / captured variables work under the hood?

First, a little disclaimer. I'll be using 3.0 syntax with Lambda's but this has been around since .NET 2.0 and anonymous methods.

Ok, so first a little demonstration:



namespace ClosureDemo
{
public delegate void StuffDoer();

class Program
{
static void Main(string[] args)
{
var stuffDoerDelegate = GetDelegate();
stuffDoerDelegate();
stuffDoerDelegate();

Console.ReadKey(true);

}

private static StuffDoer GetDelegate()
{
int counter = 1;
StuffDoer result = () =>
{
counter++;
Console.WriteLine(counter);
};
result();
return result;
}

}
}


Simple little app. First, I create a delegate type called "StuffDoer" that has a return type of void, and takes no parameters. Then, I have a private method that news up a StuffDoer delegate, and attaches an anonymous method to it. Here's the interesting thing to note though: I declared a variable called counter outside of the anonymous method. The obvious question that comes to mind is how? Shouldn't counter be outside of scope? If you didn't declare this as an anonymous method, but rather as a concrete method:



private static StuffDoer GetDelegate()
{
int counter = 1;
StuffDoer result = new StuffDoer(DoStuff);
return result;
}

private static void DoStuff()
{
counter++;
Console.WriteLine(counter);
}


This obviously wouldn't compile because counter is undefined in the DoStuff method. So how can it work in the anonymous method? Furthermore, you'll notice, that if you run this program, it prints out 2, 3 and 4 which shows us that the counter variable somehow stuck around?? How? We declared it once inside our GetDelegate method, shouldn't it have been lost after we left that method? How did it retain it's state?

This is knows as "closures" or "captured variables" and was always just one of those "black magic it just works" kinda thing for me. I researched it, thought I kinda understood it, but never QUITE got it. Recently, however, I picked up a copy of Jon Skeet's C# In Depth book (I CANNOT stress enough how great this book is! I'm only halfway through, didn't even start with the C# 3.0 / .NET 3.5 stuff yet, and already I've learned tons of stuff. I HIGHLY recommend it!) and it opened my eyes as to what exactly is going on under the covers. I'll admit, it may not really matter at the end of the day, you could just stick with the "it just works" attitude, but I like trying to understand how stuff works under the covers.

The first thing to understand is that the compiler is smart. Very smart. It notices that the counter variable is being "captured" and does some funky stuff for us. If you open reflector, you can see what exactly happened here:



You'll notice that there's a class there that I never created. You'll see it there as "<>c__DisplayClass1". And if you look at the code, it looks something like this:



[CompilerGenerated]
private sealed class <>c__DisplayClass1
{
// Fields
public int counter;

// Methods
public void <GetDelegate>b__0()
{
this.counter++;
Console.WriteLine(this.counter);
}
}



Ok, we're getting somewhere; a class was created for us with the counter variable as a public member, as well as a public method that looks just like our anonymous method. Very cool.....but how exactly does that help us? Now, if we look back in our "GetDelegate" method in Reflector, this is what we see:



private static StuffDoer GetDelegate()
{
<>c__DisplayClass1 CS$<>8__locals2 = new <>c__DisplayClass1();
CS$<>8__locals2.counter = 1;
StuffDoer result = new StuffDoer(CS$<>8__locals2.<GetDelegate>b__0);
result();
return result;
}


Looks like a mess with all the compiler generated stuff, but we can finally see the big picture. I'll rewrite the code in "plain english" so you can see what's going on:

First, I create a counter class: (this is instead of the "<>c__DisplayClass1" the compiler generated.)



public class CounterClass
{
public int counter;

public void DoSomething()
{
counter++;
Console.WriteLine(counter);
}
}


Then, back in the Program.cs, let's change the GetDelegate method to this:



private static StuffDoer GetDelegate()
{
CounterClass c1 = new CounterClass();
c1.counter = 1;
StuffDoer result = new StuffDoer(c1.DoSomething);
result();
return result;
}


Here's the key. As I've pointed out in the past, delegates are objects, and can hold references to other objects. So, what's happening here is, an instance of our CounterClass is created, and we pass in one of it's methods to a new instance of the StuffDoer delegate. This now causes the delegate to hold a reference to this CounterClass object. Then, every time after that, when you invoke the delegate, it's still holding a reference to the same object it had in the beginning so you're constantly calling the methods on the same object!

Now it all makes sense; that's how it can reference the counter variable inside the anonymous method, because it's actually a method in a class that's referencing it's own public member. And now also we understand how it maintains state, because it's just a regular object that's being kept around. Smart smart compiler :)

Wednesday, June 3, 2009

Lesson learned....RTFM!

A few weeks ago, I was tasked with writing a site map generator for our new site at work that uses ASP.NET MVC. When using ASP.NET MVC Controllers have "Actions" that map to URL's. For the sake of this blog point, all that's important to know is that generally Actions are methods that return an ActionResult object. So, the idea was, in order to generate the site map correctly, I would use reflection to inspect the entire assembly, and any method that returned an ActionResult would be entered into the site map with the correct XML.

The catch however was that in many instances, the return type of our Action's weren't actually ActionResult objects, but rather classes the derived from ActionResult. (Some that are built into ASP.NET MVC and some custom ones.) So, I quickly realized I needed a method that you can pass in a type, along with another type, and the method would tell you if anywhere up the inheritance chain, type1 inherits from type2.

My first thought was "Perfect! I'll just use recursion!" This quickly got me excited, because it's not every day you get to use recursion in a useful piece of production code, much less when it came to reflection. So, I ended up writing an extension method that crawls up the inheritance chain recursively and it worked perfectly. Here's the code:

public static class TypeExtensions
{
public static bool InheritsFrom(this Type type, Type baseType)
{
if (baseType == null || type == null)
{
return false;
}

if (type.BaseType == baseType)
{
return true;
}

return type.BaseType.InheritsFrom(baseType);
}
}


Basically, the Type class has a handy property called BaseType which basically gives you the BaseType (duh!). If the Type is object, then BaseType is null, in which case we know we've reached the top of the inheritance chain, and we're done. To test this method out, let's whip up some demo code:



public class MyMemoryStreamBase : MemoryStream
{

}

public class MyMemoryStreamChild : MyMemoryStreamBase
{

}

public class MyMemoryStreamGrandChild : MyMemoryStreamChild
{

}
Basically, I've created a nice Inheritance chain that goes up to MemoryStream -> Stream -> MarshalByRefObject and finally object. Here's how you can test it:


Type type = typeof(MyMemoryStreamGrandChild);
bool result = type.InheritsFrom(typeof(MarshalByRefObject));
Console.WriteLine(result);

If you run this, the result will be True, proving that my cute little method worked, and I was all happy.

Fast forward a few weeks, and while browsing StackOverflow, I came across a question regarding reflection and BaseType's when some dude posts a link to this:

Type.IsSubClassOf(..)

Yeah, that's right! It's built right into the .NET Framework! Now obviously it's impossible to know of every method in the framework, but as soon as I thought of the need, and that it involved recursion, I simply whipped out Visual Studio and started coding, without actually taking a moment and thinking, hey research this first! You can't be the FIRST person to come across this very issue!

Feeling completely beat, I was just curious at this point as to how it's implemented in the Framework. Were they also using recursion, or did they have some other ingenious way of doing this. Well here's how they did it:



public virtual bool IsSubclassOf(Type c)
{
Type baseType = this;
if (baseType != c)
{
while (baseType != null)
{
if (baseType == c)
{
return true;
}
baseType = baseType.BaseType;
}
return false;
}
return false;
}

Yea, no recursion, just a simple while loop. At this point, I was just curious in general as to which method is quicker. So, some quick benchmarks:




public void CompareMethodsSpeed()
{
Stopwatch watch = new Stopwatch();
Type type = typeof(MyMemoryStreamGrandChild);
Type baseType = typeof(MarshalByRefObject);
watch.Start();
for (int i = 0; i < 100000; i++)
{
type.InheritsFrom(baseType);
}
watch.Stop();
Console.WriteLine("My way took: {0} ticks.", watch.ElapsedTicks);

watch.Reset();

watch.Start();
for (int i = 0; i < 100000; i++)
{
type.IsSubclassOf(baseType);
}
watch.Stop();
Console.WriteLine("Their way took: {0} ticks.", watch.ElapsedTicks);
}


Then I ran this one my machine and the results were:

My way took: 107433 ticks.
Their way took: 41684 ticks.

So yea, I got my butt whooped!! I'll chalk this one up to experience.

Moral of the story? Two things. First, not just because you CAN do it with recursion, does that mean you SHOULD do it that way. Second, RTFM!! Chances are, MS has more time to test various ways of doing certain things, so double check that something is in the Framework before rolling your own.

The only one thing I'll say in my (somewhat weak) defense is that InheritsFrom is a MUCH better name for this method than IsSubslassOf IMO. So yea, name your methods better MS!!

Tuesday, June 2, 2009

Creating a Splash Screen in .NET with a progress bar.

EDIT: I've added a link to the source code: http://www.box.net/shared/xbo9xvlguu

Many times an application needs to do many time consuming operations at start-up. Sometimes you need to read data from a database, sometimes you may need to retrieve some data from a web service. When this happens, it's often useful to display a "Splash Screen" to the user, with a company logo or something, along with a progress bar to indicate how much longer it will take for the app to load. While it may sound simple at first, it can be a bit tricky; if you simply show a screen, and do your time consuming operations, your UI will hang and your progress bar will never update. Therefore, there's some threading involved (not too much, don't get scared!), so I'll demonstrate a simple example here.

Start off by creating a simple Windows Forms project. Once it's loaded, add another windows form (besides for the Form1 that's already there) and call it "SplashScreen". To get the look and feel right, let's set some properties:

  • FormBorderStyle: None
  • TopMost : True
  • StartPosition: CenterScreen
Now, in the properties window, find the BackgroundImage property and click the little elipsis {...} and select a picture from your hard drive. I also then changed the BackgroundImageLayout property to None but you can do whatever you want. Then, add a progress bar to the bottom of the form, and set the Dock property to Bottom. Here's what my splash screen looks like in the designer: (not sure why I chose a Halo picture....)

Now, we need to give access to someone outside of this class to update the progress (the progressBar is a private member and can't be accessed.) Here's the problem; if we simply wrap the progress bar's Value property in our own getter/setter like this:



public int Progress
{
get
{
return this.progressBar1.Value;
}
set
{
this.progressBar1.Value = value;
}
}

while you can do that, remember, this splash screen will be shown in a seperate thread. If you then try to access this property from your main thread, you'll get an InvalidOperationException that "Cross-thread operation not valid: Control 'progressBar1' accessed from a thread other than the thread it was created on." So, in order to be able to set any of the UI elements from another thread, we need to call the form's Invoke method which takes a delegate. Here's the complete code for the SplashScreen class:


using System.Windows.Forms;

namespace SplashScreenTesting
{
public partial class SplashScreen : Form
{
private delegate void ProgressDelegate(int progress);

private ProgressDelegate del;
public SplashScreen()
{
InitializeComponent();
this.progressBar1.Maximum = 100;
del = this.UpdateProgressInternal;
}

private void UpdateProgressInternal(int progress)
{
if (this.Handle == null)
{
return;
}

this.progressBar1.Value = progress;
}

public void UpdateProgress(int progress)
{
this.Invoke(del, progress);
}
}
}

As you can see, we created a delegate that we'll use to invoke the update to the progress bar. (The reason why I have a null check for this.Handle is because I was getting an exception right at the start up that the Handle wasn't created yet.)

Ok, now let's create a class that simulates a time consuming operation. Basically, it just calculates Math.Pow for numbers 1 - 100 raised to the 1 - 500,000th power. Every time we move on to another outer number (the 1 - 100) we raise an event that reports progress. Once it's done, we raise an event that we're done. Here's the complete class:



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SplashScreenTesting
{
public class Hardworker
{
public event EventHandler<HardWorkerEventArgs> ProgressChanged;
public event EventHandler HardWorkDone;

public void DoHardWork()
{
for (int i = 1; i <= 100; i++)
{
for (int j = 1; j <= 500000; j++)
{
Math.Pow(i, j);
}
this.OnProgressChanged(i);
}

this.OnHardWorkDone();
}

private void OnProgressChanged(int progress)
{
var handler = this.ProgressChanged;
if (handler != null)
{
handler(this, new HardWorkerEventArgs(progress));
}
}

private void OnHardWorkDone()
{
var handler = this.HardWorkDone;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}

public class HardWorkerEventArgs : EventArgs
{
public HardWorkerEventArgs(int progress)
{
this.Progress = progress;
}

public int Progress
{
get;
private set;
}
}
}


I also created a custom event args so that we can pass the progress on to the subscriber of our ProgressChanged event. (As a side note, if you're wondering about the strange assignement taking place when raising the event, see Eric Lippert's blog on the subject.)

Now, on to the main part of the app; the displaying of the splash screen. In the Form1's load event, we'll spawn off another thread to actually display the splash screen and then on the main thread we'll do our "Hard Work". Once the HardWorker has reported that the progress is complete, we'll dispose of the splashscreen and display the main Form. Here's the code, I'll try my best to explain:

using System;
using System.Windows.Forms;
using System.Threading;

namespace SplashScreenTesting
{
public partial class Form1 : Form
{
private SplashScreen splashScreen;
private bool done = false;
public Form1()
{
InitializeComponent();
this.Load += new EventHandler(HandleFormLoad);
this.splashScreen = new SplashScreen();
}

private void HandleFormLoad(object sender, EventArgs e)
{
this.Hide();

Thread thread = new Thread(new ThreadStart(this.ShowSplashScreen));
thread.Start();

Hardworker worker = new Hardworker();
worker.ProgressChanged += (o, ex) =>
{
this.splashScreen.UpdateProgress(ex.Progress);
};

worker.HardWorkDone += (o, ex) =>
{
done = true;
this.Show();
};

worker.DoHardWork();
}



private void ShowSplashScreen()
{
splashScreen.Show();
while (!done)
{
Application.DoEvents();
}
splashScreen.Close();
this.splashScreen.Dispose();
}
}
}

In the constructor we just hook into the Load event and new up the SplashScreen. Then, in the Load event handler, we first hide the current form, because we don't want that to be seen just yet. We then create a Thread and pass in a delegate to our ShowSplashScreen method. The show splash screen method is what's actually going to be run on a seperate thread. First, it displays the SplashScreen. Then, it just sits there in a constant loop waiting for the "done" bool to be set to true. The key ingredient here is the call to Application.Doevents(). I think Microsoft does a good job explaining what this does so I'll let them do that talking:

When you run a Windows Form, it creates the new form, which then waits for events to handle. Each time the form handles an event, it processes all the code associated with that event. All other events wait in the queue. While your code handles the event, your application does not respond. For example, the window does not repaint if another window is dragged on top.

If you call DoEvents in your code, your application can handle the other events. For example, if you have a form that adds data to a ListBox and add DoEvents to your code, your form repaints when another window is dragged over it. If you remove DoEvents from your code, your form will not repaint until the click event handler of the button is finished executing. For more information on messaging, see User Input in Windows Forms.


Basically, it allows other events to be taken care of, even though the current procedure is blocking execution. This allows our progress bar to be updated.

Back in the form load, we then new up our HardWorker and hook into it's events. (I'm using the more terse Lambda syntax, see my previous blog post on the subject here for more information.) Basically, every time the HardWorker reports progress, we update our progress bar. Then when it's done, we set our done flag to true, and show the main form. Finally, we actually kick it off with a call to DoHardWork();

Try it out and run it. It's actually kinda neat to see it in action. This is obviously a very rough example, but it should give you a basic idea on how to get this done.