Sunday, November 8, 2009

Bedtime Routine

Back at the beginning of September, Caedmon finally figured out how to climb out of his crib, and how to turn the light on in his room.  We tried very hard for several nights to get him to stay in bed on his own, but we eventually gave up.  Ever since, every night, our routine has been like this:

  • Give him a bath and brush his teeth.
  • Read three stories together.
  • Give him half an hour to play quietly in his room.
  • Tuck him in, pray together, and turn off the light.
  • Wait outside the room (at most 30 seconds) before he's up and turning on the light or opening the door.
  • Remove the lightbulb, and lock his door.
  • Wait for the screaming to die down.

For the last week or so, we've been talking about trying something different – partly because we're tired of the fight, but also because we don't like having to resort to a physical restraint, like a lock.  We'd rather that he be able to control himself, well, by himself.

So this afternoon, we explained to Caedmon that we'd be doing something different tonight.  We had four new bedtime rules: (1) Stay in bed; (2) close your eyes; (3) stay quiet; (4) put your head on your pillow.  We gave him hand motions for each of the rules, and rehearsed them with him repeatedly throughout the afternoon and evening.  We also explained that if he got up out of bed, we would immediately put him back to bed, without looking at him, and without saying anything.

So Galena drew the short straw tonight.  Everything went well, up to the point where she turned off the light, left the room and closed the door. 

At about the half hour mark, I came up to see how things were going.  She was standing outside the door with her teeth clenched.  In-between missions, she said, “Thirty-three.”  The door opened again, she disappeared inside, then re-emerged.  “Thirty-four.” The door opened again, and she disappeared once more. “Thirty-five,” she said when she came out.

I came back about half an hour later.  She was still standing outside the door, teeth still clenched, but she had removed her sweater and her arms were bare.  “One hundred thirty.”  “One hundred thirty-one.”  “One hundred thirty-two.”

Somewhere around 150, I could hear Caedmon's giggles switch to crying.

I popped my head into the hallway a bit later.  “One hundred sixty-two,” she said, but there was triumph in her eyes.  From within his room, I could hear Caedmon screaming, “No, Mom! No! Go away! Daddy! Daaaaddy!"  The door opened again, and in she went.

Caedmon is now asleep.  Galena had to put him back in bed 169 times before he finally stayed.

It's my night tomorrow.  Pray for me.

Thursday, October 1, 2009

Really Missing Serialization Callbacks

I just ran into another feature whose absence from Silverlight is sorely missed.

I’m using the WCF generated proxy classes as the basis for binding to some UI objects.  If you’re using a full MVVM pattern, the way that you’d normally do this is wrap the proxy-generated classes with a ViewModel, so that instead of binding to, say, the User class generated by “Add Service Reference”, you’d bind to a UserViewModel class that acts as a facade for the User class.  So far, I haven’t been willing to go that route.  The only point to having a ViewModel is if you’re making extensive use of databinding, and databinding happens to be my least favorite Silverlight technology, for reasons that I’ve explained elsewhere.  And if I’m only doing databinding occasionally, it seems like a lot of more-or-less pointless work to recreate a facade for my complete object model on the client, and then keep it synchronized with the classes that the Entity Framework has already helpfully generated for me.  So I’ve been making do with partial classes to add any additional properties or methods that seem appropriate.

But as I said, I ran into a problem today.  One of my classes, SharedFile, has a bindable property called StatusText whose value depends on a complicated graph of other object properties; and to get them all working, I’ve had to string together an unpleasant chain of INotifyPropertyChanged notifications, sorta like so (this is just one of numerous chained handlers):

   1: void uploadCommand_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
   2: {
   3:     if (e.PropertyName == "State")
   4:     {
   5:         UpdateStatusText();
   6:     }
   7: }

OK, so far so good – but I have to wire up these event handlers somewhere.  Since the definition of SharedFile in reference.cs didn’t define a default constructor, I thought it would be simple to create one in my partial class, and that would be the end of it:

   1: public SharedFile()
   2: {
   3:     this.PropertyChanged += SharedFile_PropertyChanged;
   4: }

But although the objects get created, that constructor never gets called.  WTF?  Well, it turns out that when Silverlight deserializes the XML from my WCF service, it uses FormatterServices.GetUninitializedObject to create the object, which skips calling the constructor.  I guess that makes a certain sort of sense – the WCF service is handing you an object that, in theory, is already constructed, so you shouldn’t need to call the constructor again.  I get that.  But then where do I put this code?

