Mad Props!

Omniscience is just a Google-search away.

Login

You're reading Mabsterama, the weblog of Matt Hamilton. Enjoy your stay.

Concatenate a list with ToConcatenatedString

I've been posting a few extension methods here lately, so I thought I'd throw another into the mix. This set of ToConcatenatedString methods will take a list of objects and return a delimited string. It gives you the option of specifying your own delimiter (the default is ", ") and your own custom "toString" function if you want to do something other than call .ToString() on each object.

public static class ConcatenationExtensions { public static string ToConcatenatedString<T>(this IEnumerable<T> list,

string delimiter, Func<T, string> toString) { if (list == null || list.Count() == 0) return string.Empty; var sb = new StringBuilder(toString(list.First())); foreach (var t in list.Skip(1)) { sb.Append(delimiter); sb.Append(toString(t)); } return sb.ToString(); } public static string ToConcatenatedString<T>(this IEnumerable<T> list,

string delimiter) { return list.ToConcatenatedString(delimiter, t => t.ToString()); } public static string ToConcatenatedString<T>(this IEnumerable<T> list) { return list.ToConcatenatedString(", ", t => t.ToString()); } }

So now you can take, for example, a list of Customers, and say:

Console.WriteLine(custs.ToConcatenatedString("\n",

c => c.Name + " " + c.Address));

Handy for logging or quickly showing a message to the user.

Enter to Tab as an Attached Property

Updated! As suggested by Eric Burke in the comments, I'm now handling the "Unloaded" event and unhooking the event handlers there, so the elements are properly cleaned up by the garbage collector. Thanks Eric!

A while ago I posted about a trick to make your WPF applications treat the enter key as a tab, and shift focus to the next available control. Paul commented that it should be possible to do that using an attached property, and I agreed, but didn't know how.

Well, now I do.

I'll post the full code for the property itself below, but first, its usage. Firstly you'll need to add an xmlns declaration to the top of your form pointing to the class' namespace. Usually this is just the namespace of your application, like WpfApplication1. I like to call this XML namespace "my". So your window's declaration ends up looking like:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:my="clr-namespace:WpfApplication1"
    Title="Window1">

Next you want to find the container control (usually a Grid or StackPanel) in which all your data-entry controls live, and add the attached property to it, like this:

<StackPanel my:EnterKeyTraversal.IsEnabled="True">

Now all the controls within that StackPanel will treat the Enter key as a tab! Once the focus shifts out of the StackPanel the Enter key reverts to its normal behaviour, which is nice because the very next control will probably be an "OK" button, and the user will want to be able to press Enter to click it.

Here's the complete class declaration to give you the EnterKeyTraversal.IsEnabled property. Have fun!

public class EnterKeyTraversal { public static bool GetIsEnabled(DependencyObject obj) { return (bool)obj.GetValue(IsEnabledProperty); } public static void SetIsEnabled(DependencyObject obj, bool value) { obj.SetValue(IsEnabledProperty, value); } static void ue_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e) { var ue = e.OriginalSource as FrameworkElement; if (e.Key == Key.Enter) { e.Handled = true; ue.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next)); } } private static void ue_Unloaded(object sender, RoutedEventArgs e) { var ue = sender as FrameworkElement; if (ue == null) return; ue.Unloaded -= ue_Unloaded; ue.PreviewKeyDown -= ue_PreviewKeyDown; } public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached("IsEnabled", typeof(bool),

typeof(EnterKeyTraversal), new UIPropertyMetadata(false, IsEnabledChanged)); static void IsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var ue = d as FrameworkElement; if (ue == null) return; if ((bool)e.NewValue) { ue.Unloaded += ue_Unloaded; ue.PreviewKeyDown += ue_PreviewKeyDown; } else { ue.PreviewKeyDown -= ue_PreviewKeyDown; } } }

Collection Implementations as Dependencies

We're working on a series of classes here at work which form a domain model which will some day be accessed from both a PC client (using Windows Presentation Foundation) and a mobile client (using the .NET Compact Framework).

Targeting two different frameworks from the same set of classes is usually not too big a challenge, but we recently found one case that stumped us.

Imagine, if you will, that you have a class called "Order" which stores a collection of "OrderItem" objects. So the order itself has the customer, the date etc, and each item within the order has a product, quantity etc. We define our order's items property thusly:

private IList<OrderItem> _items = new List<OrderItem>();
public IList<OrderItem> Items { get { return _items; } }

