Would you be enthusiatic about a Lada, even if the salesman proved it had the engine of a Ferrari? Probably not.
Same goes for software. The user interface says a lot about the application, and in many cases, can easily make up for quirky behaviour and simple bugs. Your customer will always see the interface first and give his first impressions based on it, so it's extremely important.
So the next big thing for interfaces seems to be WPF. I started looking into it, and it looks quite promissing. Visual Studio 2008 has a pretty good editor for it, so it's a good idea to get into it.
For those just starting out, I found a very good (albeit lengthy) hands-on lab that guides you through the steps of putting together an Outlook clone with WPF.
The lab can be found here: http://www.00001001.ch/download/HOL/WPF/Outlook_HOL_WPF.pdf
The original blog post where I found this is here: http://blogs.msdn.com/tims/archive/2007/06/13/wpf-hands-on-lab-build-an-outlook-2007-ui-clone.aspx
Enjoy!
Thursday, January 10, 2008
It's all about the looks ...
Posted by Muaddubby at 7:42 AM 0 comments
Wednesday, January 2, 2008
C# string enumerator working flawlessly now
I was able to remove the dependency on ToString() in the string enumerator article posted a couple of days ago, so now it really is perfect. You can assign the value of the enumerator directly to a string variable without any casting or the use of ToString(), although you still can if you want to (but I don't see why you would).
The article and the sample code on CodeProject are both up to date now.
Posted by Muaddubby at 6:06 AM 0 comments
Monday, December 31, 2007
The perfect C# string enumerator
Introduction
I really love C# and do my utmost at kicking myself in the rear for not having gotten into it sooner than I did. In spite of that, though, I sorely miss some things from other languages.
Take string enumerators for example. Enumerators are an amazing thing when it comes to keep things clean and easy to maintain. You define them once, assign them their values, and use their names throughout the program. Behind the scene each enumeration is translated to the value you set it up with, and that's what you end up storing on disk, using for calculations, etc.
String enumerations are no different. You pick a set of names, assign them their text values, and in code you always refer to the names. When it comes time to using them for disk storage, display, etc., you refer to the enumeration's value.
But in C#, for whatever reason (anyone know why?), this was left out. I've looked around for quite a while, and found proposed solutions that ranged from simply creating a class with static strings to some that use reflection. They all seemed either too simple or too complex, and none gave me all the functionality an enumerator should have.
So I set out to make my own, and I think I've done it. I would say that it mimics a regular enumerator to 100%, and provides exactly the same functionality, usage and behaviour as regular enumerators. I cannot think of anything that has been missed, and am depending on my readers to help point those things out :)
Background
When I set out to create this, I had to sit down and think about all the ways in which an enumerator could be used in order to make sure the string enumerator is as true to its name as possible. I've created a number of tests using nUnit, and so far so good. I can't really think of any more good tests for this, but if anyone else can please let me know.
Usage
You can refer to the tests in the nUnit project to see the enumerator's capabilities, but here they are in brief:
Once you create the enumerator class, you use it exactly the way you would use a regular enumerator, with one slight difference: When you want to deal with the actual string value of either the enumerator object or of one of the static enumerator values, you must use the ToString() method. I would dearly love to find a way around this, but even if I can't, I think it's a pretty small price to pay.
The EStringEnum base class
To create a string enumerator you'll need two things. The base EStringEnum class, and a class that you define (which inherits from EStringEnum) with the actual string values.
The base class exposes two items that must be overwritten, as well as logic for validating string values that the user is trying to assign to an enumerator object (i.e. the value must be one of the valid enumerated strings), and some overloads for ToString() and various operators:
///
/// A string enumerator base class. Must be inherited.
///
public abstract class EStringEnum
{
#region Data
protected string mCurrentEnumValue = "";
protected abstract string EnumName { get; }
protected abstract List PossibleEnumValues { get; }
#endregion
#region Constructors
public EStringEnum() { }
///
/// A string enumerator
///
/// A valid enumerator value.
/// An exception is raised if the value is invalid.
public EStringEnum(string value)
{
if (PossibleEnumValues.Contains(value))
{
mCurrentEnumValue = value;
}
else
{
string errorMessage = string.Format(
"{0} is an invalid {1} enumerator value",
value,
EnumName);
throw new Exception(errorMessage);
}
}
#endregion
#region Overloads
///
/// Returns the enumerator's current value
///
///
public override string ToString()
{
return mCurrentEnumValue;
}
///
/// Test for equality
///
///
///
///
public static bool operator ==(EStringEnum stringEnum1, EStringEnum stringEnum2)
{
return (stringEnum1.ToString().Equals(stringEnum2.ToString()));
}
///
/// Test for inequality
///
///
///
///
public static bool operator !=(EStringEnum stringEnum1, EStringEnum stringEnum2)
{
return (!stringEnum1.ToString().Equals(stringEnum2.ToString()));
}
///
/// Test for equality
///
///
///
public override bool Equals (object o)
{
EStringEnum stringEnum = o as EStringEnum;
return (this.ToString().Equals(stringEnum.ToString()));
}
///
/// Retrieve the hashcode
///
///
public override int GetHashCode()
{
return base.GetHashCode();
}
#endregion
}
Only two things from this base class must be implemented when inheriting it:protected abstract string EnumName { get; }
protected abstract List PossibleEnumValues { get; }
EnumName must return a string with the name of the string enumerator (e.g. ECarManufacturers), and PossibleEnumValues must return a ListThe actual enumerator class
The enumerator must inherit from EStringEnum and do the following:
- implement the two properties mentioned above,
- provide static accessors for each enumerated string,
- provide a constructor (which just calles the base constructor), and
- provide an implicit conversion from a string type to an object type of the actual enumerator (e.g. ECarManufacturers).
It may sound complex, but as the example below shows, it really is quite simple to implement:
///
/// A car manufacturer enumerator
///
public class ECarManufacturers: EStringEnum
{
#region Data
///
/// Used by EStringEnum to identify the current class
///
protected override string EnumName { get { return "ECarManufacturers"; } }
protected override List PossibleEnumValues { get
{ return mPossibleEnumValues; } }
///
/// Complete list of string values that this enumerator can hold
///
private static List mPossibleEnumValues = new List()
{
"Toyota",
"Honda",
"Ford",
"Chrysler",
"Volvo",
"General Motors"
};
///
/// CarManufacturers type
///
static public ECarManufacturers Toyota
{
get { return new ECarManufacturers(mPossibleEnumValues[0]); }
}
///
/// CarManufacturers type
///
static public ECarManufacturers Honda
{
get { return new ECarManufacturers(mPossibleEnumValues[1]); }
}
///
/// CarManufacturers type
///
static public ECarManufacturers Ford
{
get { return new ECarManufacturers(mPossibleEnumValues[2]); }
}
///
/// CarManufacturers type
///
static public ECarManufacturers Chrysler
{
get { return new ECarManufacturers(mPossibleEnumValues[3]); }
}
///
/// CarManufacturers type
///
static public ECarManufacturers Volvo
{
get { return new ECarManufacturers(mPossibleEnumValues[4]); }
}
///
/// CarManufacturers type
///
static public ECarManufacturers GeneralMotors
{
get { return new ECarManufacturers(mPossibleEnumValues[5]); }
}
#endregion
#region Constructor
///
/// A car manufacturer enumerator
///
/// A valid enumerator value.
/// An exception is raised if the value is invalid.
private ECarManufacturers(string value): base(value)
{ }
#endregion
#region Misc methods
///
/// Implicitly convert a string to a CarManufacturers object
///
/// A string value to convert to an ECarManufacturers enum value.
/// An exception is raised if the value is invalid.
///
public static implicit operator ECarManufacturers(string value)
{
return new ECarManufacturers(value);
}
///
/// Implicitly convert an ECarManufacturers object to a string
///
/// A ECarManufacturers object whose value is to
/// be returned as a string
///
public static implicit operator string(ECarManufacturers carManufacturers)
{
return carManufacturers.ToString();
}
#endregion
}
ExamplesThe following tests are included in the nUnit test project (available in the download), but I'm posting them here as well to give you a quick look at how true-to-life this implementation is:
[TestFixture]
public class TestClass1
{
[Test]
public void Test01()
{
string result = ECarManufacturers.GeneralMotors.ToString();
string expected = "General Motors";
Assert.IsTrue(result == expected);
}
[Test]
public void Test02()
{
ECarManufacturers carManufacturer = ECarManufacturers.Honda;
string result = carManufacturer.ToString();
string expected = "Honda";
Assert.IsTrue(result == expected);
}
[Test]
public void Test03()
{
Test03A(ECarManufacturers.Ford);
}
private void Test03A(ECarManufacturers carManufacturer)
{
string expected = "Ford";
string result = carManufacturer.ToString();
Assert.IsTrue(result == expected);
}
[Test]
public void Test04()
{
Test04A(ECarManufacturers.Ford);
}
private void Test04A(ECarManufacturers carManufacturer)
{
ECarManufacturers tempCarManufacturers2 = carManufacturer;
string result = tempCarManufacturers2.ToString();
string expected = "Ford";
Assert.IsTrue(result == expected);
}
[Test]
public void Test05()
{
Test05A(ECarManufacturers.Ford);
}
private void Test05A(ECarManufacturers carManufacturer)
{
ECarManufacturers carManufacturer2 = carManufacturer;
carManufacturer = ECarManufacturers.Chrysler;
string expected1 = "Chrysler";
string result1 = carManufacturer.ToString();
string expected2 = "Ford";
string result2 = carManufacturer2.ToString();
Assert.IsTrue(result1 == expected1 && result2 == expected2);
}
[Test]
public void Test06()
{
ECarManufacturers tempCarManufacturers = "Ford";
string result = tempCarManufacturers.ToString();
string expected = "Ford";
Assert.IsTrue(result == expected);
}
[Test]
public void Test07()
{
ECarManufacturers tempCarManufacturers2 = null;
try
{
tempCarManufacturers2 = "Orion";
Assert.Fail();
}
catch (Exception ex)
{
Assert.IsTrue(ex.Message.Equals(
"Orion is an invalid ECarManufacturers enumerator value"));
}
}
[Test]
public void Test08()
{
ECarManufacturers tempCarManufacturers = "General Motors";
Assert.IsTrue(tempCarManufacturers.ToString().Equals(
ECarManufacturers.GeneralMotors.ToString()));
}
[Test]
public void Test09()
{
ECarManufacturers carManufacturers1 = ECarManufacturers.GeneralMotors;
ECarManufacturers carManufacturers2 = ECarManufacturers.GeneralMotors;
Assert.IsTrue(carManufacturers1 == carManufacturers2);
}
[Test]
public void Test10()
{
ECarManufacturers carManufacturers1 = ECarManufacturers.GeneralMotors;
ECarManufacturers carManufacturers2 = ECarManufacturers.Ford;
Assert.IsTrue(carManufacturers1 != carManufacturers2);
}
[Test]
public void Test11()
{
ECarManufacturers carManufacturers1 = ECarManufacturers.GeneralMotors;
int number = carManufacturers1.GetHashCode();
}
[Test]
public void Test12()
{
ECarManufacturers carManufacturers1 = ECarManufacturers.GeneralMotors;
string value = carManufacturers1;
ECarManufacturers carManufacturers2 = carManufacturers1;
string value2 = carManufacturers2;
Assert.IsTrue(value.Equals("General Motors"));
}
}
In conclusionNot a very complex solution when you think about it, and I think it covers all the behaviours an enumerator is expected to have. When it comes time to use the enumerator, you use it exactly like you would use a regular enumerator. If you can think of anything I've missed or can improve upon, feel free to tell me.
Something I'd like to do to improve on this are:
- Extract as much logic as possible and put it in the base class (such as the static accessors). I could use something like Spring.Net to inject the accessors at runtime, but this would have two major drawbacks: intellisense would no longer work, and I would be adding a dependency to the project.
A downloadable copy of the source code is available here: http://www.codeproject.com/KB/cs/csharpstringenumerator.aspx
History
December 31 2007 - Initial post (happy new year!).
January 01 2008 - Added examples.
Posted by Muaddubby at 11:11 AM 1 comments
Saturday, December 29, 2007
The search for string enumerators
Of all the languages I've worked with so far, C# is by far my favourite. My list of reasons for this is quite long, but in spite of all that, I still have things I enjoyed very much in other languages that C# lacks. Simple examples are Cobol's EVALUATE statement and Visual Basic's string enumerators.
Under the hood, the EVALUATE statement is the equivalent of a series of else if statements, except that the debugger does a very elegant job of jumping straight to the else path that is true instead of stepping through every single one until it finds one that it likes. Not a big deal, but certainly something I miss.
The string enumerator, on the other hand, is sorely missed. I have yet to find a good way to implement one in C#, and so am embarking on a mission. I've seen several suggestions out there, ranging from simple ones that use string formatting to extract the names of an integer enumerator to more complex ones that use reflection.
Suffice it to say that I didn't like any of them. Too simple, too inelegant, etc. There has to be a good way out of this that will not be too complex to implement, will be elegent to code and use, and will resemble the normal enumerators as much as possible (preferably to 100%).
Posted by Muaddubby at 9:30 AM 0 comments
Friday, December 28, 2007
If you need to do asynchronous work, this is for you ...
I read a good article by Mike Peretz on the codeproject about delegates and asynchronous method invocation. It's a good thing to know, especially if you have work that takes a while to complete, have a UI that needs to remain fresh while doing some heavy work (although you should probably be using an MVP pattern - which takes care of this - if your app has a front end and complex business logic), etc.
You can read the article here: http://www.codeproject.com/KB/cs/AsyncMethodInvocation.aspx
Posted by Muaddubby at 6:27 PM 1 comments