OK, I know, I can put that code in a method that I tag with the [OnDeserialized] attribute.  That’s the normal way of doing it, right?  Oh – except Silverlight doesn’t support serialization callbacks.

Huh?

I get that some features need to be left out of Silverlight.  But this seems like a really odd one.  Serializing and deserializing objects is what you do in Silverlight.  You can write real-world WPF or WinForm or ASP.NET applications all day long and never once have to deal with object serialization and deserialization.  But you can’t use Silverlight for five minutes without needing to touch an object that’s just been deserialized from some web service.  So why choose that particular set of features to cut?  It sure seems like a bizarre design choice.

At any rate, my choices were either to implement a full-blown ViewModel layer, which I really don’t want to do, or write a hack of some sort to initialize the event handlers manually.  Uggh.

What I’ve done for now is to throw an Initialize() method on the containing object (User), which in turn initializes any object that gets added to its SharedFiles ObservableCollection:

   1: // This is necessary because Silverlight doesn't call a constructor when deserializing classes,
   2: // and also doesn't support on the [OnDeserialized] attribute.  Damn annoying.
   3: public void Initialize()
   4: {
   5:     InitializeSharedFileList(this.SharedFiles);
   6:     SharedFiles.CollectionChanged += SharedFiles_CollectionChanged;
   7: }
   8:  
   9: private void SharedFiles_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
  10: {
  11:     if (e.Action == NotifyCollectionChangedAction.Add)
  12:     {
  13:         InitializeSharedFileList(e.NewItems);
  14:     }
  15: }
  16:  
  17: private void InitializeSharedFileList(IList sharedFileList)
  18: {
  19:     foreach (SharedFile sharedFile in sharedFileList)
  20:     {
  21:         sharedFile.Initialize();
  22:     }
  23: }

And then I call User.Initialize() when I first retrieve it from the web service:

   1: private User user;
   2: public User User
   3: {
   4:     get
   5:     {
   6:         return user;
   7:     }
   8:     set
   9:     {
  10:         if (!object.ReferenceEquals(user, value))
  11:         {
  12:             user = value;
  13:             if (user != null)
  14:             {
  15:                 user.Initialize();
  16:                 UserId = user.UserId;
  17:             }
  18:             else
  19:             {
  20:                 UserId = string.Empty;
  21:             }
  22:         }
  23:     }
  24: }

Like I said, uggh.  But it works.  I just wish that MS had thought through their deserialization scenarios a little better.  I don’t like being forced into creating another abstraction layer if I don’t have to.

Friday, September 25, 2009

Silverlight duplex client limitation

I ran into an interesting (and largely irrelevant) limitation today on Silverlight's implementation of duplex web services.

I'd been using Jeff Wilcox's handy Silverlight Unit Test Framework to test the data access piece of my current Silverlight project. However, I was running into a nasty problem that was driving me nuts. Part way through every test run, my unit tests would start failing. I could usually get through something like 10 or so tests before every new WCF call would return "Not Found" (which is not really the most helpful error message Microsoft ever came up with). It didn't have anything to do with the individual tests themselves, because the error would show up after 10 tests, no matter which 10 tests they were.  I'd been working through this error for some time before I realized that the "10" number was undoubtedly significant, since that seems to be the default number of connections that WCF allows, unless you go in and bump it higher. (Now of course, IMO, that's a pretty dumb default: the obvious purpose for leaving it that low is to prevent DOS attacks -- but the net result is that instead of needing some 10,000 connections to DOS your server, you only need 10. Sigh.)

This made me think that I might be leaking a connection somewhere. In theory, I was closing all my connections in one test before moving on to another, but we all know how well that works :-).  Since I was opening and closing all my connections through the same static class, I wrote up some quick instrumentation, and saw that yeah, one connection was staying open after every test.  Some additional poking around, and I found a method that was opening a new connection and failing to close it.  Easily fixed.  Instead of this final line in my method:

EnqueueTestComplete();

I just made it do this:

EnqueueCallback(() => DataConnectionManager.TryClientClose(client,
            error => EnqueueTestComplete()));

And TryClientClose() looks something like this:
        public static void TryClientClose(RoomServiceClient client, OperationCallback callback)
        {
            if (client != null && client.State == CommunicationState.Opened)
            {
                client.CloseCompleted += (sender, e) =>
                {
                    ClientsClosed++;
                    ClientsOpen--;
                    if (e.Error != null)
                    {
                        client.Abort();
                    }
                    if (callback != null)
                    {
                        callback(e.Error);
                    }
                };
                client.CloseAsync();
            }
            else
            {
                if (callback != null)
                {
                    callback(null);
                }
            }
        }