Pretty straight forward, really. However, we decided that we wanted to bind directly to the Items property of our order from within our WPF client, and we therefore wanted to make use of .NET 3.0's ObservableCollection<T> property. So our first instinct was to simply do this:

private IList<OrderItem> _items = new ObservableCollection<OrderItem>();
public IList<OrderItem> Items { get { return _items; } }

Sounds reasonable, right? And it is, until you realise that ObservableCollection<T> is not available on the compact framework. Now our class won't compile to a mobile version of the assembly. To get nice collection data-binding on the compact framework you pretty much have to use BindingList<T>.

We tossed around a few solutions. One idea was to make a "CollectionFactory" class which would have a different implementation in the "desktop" version of the assembly and the "mobile" version. The code would then morph into something like this:

private IList<OrderItem> _items = CollectionFactory.New<OrderItem>();
public IList<OrderItem> Items { get { return _items; } }

I still believe that this idea would work, but now I'm considering another approach. We've learned a lot recently about dependency injection, and have been structuring our classes in such a way that each class accepts its dependencies as constructor parameters. I'm starting to wonder now whether the implementation of a collection should be a dependency too. So now instead of constructing the _items list in its declaration, we would accept it as a parameter to the class itself, like this:

class Order
{
    public Order(IList<OrderItem> items)
    {
        _items = items;
    }

