Friday, May 01, 2009 

Einstellung

As I mentioned in previous posts, one of the projects I've been recently involved with is a complete rewriting of the GUI layer for a rather large system. We want to move from an MFC-based framework to .NET, mostly to improve productivity.
Initially, we'll basically move the GUI as-is, without re-designing the human-computer interaction. Therefore, it would pay to recover as much information as possible from the existing system, and do it automatically.

Among other things, we have about 250 dialog boxes to port, so I thought it would be a good idea to write a translator from the Win32 RC format to whatever new format we need. This way, we can recover layout (positioning and sizes) and also translate each control to their nearest equivalent.

That means, of course, that we know the target, and today, the .NET game boils down to choosing between Windows Forms and WPF. The choice is rather hard, althogh I know many programmers would jump immediately on the WPF bandwagon. Anyway, as we discussed the translator above, the project manager observed that WinForms stores everything in code. If we ever have to do this kind of change again, she said, we will miss the simplicity of RC. XAML would make layout and controls easier to move to another technology, just as RC.

That's true; I don't particularly like the idea of having to parse C# to recover layout information, control initialization parameters, and so on.

Funny thing is, for a while I got trapped in this parsing concept. I guess it has to do with education. Any computer scientist will recognize this as a parsing and translation problem. It's a well known problem frame. And that calls for a parser, of course :-).

It took me a while to realize I didn't have to write a parser at all: I could just use reflection! To test the idea, I wrote a simple C# program (about 60 lines of code) which takes a form and recursively dumps layout and initialization parameters in an XML format.

For instance, given this form:



where the blue rectangle is a panel, and the label and button are nested controls, I'll get this XML.

The idea is pretty simple: I dump every property without a Browsable(false) attribute, that is, everything that you can change at design-time. If the Controls collection is not empty, I'll recurse into it. The nice part is that it could be made to work also for dynamic controls, created at run-time and not a design-time. Just call the translator after all the controls have been created, and that's it.

Things could be easily improved. Right now, I don't handle collections (see bindings), non-visual components, and I dump every single property. It would be useful, perhaps, to dump only values that have been changed. That's easy, just create a control of the same class on the fly, and check for differences. Piece of cake.

Now, I wish I could say I thought of this through my understanding of the forcefield :-). But I can't. It just came to me. Dunno how. The problem, of course, is moving past the Einstellung effect of education. What can I say? Keep your mind open, practice lateral thinking, never give up :-). And yeah, well, keep an eye on that forcefield, as that may help too...

Labels: , ,

Saturday, April 14, 2007 

More .NET / STA madness...

I was just writing a piece of code to coordinate two cooperating processes under Windows, and that involved signaling a kernel object and waiting on another.
Now, the Win32 API has a nice function to do just that: SignalObjectAndWait. I was writing the code in C#, but hey, the WaitHandle happens to have a similar function (SignalAndWait). A WaitHandle isn't much more than a wrapper over kernel objects, so that wasn't really surprising: that function ought to be just a wrapper over the API.
What was surprising was the exception I got trying to use it: apparently, you cannot call that function from an STA thread. By the way, my thread happened to be in an STA just because of code Visual Studio itself had generated. I'm not using COM and I shouldn't be bothered with this stuff.
Again, COM is raising its ugly head behind all the .NET stuff. Again, some framework implementer thought it was wise to protect some unaware user from getting his message pump stuck. Which is kind of ridiculous, as you can just as easily get your message pump stuck by decomposing the forbidden call in a signal and a wait. Oh well...

Labels: ,