Close enough. But why was I running into this error in the first place?  Truth be told, I don't completely know.  But my best guess is that the Silverlight client (or maybe the browser that's hosting it) has a limitation on how many duplex callback sessions it can support.  And so far as I'm aware, there's no way to increase this number.  At least, I've poked around in all the relevant blogs, and looked through the appropriate docs, and can't find anything obvious.  But the net result is that you don't want to have more than 10 duplex clients open at the same time on any given Silverlight application.

Saturday, September 19, 2009

And Three More Silverlight Complaints

Unhelpful error messages rank high on my list of complaints about Silverlight and WPF. Take this particular error message: AG_E_PARSER_BAD_TYPE. I get it pretty regularly, but this particular instance started showing up about 15 minutes ago. The location that it points to (presumably in App.xaml? – it doesn't actually say) has nothing to do with the error in question.

The error is presumably triggered by some XAML I screwed up somewhere in one of the 50+ different XAML files in my solution. I have no idea which file (I just did a big search-and-replace as part of a codebase-wide refactor). So first of all: AG_E_PARSER_BAD_TYPE? Since when does that qualify as an error message? We're not doing sockets programming here, folks. The CLR allows for an incredibly rich set of exceptions. So why the incomprehensible error message? But more to the point, why am I being pointed to the wrong location? One of the things that makes the Silverlight tools for Visual Studio feel so frustrating is that they know which file is problematic, and they know precisely what the problem is, and they refuse to tell me. Yes, I know my reaction makes Silverlight a little more anthropomorphic than necessary. But it sure as heck feels like the Microsoft tools are out to get me sometimes. (For what it's worth, after an hour of troubleshooting, I opened the solution in Blend, and for once, Blend gave me what I needed to know, and immediately highlighted the problematic file.)

My second complaint is bogus error messages, i.e., error messages when there's no error. Again, to take one example of many, in one particular form in my project, Visual Studio lists 27 different errors, all having to do with certain local controls not being found.

