This blog attempts to be a collection of how-to examples in the Microsoft software stack - things that may take forever to find out, especially for the beginner. I see it as my way to return something to the Microsoft community in exchange for what I learned from it.

08 August 2010

Some may have read my foray into using Windows Phone 7 to view maps, utilizing a Multi Scale Image (msi), MVVM Light and some extension properties. This application works quite well, but being mainly a study in applied architecture, the user experience leaves much to be desired. Studying Laurent Bugnion’s Multi Touch Behaviour got me on the right track. Although Laurent’s behaviour is very good, it basically works by translating, resizing (and optionally rotating) the control(s) inside the FrameworkElement is is attached to. For various reasons this is not an ideal solution for a map viewer.

So I set out to make my own behaviour, the first one I ever made by the way, and it turned out to remarkably easy – less than 90 lines of code, including whitespace and comments:

Method AssociatedObject_ManipulationStarted just records where the user started the manipulation, as well as what the MSI ViewportOrigin was on that moment. Method AssociatedObject_ManipulationDelta then simply checks if the delta event sports a scaling in x or y direction – if it does, it calculates the properties for a new ‘logical point’ to be fed into the ZoomAboutLogicalPoint method of the MultiScaleImage. If there is no scaling, the user just panned, and a new ViewportOrigin is being calculated in the MSI’s own coordinate system which runs from 0,0 to 1,1. And that’s all there is to it.

And there you go. You can now zoom in and out using two or more fingers. That is, if you have a touch screen. If you don’t have a touch screen and you were not deemed important enough to be assigned a preview Windows Phone 7 device then you are in good company, for neither have I, and neither was I ;-). But fortunately there is Multi Touch Vista on CodePlex. It needs two mice (or more, but I don’t see how you can operate those), and it’s a bit cumbersome to set up, but at least I was able to test things properly. So, don’t let the lack of hardware deter you getting on the Windows Phone 7 bandwagon!

What I learned from this: behaviours will give an interesting twist to design decisions. When is it proper to solve things into behaviours, and when in a model by binding? For me, it’s clear that in this case the behaviour wins from the model – it’s far easier to make, understand and – above all – apply! For now, just drag and drop the behaviour on top of a MultiScaleImage and bang – zoom and pan. No complex binding expressions.

Incidentally, although this behaviour was created with maps in mind, in can be applied to any MultiScaleImage of course, showing ‘ordinary’ image data.

For those who are not very fond of typing: the sample map application with the behaviour already added and configured can be downloaded here. For those lucky b******s in possession of a real live device: you will find the XAP here. I would very much appreciate feedback on how the thing works in real life.

And then I noticed something odd: according to the compiler, ConcreteProp1 and ConcreteProp2 were not defined. Even worse is the situation when you choose to upgrade your properties not using lambda expressions, but PropertyInfo objects, like this:

because this will compile - and run. Until you create a second child class MyConcreteClass2, instantiate it, then instantiate a MyConcreteClass1 – and then you will get a cryptical runtime error message saying “Can not register property ConcreteProp1Property after containing type MyBaseClass has been instantiated”.

Fortunately the CSLA framework comes with sources, and after some rooting around I found the culprit, if you can call it that, in Csla.BusinessBase:

Although MyConcreteClass1 inherits from MyBaseClass, MyBaseClass inherits in turn from templated class BusinessBase<MyBaseClass>. Therefore, in RegisterProperty called from MyConcreteClass1 T is still MyBaseClass. It does not matter that I actually called it from a child class. So what happens is that all the statics are defined in the base class MyBaseClass. If you are using the lambda variant to register, the compiler saves your *ss, but if you use the PropertyInfo method something weird happens. Remember, statics in a class are initialized as soon as you touch any one of statics. So what happens is: you instantiate your concrete child class, immediately the statics of both the concrete and the base class are initialized, and all the properties are registered in the base class. If you try to instantiate a second concrete child class, Csla finds that your base class properties are already initialized, and the dreaded “Can not register property ConcreteProp1Property after containing type MyBaseClass has been instantiated” error message appears.

Now you can of course change the way you implement classes. I might make MyBaseClass generic as well, then T changes along. But when upgrading an existing API out of a situation in which direct inheritance used to be perfectly legal, it’s a different story.

There are actually two ways out of this. The first one is: use PropertyInfo, but explicitly name the object type to which the property belongs

This works, but I like the solution below better, because that uses lambda expressions again and so your friend the compiler ;-) can help you catch typo’s. The only way I see to realize that is to add a static method at the bottom of your class

The drawback of this solution is, of course, that you have to define a static RegisterPropertyLocal in every inherited class you define. But at least you will be saved from typos and weird runtime errors.

Now you are ready to upgrade, but I would recommend recording some macros to do the actual syntax change, unless you are very fond of very dull repetitive typing jobs ;-)

Feedback, comments and tokens of appreciation

If you spot things that are incorrect, or if you don't understand what I mean, please drop a comment on the offending article and I will help you ASAP. You can e-mail me at joostvanschaik at outlook dot com or contact me via twitter.

If you find the information on this blog useful (and apparently some 600+ people per day do on average) please let me know as well, that encourages me to keep doing this. Or do tell others - that made me an MVP; who knows what more it might bring ;-P

Disclaimer and legal stuff

Although I take great care in providing quality samples, all postings, articles and/or files on this site are provided "AS IS" with no warranties, and confer no rights. The views expressed on this blog are strictly my own and do not necessarily reflect the views of my employer, or anyone else on the planet for that matter.

I usually make original content, sometimes building upon other people's work. Sometimes I explain things that can be found elsewhere because I felt what I read was not clear enough for my limited mind so I explain it the way it finally clicked with me. In all cases I take great pains to be sure to link to people or articles who deserve the credit. If you think I have shortchanged you on the credits please let me know.

Please note, I do not work for Microsoft and while I proudly wear the title of "Microsoft Most Valueable Professional", my opinions, files offered, etc. do not represent, are approved of, endorsed by or paid for by Microsoft. The only power behind it is me and my sometimes runaway passion for parts of Microsoft's technology.