    public Order() : this(new List<OrderItem>())
    {
    }

 

There are drawbacks to this approach. The calling code could pass in a non-empty list. I guess we could call Clear() on the list first, but it still seems hackish. The calling code could also construct more than one Order instance with the same list of OrderItems, which is a bit ugly too.

Another approach would be generics:

class Order<T> where T : IList<OrderItem>, new()
{
    public Order()
    {
        _items = new T();
    }

And this works nicely but makes the code to instantiate a new order look pretty messy. Or we could use lambdas and just ask for how to make a new list:

class Order
{
    public Order(Func<IList<OrderItem>> itemsGenerator)
    {
        _items = itemsGenerator();
    }

This is kinda cool too, but has similar problems to simply passing the list in as an object.

One last approach would be to define our own ObservableCollection<T> class in the mobile version of the assembly, by simply deriving it from BindingList<T>. If we never explicitly used any of ObservableCollection<T>'s unique methods or properties from within our assembly then that could work.

Have you solved this particular problem? Hey Microsoft! When can we have ObservableCollection<T> and INotifyCollectionChanged in the compact framework?

Usability of Pigeon-Holes

Everyone knows what a pigeon-hole is, right? It's a term for the trays or boxes in offices that are assigned to people or groups so that you can drop mail off for someone without having to know where their office is. I'm defining the term because I honestly don't know if it's an Australian term or if it's used overseas.

Anyway, we recently got some new ones here at work, and I felt the need to post and rant about the labelling system that some brainiac here has come up with.

Firstly, instead of the individual shelves being labelled with the name of the person (or group) that the shelf belongs to, they are instead assigned a number (from one to about 35). A "legend" mapping numbers to names has been printed out and stuck on the wall nearby. So straight away you can't just look at the shelves to determine where to drop your mail - you have to shoot your attention back and forth between the printed list and the shelves.

Secondly, the numbers run top to bottom instead of left to right, contrary to how the western world thinks when reading. So you're looking for #19 - you find #20 and move one to the left, and it's #13.

And lastly, the numbers are actually on the shelf above the one you want. So you find the shelf labelled "19" and go to put your mail onto it, and then you have to remember that the label actually applies to the shelf below the one it's stuck to.

Basically they've turned what should be a very simple exercise - finding someone's name and putting your mail on that shelf - into a complex, multi-step process. It's a small thing, but the small things add up.

Type Inference for Constants

In C# 3.0 (part of .NET 3.5) we were introduced to a new keyword: var. The var keyword enabled a really nice feature called "type inference", whereby you can declare a variable without having to explicitly state its type. In trivial examples like this, it doesn't feel like it saves you much:

var i = 3; // i is an int

... but where it saves a lot of typing and redundancy is when you have a reasonably complex type, like this:

var x = new Dictionary<string, Func<string, IList<int>>>();

This saves you from having to repeat a long type name on both sides of the "=" sign, which adds to code readability.

Now, some would argue that var can be evil. For example, when you use it like this, it's very difficult for the reader of your code to tell what type your variable is:

var foo = DoStuff();

... but then, that's more a problem of method naming than anything. If that method had been called "GetFoo" then perhaps it would be more obvious what type "foo" is.

Anyway, a feature that has been around in Pascal forever and would be handy to have in C# is type inference for constants. Right now if you want to declare a constant, you use the const keyword as a modifier for a variable declaration, like this:

const float pi = 3.14159;

But since constants have to be initialised at the same time that they're declared, it makes sense to me to adapt the type inference rules to apply to the const keyword too. Then we'd have the option of declaring constants like this:

const pi = 3.14159;
It's a small change, but I don't think it would be particularly hard to pull off. What do you think?

Campaign for a Free EPG in Australia

Last week saw the launch of MyEPG.com.au, a web site devoted to the freeing of the electronic program guide (EPG) data for Australian free-to-air digital TV. As I've written in the past, the commercial FTA television networks here in Australia actually use copyright law to protect their guide data. It's astounding, in that by their definition it should actually be illegal to program your VCR based on the listings in your local newspaper!

As part of the MyEPG campaign, they suggest that you use one of their form letters to contact your local member and bring the issue to their attention. I didn't use a form letter, but I did contact Sussan Ley, my local representative, and let her know about the site. I was amazed to get a personal reply from Sussan later that evening (6:45pm, no less) saying that she would be contacting the federal minister for communications on my behalf! I've even since received a letter in the (snail) mail from her office promising to forward me any future correspondence she receives on the subject!

I really hope the MyEPG campaign works. As a user of Windows Media Center I rely on Free*EPG for my data, and while those guys do a great job, the information should be free and not need any third-party intervention to make it available.

So if you're in Australia and use FTA digital TV, jump on over to the MyEPG site and sign their petition. Every voice helps!

Video Time!

Firstly, the reason why I went to bed last night feeling very angry (video courtesy of Kotaku Australia). Thanks for nothing, Australian politicians (not to mention the host, Tony Jones, who is usually awesome):

... and secondly, an interview with two of the guys from Team SOAK, who took out the Imagine Cup this year for Australia. Well done guys!

The 60th Birthday Party and The Dark Knight

This weekend was pretty busy for us. On Saturday night we celebrated my father-in-law's 60th birthday party, a party that Sal had been organizing for months. We had invited friends and family from all over the place, and it went off without a hitch.

An hour or two into the party, Sal and I were relaxing on a couch along one of the function room's walls. The room itself sits above a large tavern and is basically an attic - the ceiling slopes upwards following the roof. I decided to get up and grab a drink from the bar, but didn't realise/remember that directly above me was a large wooden rafter. The "crack" sounds as my forehead hit the corner of the rafter could be heard several metres away, along, no doubt, with the rather vocal exclamation I made thereafter. It didn't hurt so much, but it was a hell of a shock and left me a little weak-legged for the next half hour or so.

On Sunday we caught The Dark Knight. Sal had no interest in seeing it, so while she enjoyed it she did start to get a bit fidgety toward the last half hour. The movie is very strangely structured - it's almost episodic in nature, with a whole series of mini-climaxes throughout instead of one big one. Still, Heath Ledger is no less than a force of nature, and deserves the hype he has been getting, as does the entire movie. His death was a real loss.

Even after nearly 50,000 votes on IMDb, the movie is rating 9.7/10 and sits atop the Top 250 Movies list. I'm sure it'll drop down as more people vote, but it's amazing that it has hit that level so quickly.

Does Your 404 404?

Remember my awesome custom 404 page built using Graffiti's Chalk theme engine? I found out today (thanks to Google's webmaster tools) that it had a problem - it was actually returning HTTP/200 (OK) as its response code. That meant that any robot visiting an address on my site that doesn't exist would actually believe that the page does exist, which is not ideal.

After searching around fruitlessly for a way to change the http response code from Graffiti, I decided to write my own Chalk extension to do it for me.

Essentially I've created a new class with a method called SetResponseStatusCode, which takes an int parameter. It grabs the current HttpContext and sets its Response.StatusCode to the given value. I then added this code to my custom 404.layout.view file:

$Props.SetResponseStatusCode(404)

Now my 404 page really does 404, so that's good to know. Worth checking if you've taken the time to provide a custom page of your own.

Graffiti Similar Posts

At lunchtime today I was poking around and discovered this tip from Steve Smith (presumably not the minister for foreign affairs).

I've implemented it, and it works a treat, so now you get three similar posts at the bottom of the page (just before the comment section) in case there's some more reading on the site around the topic you're tracking.

The only problem, though, is that it does seem to slow down the "view post" pages quite dramatically. So let me know if you think it's worth the tradeoff ... are slower post views acceptable for the nifty "similar posts" list?