I’ve been working for some time now on implementing a framework to enable coding WPF applications that follow the Model-View-ViewModel pattern. I’ve found several "dark corners" of the WPF libraries where the designers have made choices that make it difficult to extend the library. For instance, the constructors on the routed event arguments are not public, making it very difficult to reroute events back into the ViewModel. It’s been frustrating, but you could always understand why the design was the way it was, even if I thought it was a mistake that should be rectified in some later release.
However, I just ran into one that just astounds me. I’ve been trying to implement a scheme in which the ViewModel would be able to effect navigation with no knowledge of the View. It’s similar in concept to the Java Struts concept where navigation information is stored in a configuration file and is initiated via keywords instead of actual knowledge of the view (if that made sense to anyone). Anyway, I had something working fairly well, and reached the point where I had to start worrying about getting "return values" back to the VieModel. This is when I discovered something interesting. When you follow the normal pattern for handling PageFunction code, like this:
class MyPayge : Page
// … other stuff removed
MyPageFunction f = new MyPageFunction();
f.Return = MyPageFunction_Return;
void MyPageFunction_Return(object sender, PageFunctionEventArgs<int> e)
// what ever
(That was done by memory and likely has several signature and syntax issues… it’s for illustration only.)
Knowing how delegates work, you’d expect that the addition of the MyPageFunction_Return would cause the MyPageFunction instance to maintain a reference to the MyPage instance, keeping it alive, and that’s what the method would be called on. A little experimentation, though, proved this to be wrong. The old MyPage instance is collected, a new one is created, and that’s the instance the MyPageFunction_Return is called on. I have no idea how it’s even possible to do that! I started to do a Google search, and that’s when I discovered something interesting. Did you know the documentation for the Return event indicates you must only handle the event from a method on the calling page? If you try and handle it somewhere else, an exception will be raised! Seems to me they implemented some hack to allow the event handler to be retargeted to the new Page instance when you return (this is the default behavior, unless you assign KeepAlive="True"). This hack either required or made it easier for them to document that you must target the calling Page with any event handler. Does that sound like a hack to you, or what? Worse, it leads to many dead ends when using PageFunction. My scenario tries to handle the return event in the ViewModel, but the situation I foundusing Google was trying to encapsulate all of the functionality in a custom control, which seems like a very common thing to want to do.
I love WPF. It makes a large part of the development effort simple, while allowing you to still do amazing things with your UI. However, there’s enough of these dark corners to frustrate the heck out of me.