The assembly in question (SlideLinc.Client.dll) and the associated namespace (SlideLinc.Client) are registered correctly in the form, and indeed, the form loads and executes correctly at runtime. For once, Blend recognizes this, and doesn't raise any errors. In other words, there's no problem. But I've tried six ways from Sunday to make those errors go away in Visual Studio, and nothing has been successful. In this particular case, it's generally not that problematic – except when it comes time to track down a real error, and you have to fight your way through dozens of bogus error messages to find the right one. (It's at least a tad ironic that these bogus error messages at least show up at the right time, i.e., when the app is compiling, and are able to point me to exactly the place where they – incorrectly – think the error is occurring. In contrast, the quite real AG_E_PARSER_BAD_TYPE error that I ran into above showed up way too late, at runtime, and wouldn't even tell me which file it was occurring in. It's this kind of stuff that makes you pull your hair out – and which has cost me at least a month of troubleshooting on my current project.)

My third complaint is more specific, and has to do with the Add Service Reference feature in Visual Studio. Specifically, I run into two reoccurring, distinct, but possibly related bugs when I try to update my service references. Neither of these occurred with the March 2009 SL3 CTP, and both started showing up immediately after I upgraded to SL3 RTW.

  1. Periodically (about a third of the time), when I pull open the "Configure Service Reference" dialog box, the "ObservableCollection" option for collection types isn't available; instead, there's something listed called "(Custom)", which is apparently interpreted as "Array[]". So when it generates your proxy references, it generates any list of values as an array, rather than an ObservableCollection. This breaks all your existing code, which of course was written to expect an ObservableCollection in those instances. What usually fixes it for me is simply restarting Visual Studio. Nothing else seems to work.
  2. The second problem is that periodically the Reference.cs file fails to generate – which of course also breaks all your existing code. I ran into a tip from someone, somewhere, who recommended deselecting the option "Reuse types in specified referenced assemblies", and then select only "System.Core". When I do this, and then I update my service reference, it seems to fix this pretty reliably. Sometimes it happens anyway, at which point in time I simply change which referenced assemblies it should try to reuse, and then immediately everything works again.

On this last point, I should note that lots of other folks besides me have been running into these issues. Indeed, I don't know anybody who regularly works with Silverlight and WCF who hasn't run into them. It's also been quite well-documented on the forums. I've tried to report these bugs on the MS Connect website, but I can't figure out how: I don't seem to have that option.

I should note that the Silverlight runtime itself seems to be pretty darned stable, all things considered. I've beat the thing to death with stress tests, and haven't run across any bugs worth mentioning that weren't my own damned fault. All the real problems seem to show up in the various tools you use to build Silverlight applications.

It seems to me that there are going to be some massive improvements to the .NET platform coming with the impending .NET 4.0 / Visual Studio 2010 release. I keep hoping that the reason these bugs haven't been fixed out in the wild is that MS is working so hard to improve the quality of that upcoming release.

I can hope, can't I?

Friday, September 18, 2009

Mixed Feelings about Silverlight

I spent all day yesterday at the Microsoft Silverlight FireStarter event. (You can see the decks here, and the recordings will be up shortly.) It was an appropriate place for me to be hanging out, since Alanta, my current startup, is working to build a web conferencing system on Silverlight. I came away from the day with a variety of mixed feelings.

First, I realized that not only do I have a lot to learn, but what I don't know is hurting me. If I knew the tools and frameworks better, I could make a lot faster progress on our product, and the resulting code would be better architected and more maintainable. The demo that Adam Kinney did on Blend made me realize just how crappy my own Blend skills are, and how I could be a ton more productive if I knew Blend better. Similarly, the stuff that Marco Matos did with the Silverlight Toolkit, and Karl Shifflet's demo of his XAML PowerToys.

I also felt grateful to Microsoft (in general) and to the people who put on the event (very specifically). They've pretty much dedicated their lives to creating tools and frameworks and platforms for the rest of us to use, to make our lives easier or better in some way. And when, like me, you spend more than half your waking hours working with those particular tools and frameworks and platforms, that's not something you can just take for granted. It's something to be thankful for.

At the same time, I think I spent more time at the event feeling frustrated than anything else. I've said before that you know a product is succeeding when people start complaining about it; and by that standard, Silverlight is a very successful product. Here are my top three complaints about the current state of Silverlight and its tools:

  1. Silverlight databinding is a mess. This is partly due to the fact that it is missing many of the features of its more powerful cousin, WPF databinding. But databinding in both Silverlight and WPF suffer from the same underlying problem: it's incredibly difficult to get right, very easy to get wrong, and MS provides provides no troubleshooting help at all. As I've said elsewhere (here and here), I think the biggest underlying problem with the current databinding implementation is that it's untyped, i.e., you never specify the type of the DataContext your XAML is expecting. As a result, there's no Intellisense to help you when you define your bindings, and no design-time or compile-time checking to make sure you got them right. I could probably live with this limitation if Microsoft provided better tools and frameworks for runtime troubleshooting. But at runtime, any binding errors get swallowed entirely and completely. A very few of them will show up in the Visual Studio debug window, but you have absolutely no insight into the vast majority of things that might go wrong. Even if MS thought that it was too big of a switch to move to a strongly-typed DataContext, they should at the very least, for the sake of all that is holy, have their controls raise an event when something goes wrong, so that you have some opportunity to see and respond to what might be happening. I can't express strongly enough how absolutely insane it is that we're about to see version 4 of Silverlight, and Microsoft still hasn't fixed this basic, fundamental problem. I spend enormous amounts of time troubleshooting databindings, and it's the primary reason why we've only partially adopted an MVVM architecture for Alanta. I spend enough of my time troubleshooting WCF, which is complicated and annoying, but at least it provides real errors. There's not enough time in the day to guess (and guess and guess and guess again) what might be going wrong with my databindings.

    I should note that Tim Heuer, frequently the face of Silverlight to the world, was able to sympathize with these problems yesterday. He pointed me Glimpse, a recent tool from Karl Shifflett, that provides some insight into what's happening under the hood with databinding errors. I haven't had the chance to try it out yet, but it seems like a step in the right direction. Still, "not sucking quite so horribly" doesn't equal "sufficient", least of all "good".

    Microsoft and the rest of the community is making a huge push towards MVVM as the standard development model for Silverlight and WPF. That makes sense: but its adoption by average, everyday developers will remain limited until MS is able to fix their databinding experience.

  2. Blend is a mess. Yes, it's incredibly powerful. And yes, V3 is much, much better than V2. And it goes without saying that it's better than Visual Studio for UI design. But as the premiere MS tool for designing great user experiences, it shows an astonishing lack of appreciation of what makes for a great user experience. To take just a couple examples:
    1. In the picture below, try to guess which of the two tabs ("FileRowControl" or "FileManagerControl") is selected. Is it the one on the left whose bold, dark color seems to flow uninterrupted into the workspace? Or is it the one on the right whose light gray color, everywhere else in the Windows UI, indicates "disabled"? Astonishingly, it's the tab on the right that's selected. I've been working with Blend for months now, and every time I have to look at the open file tabs, I have to stop and think which one is actually selected. That's inexcusable for a company with as many design resources as Microsoft.
       
    2. Here's a fairly standard property panel in Blend:
       
      Now, somewhere in this panel is a way to set these properties in an advanced fashion, i.e., through databinding or other similar techniques. Care to hazard a guess as to how you do that? Might one, perhaps, right-click and see some options for doing so? Nope, no right-click menu is available. Give up? Well, I'm astonished that you missed the tiny little two-pixel squares to the right of every property. It turns out that if you navigate your mouse over one, and after two or three tries manage to click on it successfully, it pops up this context menu:
       
      This is such a non-standard user experience that there isn't even a name for it. (Adam Kinney has suggested calling it a Property Peg.) But there's a darned good reason there isn't a name for it. It's because it's a bad idea! It's a horrible UX design. How more completely unintuitive and non-discoverable is it possible to get? (That's apart from the fact that because the, umm, peg is so small, even once you figure out what it's there for, it's still hard to use.)

    3. In every other Windows application, scrolling your mouse wheel moves the selected document up or down. In Blend, scrolling your mouse wheel sets the zoom level on your current workspace. That wouldn't be so bad if it made a better guess about where you were aiming. For instance, here's what my screen looks like after I open up a specific user control I've been working on lately:
       
      And here's my screen after four "zoom in" mouse-wheel movements:

       
      Rather than zooming in on anything that I might be interested in (say, the selected object), Blend has helpfully chosen to move my control entirely off the screen. And what's worse, it seems bloody well determined to be random about the direction it moves it. Sometimes it moves it off my screen up and to the right, sometimes down and to the left, and occasionally, very occasionally, it decides to actually zoom in on the thing I'm interested in. Taken all together, though, it's as if Blend is determined to take my mouse wheel, one of the very best usability inventions ever, and make it entirely useless.

      I could go on for quite a while about Blend's failings: these are only a couple of random examples. But they should be sufficient to demonstrate that Blend desperately needs some UX TLC.

  3. Silverlight is still missing some very basic features. The one that's usually brought up is the lack of printing support, which is laughable, if you think about it very long at all. Given the project I'm currently working on (web conferencing), the feature that I miss the most is obviously web camera and microphone support. Flash has supported web cameras and microphones for years, so it was very disappointing to me that this feature got cut from Silverlight 3. From the knowing and slightly embarrassed looks that I see on Microsoft faces whenever the subject gets brought up, I'm pretty sure that it'll make it into Silverlight 4. But still. Of the six months that I've spent working on Alanta, at least a month out of that time has gone to troubleshooting the clever but ultimately painful Flash hack that we're currently using.

I feel the need to repeat what I said earlier: you know a product is being successful when people start complaining about it. They complain – I complain – because I can see the potential for the product, and get frustrated because it's not there yet. I'm also a developer, and know how hard it is to get these things right. The folks at MS are only human. But I also hope that they're listening.

Monday, August 10, 2009

Clearing event handlers implemented with lambda expressions

One of the niceties included with C# 3.0 is lambda expressions, a concise syntax for implementing anonymous methods. Lambda expressions can be over-used, but they're very handy for certain kinds of things, like defining how asynchronous callbacks should be handled, all within a single method.

Lambda expressions are also very handy for implementing event handlers. For instance, if you've got an AlarmClock class that looks like this:

class AlarmClock 
{
  public event EventHandler<EventArgs> Alarm;
  public void SoundAlarm()
  {
    if (Alarm != null)
    {
        Alarm(this, new EventArgs());
    }
  }
}

You would normally assign and implement an event-handler like this, with a separate defined method:

class Program
{
  public static void Main()
  {
    AlarmClock clock = new AlarmClock();
    clock.Alarm += new EventHandler<EventArgs>(clock_Alarm);
    clock.SoundAlarm();
  }

  static void clock_Alarm(object sender, EventArgs e)
  {
    Console.WriteLine("The alarm went off at {0}.", DateTime.Now);
   }
}

But with lambda expressions, you can do it like this, all in one method:

class Program
{
  public static void Main()
  {
    AlarmClock clock = new AlarmClock();
    clock.Alarm += (s, e) => 
    Console.WriteLine("The alarm went off at {0}.", DateTime.Now);
    clock.SoundAlarm();
  }
}

Most folks would agree that this looks cleaner, and is easier to follow. (At least, once you get the hang of lambda expressions, which admittedly have a rather odd syntax.)

The one time this doesn't work is when you need to be able to clear event handlers as well as assign them. The way that you normally do this is so:

public static void Main()
{
  AlarmClock clock = new AlarmClock();
  clock.Alarm += new EventHandler<EventArgs>(clock_Alarm);
  clock.SoundAlarm();
  clock.Alarm -= new EventHandler<EventArgs>(clock_Alarm);
}

But you can't do that with the lambda expression I used above, because you don't have any way to identify the lambda expression that you want to remove. I've seen a variety of folks asking questions about how to clear event handlers implemented as lambda expressions for this very reason.

It turns out that there are (at least) two ways to do it.

If you've got access to the source code for the class in question, you can write a public ClearEventHandlers() method that works like so:

class AlarmClock
{
    public event EventHandler<EventArgs> Alarm;
    public void SoundAlarm()
    {
        if (Alarm != null)
        {
            Alarm(this, new EventArgs());
        }
    }
    public void ClearEventHandlers()
    {
        Alarm = null;
    }
}

But if you need more granularity, or don't have access to the source, you can assign the lambda expression to a variable (e.g., "handleAlarm") before you assign it as an event-handler, like so:

public static void Main()
{
    AlarmClock clock = new AlarmClock();
    EventHandler<EventArgs> handleAlarm = null;
    handleAlarm = (s, e) =>
    {
        clock.Alarm -= handleAlarm;
        Console.WriteLine("The alarm went off at {0}.", DateTime.Now);
    };
    clock.Alarm += handleAlarm;
    clock.SoundAlarm();
}

Note that you need to assign the handleAlarm variable to null first, because otherwise the compiler complains about the first line of the lambda expression: it thinks that you're trying to muck about with an uninitialized variable. This is only sort of true, but to work around it, just assign the variable to null when you declare it.

The syntax for #2 is a bit unwieldy, but it can let you do things that would be difficult or complicated if you had to use a second method. For instance, within lambda expressions you can use local variables declared at the level of the containing function:

public static void Main()
{
    AlarmClock clock = new AlarmClock();
    EventHandler<EventArgs> handleAlarm = null;
    ManualResetEvent resetEvent = new ManualResetEvent(false);
    bool alarmSounded = false;
    handleAlarm = (s, e) =>
    {
        clock.Alarm -= handleAlarm;
        Console.WriteLine("The alarm went off at {0}.", DateTime.Now);
        alarmSounded = true;
        resetEvent.Set();
    };
    clock.Alarm += handleAlarm;
    clock.SoundAlarm();
    resetEvent.WaitOne(5000);
    if (!alarmSounded)
    {
        Console.WriteLine("The alarm didn't sound within the timeout period.");
    }
}

To duplicate this without lambda expressions, you'd have to declare the resetEvent and alarmSounded variables at the class level, which isn't nearly as clean, and could lead to some odd bugs if other methods were trying to use those same variables simultaneously.

Windows 7: Not Terribly Impressed

Windows Vista was never as bad as its reputation; and Windows 7 is not as good as everyone claims. It's not bad, but it still needs some work. And I dislike some of the design decisions.

Here are a few snippets from my own experience:

  • Something is wrong with the graphics drivers or graphics subsystem. Applications that worked fine under Vista keep "flickering" in Windows 7. And my Chrome browser keeps displaying a weird sort of static when it opens a new page, sometimes just for a quarter-second, sometimes permanently. Perhaps this is just a case of a bad video driver (I'm running an NVIDA GeForce 8800 GT), but I've installed the latest Windows 7-optimized driver, and it's still happening.
  • I've had two different blue-screens in my 48 hours of running Windows 7 RTM. Not a reassuring way to start a new relationship.

The computer has rebooted from a bugcheck. The bugcheck was: 0x0000001a (0x00001236, 0x878be008, 0x878be08c, 0x00070054). A dump was saved in: C:\Windows\MEMORY.DMP. Report Id: 081009-37970-01.

The computer has rebooted from a bugcheck. The bugcheck was: 0x0000008e (0xc0000005, 0x8c28b885, 0xb062b750, 0x00000000). A dump was saved in: C:\Windows\MEMORY.DMP. Report Id: 081009-30061-01.

  • If you turn off UAC (you pretty much have to, unless you're the sort of person who shops here), the UI gives you the impression that it's taken effect right away – and some parts of it have. But you have to restart your machine to actually be able to launch everything as an Administrator. This is no different from Vista, except that the UI is more honest in Vista, and tells you that you need to reboot.
  • I dislike the new metaphor for "pinning" applications to the taskbar. Among other things, it means that if I have multiple instances of an application open, I have to click twice to switch to the right one, rather than just once. It's also much more difficult and confusing to tell at a glance if something is open. I don't get why MS thought that clicking twice is better than clicking once. Luckily, you can turn this behavior off; but it's still an odd default.
  • I don't get the new file manager. It lists very prominently a whole bunch of shortcuts that I've never found useful, and hides the thing I actually do want to use, a direct tree view into my file system. On top of that, it doesn't synchronize the tree view with where I've navigated to in the file system. So the tree view can be sitting at C:\, long after I've navigated to, say, C:\source\slidelinc\branches\98a_SlideLincClient_ken. This last is a behavior that you can change if you know where to look, but it's a rather weird and unpleasant default.

Again, none of these things are killers, and maybe some people will like the new UI and its associated defaults. On the whole, Windows 7 is OK, though not terribly impressive. It feels to me like a not-very-ambitious bug-fix to Vista. I'll run it. But I continue to wish that MS could be more successful at their core operating system business.

Monday, July 27, 2009

Why WPF databinding is an awful technology

[Edited 10/23/09: I wrote this blog post when I was in a pissy mood, because I'd just wasted a day trying to get some WPF databindings to work. I still agree with my critiques of WPF/Silverlight databinding, and I think that MS really needs to rethink some aspects of their databinding implementation, especially their failure to implement a strongly typed DataContext. Still, if I were to write this post now, I'd be less inflammatory, especially in my opening paragraphs. Just to put it in perspective. But on to the original post . . . ]

I hate WPF databindings. Let me say that again. I hate WPF databindings. Of all the technologies to come out of Microsoft over the last five years, WPF (and Silverlight) databindings are easily the most awkward to configure, the least intuitive to use, the hardest to troubleshoot, and the easiest to get wrong. XAML itself is an ugly, verbose language, but once you get used to it, it's at least possible to get the UI pieces right with a little bit of troubleshooting. But setting up bindings in XAML is a different world altogether.

Microsoft claims, with some justice, that XAML/WPF/Silverlight provide for a very clean separation of concerns between the UI and the business logic, and this is true. But the reason to have a clean separation of concerns is to make things easier for the developer and the designer. And any benefits that may accrue from this theoretical achievement are entirely wiped out by Microsoft's piss-poor, half-assed, god-awful implementation. I've spent many painful hours troubleshooting WCF bindings, I've pored over bizarre Linq-to-Entity query side-effects until I couldn't see straight. But my time has never been so fruitlessly spent as trying to get my databindings right in XAML.

I could give you an example from nearly every single WPF form I've ever tried to design, but let's just go with this one, taken straight from this MSDN article. Create a simple form that looks like this:

<Window x:Class="WpfDatabindingDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <XmlDataProvider x:Key="MoreColors" XPath="/colors">
            <x:XData>
                <colors >
                    <color name="pink"/>
                    <color name="white"/>
                    <color name="black"/>
                    <color name="cyan"/>
                    <color name="gray"/>
                    <color name="magenta"/>
                </colors>
            </x:XData>
        </XmlDataProvider>
    </Window.Resources>
    <Grid>
        <ListBox x:Name="lbColor" Width="248" Height="200"
            IsSynchronizedWithCurrentItem="True"
            ItemsSource="{Binding Source={StaticResource MoreColors},
            XPath=color/@name}">
        </ListBox>
    </Grid>
</
Window>


And note that in the Visual Studio form designer, you see the data, as you'd expect:



But then try to run the application, and you'll note that the data doesn't show up.



There's no explanation as to why the data doesn't show up. There's no error in the Visual Studio debug output window. There's no error anywhere. The data simply isn't there. And note that this WPF binding error is taken straight from Microsoft's own documentation! But the biggest problem isn't that Microsoft's own proof-of-concept, easy-as-pie, this-is-how-you-learn-this sample code doesn't work: it's that your program fails silently, without an error, and without any debugging information.

If you want to know why it doesn't work, you have to add a short but ugly little piece of XAML to turn on extended diagnostics. (By "ugly" I mean, "You'll never remember what to type unless either (a) you Google it and then cut-and-paste the example into your code; or (b) this is the 57th time you've had to do this." I'm quickly approaching (b).)

First you have to register the diagnostics namespace:

<Window x:Class="WpfDatabindingDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:diagnostics="clr-namespace:System.Diagnostics;assembly=WindowsBase"
Title="Window1" Height="300" Width="300">

And then you have to turn the trace level on to "High".

<ListBox x:Name="lbColor" Width="248" Height="200"
    IsSynchronizedWithCurrentItem="True"
    ItemsSource="{Binding Source={StaticResource MoreColors},
    XPath=color/@name,
    diagnostics:PresentationTraceSources.TraceLevel=High}">

And then when you run your app, you finally see this error in the Visual Studio output window:

System.Windows.Data Error: 47 : XmlDataProvider has inline XML that does not explicitly set its XmlNamespace (xmlns="").

Another hour or so of poking around on Google, and you realize that you have to add the blank namespace to your XML data, like so:

<XmlDataProvider x:Key="MoreColors" XPath="/colors">
    <x:XData>
        <colors xmlns="" >
            <color name="pink"/>
            <color name="white"/>
            <color name="black"/>
            <color name="cyan"/>
            <color name="gray"/>
            <color name="magenta"/>
        </colors>
    </x:XData>
</XmlDataProvider>

And after only two or three hours of troubleshooting (if you're lucky), you've fixed your problem. But my point is that this isn't something that you should have had to troubleshoot. This is something that either (a) should have worked out of the box, as the MS sample code led you to believe, or (b) should have thrown a simple and explanatory runtime error, or (c) even better, should have been caught at compile-time, since everything that the compiler needed to know to realize that this wouldn't work is already there in the source code. Any of those three options would have been fine. But it's simply not acceptable to fail silently, without even a hint about what's going wrong.

This is just one example, of course.  I could produce plenty of others, but then this post would get even more boring and repetitive than it is.

The fundamental problem with Microsoft's data binding implementation is that it's a significant step backwards in terms of computer language theory. Folks have known for years that strongly typed languages help you write better code. It's better to catch an error when you're writing your code than to catch it at compile-time;  it's better to catch an error when you compile your code than to catch it at run-time; if the compile doesn't catch it, at least your automated tests should be able to; and of course it's better to throw an error (even at runtime) than to fail silently. But the way that MS has implemented bindings, there's no way to catch errors at any of these steps: neither when you're designing your form, nor when you're compiling your app, nor when you're running your tests, and most inexplicably, not even when you're running your application. The minute you type "{Binding…}", you're working without a net.

For instance, MS could have implemented the XmlDataProvider in such a way as to allow (or even require) you to specify an XSD file as a parameter.  If they had, not only could you get Intellisense when typing in your bindings, but you could also receive a helpful compiler error if it sees, say, that you've bound your ListBox ItemsSource to an element or an attribute that doesn't exist. Similarly, if you're using an object data source, you should be allowed to declare the type up-front, and then only bind to properties available in that type. And if the binding fails at run-time, e.g., the object isn't available, or doesn't have the properties available that you're trying to access, you should get exactly the same sort of error that you'd get in C# if you tried to access a non-existent property on a class from a third-party DLL. In other words, it should not only fail (which it does now), but it should tell you why it's failing, and it should tell you this before you ever run your application.

In other words, MS should have made the DataContext property strongly typed. It would allow the Visual Studio tools to do in milliseconds what you can only do now through hours of futile Googling and days of pointless troubleshooting. Microsoft decided that they wanted the extra flexibility that comes from not having any type safety, and I agree that there are scenarios where this flexibility is helpful. But there's a reason nobody writes enterprise applications in VBScript: type safety is orders of magnitude more important than flexibility when working with complex systems. And I would willingly, in a heartbeat, give up some extra flexibility, in exchange for knowing immediately why my bindings won't work, instead of waiting until I run the app to find out that they don't work and not having a clue as to why.

Undoubtedly this would require a pretty significant rethinking of how data binding works with XAML. Neither WPF nor Silverlight would look the same when they were done. But that may be exactly my point. MS had the chance to get WPF right; and they missed a huge opportunity. And until they fix this, their databinding implementation will remain a significant drag on the adoption of WPF and Silverlight. Without strong typing, WPF databinding is just too difficult to use.