Thursday, May 28, 2009

Can I write a WPF+Silverlight App?

In this post we explore the question, “can it be done ?”

If we were asking “can we write one application that targets both Windows Forms and ASP?”, I would say “sure but there will be little code in common.” The data access and business layer perhaps … if you are careful. At the application, UI, and presentation levels there are irreconcilable differences. Maybe 10% to 20% of the code can be the same in both environments. That’s my opinion based on recent observations of such a case.

I believe the story is much better when it comes to writing a single application with both a Silverlight and a WPF face. It helps enormously that they share the same presentation philosophy, the same UI specification language (XAML), the same client-side execution model, and many of the same components.

According to my calculations for Silverlight v.3, you will be able to re-use between 52.6% and 93.7% of your code-base to support both platforms.

I’m sure you are wondering … why the big spread?

Let’s walk up the application stack and evaluate the opportunities for re-use at each level.

Let us stipulate that we can use the same database regardless of client technology.

You can write a 99% common data access and business layer … at least you can if you adopt my company’s DevForce product; Rocky’s CSLA.NET for Silverlight may be another option.

This high percentage presupposes that you are prepared to use the same asynchronous server-access in your WPF application that you must use in your Silverlight application. Silverlight only permits asynchronous communications with the server. This means you can’t issue a query and block the UI thread until the data arrive. You must design and develop a user experience that keeps going while the program is waiting for data.

Some argue that this is a good idea anyway. Unfortunately, only a handful of developers have experience writing non-blocking UIs. You should anticipate a slower initial pace of development as the team learns to program in async style.

North of 90% of your non-visual application layer classes can be written once and “shared” between regular DotNet and Silverlight assemblies.

The word “shared” deserves at least passing comment. The DotNet and Silverlight assemblies often look alike but they are not the same. Your Silverlight projects must be compiled against Silverlight assemblies; your WPF projects must be compiled against DotNet assemblies. Obviously you don’t want to duplicate your code. The trick is to arrange for the two kinds of project – Silverlight and WPF – to share the same physical code files. You can do this manually in Visual Studio but it is far more convenient to use the Project Linker from Microsoft Patterns and Practices.

Rocky Lhotka presented techniques for coordinating bi-platform development in his Tech Ed 2009 talk “WUX313 Sharing Code between Your Microsoft .NET Framework Applications and Microsoft Silverlight”; I hope a public link to his talk becomes available soon; you can always ask Rocky for it.

You can rely on Prism to glue your application together because Prism was built to support multi-platform scenarios.

XAML is the Problem

It is the visual classes – the XAML files in particular – that present a significant challenge.

There is no issue if you’ll have one look for Silverlight and a different look for WPF. The Model-View-ViewModel pattern makes it easy to share the non-visual Model and ViewModel classes across platform; all you have to do is whip up your separate View XAML files and – shazam! - you’re done.

Did he really say “whip up your separate XAML files …”? He’s got to be kidding!

Yes I am kidding. Unless you’re auto-generating your views (which I cannot recommend), you will invest heavily in crafting XAML that both looks good and enhances user productivity.

If you decide to produce different views for each platform – and there are strong arguments for doing so – you’ve just identified the 50% of your application (measured in effort if not in kilobytes) that will differ across platform.

On the other hand, if you intend to use the same views – the same XAML – on both platforms, you can hope for 90+% XAML re-use, thus bringing your total for the application to the coveted 93.7% mark.

So there’s the spread: 50% common code if you have separate views and 90+% if you have shared views. No brainer, right?

Not so fast. The effort and compromises essential to re-use XAML can be daunting. WPF and Silverlight XAML are similar but maddeningly different in critical details. I won’t explore these differences in depth here but I will call out a few of the obstacles.

First, there are many features in WPF that don’t exist in Silverlight 3. Implicit styling is one example but there are numerous others. Because WPF is (mostly) a superset of Silverlight, if you design for Silverlight you’ll have a better chance of retargeting the XAML for WPF.

Second, there are features of Silverlight 3 that are not yet in WPF-proper such as the DataForm. It will show up eventually but you’ll have to wait for it.

Third, some of the features available on both platforms happen to be declared in different namespaces; Shawn Burke posted about this back in November 2008 and I don’t believe the incompatibilities have been resolved in Silverlight 3. If I recall (to be confirmed) the VisualStateManager is one crucial example of a component that is not in the same namespace on both platforms.

As it happens, you can define a namespace that doesn’t exist in your XAML file as long you don’t reference it. Your app will build and run fine with a phantom namespace declaration such as xmlns:dummy="dummy". You might think this would provide some relief. You could define one prefix to refer to the WPF namespace and and a second prefix to  refer to the Silverlight namespace. But which prefix do you use when you want to declare a VisualStateManager? You can’t toggle prefixes.

Fourth, XAML does not support compiler directives (#if/#else/#endif), namespace aliasing, or partial class files. Why does this matter? Because we use all three of these techniques to manage the inevitable differences between WPF and Silverlight implementations … when managing such differences in code.

If XAML supported compiler directives, for example, we could work around the dueling namespace prefix problem with something like this:

<!-- This does not work! -->

You can find a XAML pre-processor that will recognize and resolve compiler-like directives in XAML. Sadly, you won’t be able to use the visual design tools (e.g., Blend) because they won’t recognize or honor the directives.

I hope you will join me in asking Microsoft to make support for conditional XAML a priority.

Netting It Out

It is reasonable to contemplate writing a single application that sports both a WPF and Silverlight client experience.

It isn’t automatic. Microsoft could make it easier … and eventually will. But the important take away is that you can do it and it is relatively easy today if you are willing to write distinct Silverlight and WPF XAML files.

In a future post we’ll talk about who wants to write a WPF+Silverlight application and why.


Tim Erickson said...

Other pain points I have encountered and worked around:
* Different style definition paradigms/capabilities (trigger-based vs VSM-based)
* XML handling (XmlDocument vs XDocument)
* ToolTip declarations in XAML
* differences in the Measure/Layout cycle in Custom Control logic
* different schemas necessary for storing/resolving/loading application resources remotely/as assembly resources/in the Xap/on the local filesystem
* Storyboard manual control (Begin() vs Begin(frameworkElement, true) and Stop() vs Stop(frameworkElement))
* MediaElement manual control behavior
* handling/implementing full-screen support
* BitmapImage differences (WPF requires Begin/EndInit() calls)
* SetSource(stream) vs StreamSource = stream

I worked around the Visual State Manager issue by including the VSM portion of the WPF Toolkit project on Codeplex in our source. As such,all VSM usages have had to be wired in code-behind instead of declaratively in XAML. Obviously this makes it difficult to implement styling.

I wrote the app originally in Silverlight and adapted it to work in WPF. It was cross-compiled via linked source files -- including XAML -- to WPF. It is a commercial application and yes, it made sense to do it this way, but obviously it is not for the faint of heart, as you say. Probably the single most difficult pieces were massaging the control styles to be compatible across both platforms, and handling the packaging, addressing and accessing of content resources. Also there was quite a bit of pain at times in getting a couple of complex Custom Controls to work -- it seems Silverlight can be quite a lot more flexible/forgiving in Custom Control code requirements.

Phil said...

In business apps, generally the argument for writing something in WPF is to have the local access to services (printer, hardware etc), not to get "3D" and other WPF niceties. Silverlight 3 (IMO) is ready for LOB app development...more than ready.

--- IDEA ---

So, if that is the case (which I believe it is), for the "I need a bar code reader" type scenarios I'm considering an approach where you "host" SL within a rich-client container, and through a JavaScript bridge access your full-trust code which is locally installed. You app is installed locally, you have full-trust assemblies, so can do the local manipulation of the system or access to printers, scanners, barcode readers etc.

[NB: This is not the same as the FIT client install, because your getting access to a full-trust installation that you setup. The FIT client install is still in the sandbox]

This would essentially boil down to having the local, full trust container look-n-feel like a service'd be making calls to methods by serializing parameters (via DataContract) and passing them through JS as strings. Far from this being a down-side, I could see how this might force good architectural design...ensuring the UI really is View...and not tangled up with stuff that should be there.

Now, the hypothesis (unproven at this stage) is that the effort of setting up this container (once) would be less than the ongoing development friction and maintenance woes of cross compilation and incompatibilities that Ward describes. You also get the ability to use the same UI (or parts of it, if it's nicely modularized a la PRISM) in the browser - and instead of accessing the local-full trust code via the JS bridge you hot-swap to calling a web-service.


I would love to hear people either beat up on this idea and tell me why this is crazy, or offer other opinions and insights.

I've done a bit of dual-WPF/SL development (mainly building out PRISM examples) and it's has a bit of a smell to it. I'm optimistic that a surgical containment strategy may be a smart move? What do you think??

Ward Bell said...

@Tim Erickson - Thanks for this inventory ... a big help.

Seems to me that one can work around many of the issues by encapsulating differences in a platform-agnostic wrapper.

There would be lingering XAML markup challenges ... some of which could be addressed with Attached Properties (volunteers?). You've got to hope MS gets to this soon; the VSM will surely be a first class citizen by 2010.

Image resources are just completely different; I guess you bite the duplication bullet on that one.

All in all, these strike me as nits ... PITAs for sure ... but not huge hurdles, especially if you're prepared to bend on the "no-code behind" mantra.

Ward Bell said...

@Phil - Interesting idea and not crazy ... but I'm cringing. A homebrew full trust container with JS-based service boundary? Yikes!

I don't have a lot of experience in this area either ... but I'm still inclined to believe that WPF/SL dual-port development will be easier now, get easier in future, and (perhaps more important) will be the standard approach.

Tim Erickson said...

"Seems to me that one can work around many of the issues by encapsulating differences in a platform-agnostic wrapper."

That's what I did as much as possible, but as you say this works much better the more you're willing to do in code-behind instead of declaratively in XAML. There's just no getting around the need for some sort of #if SILVERLIGHT conditional preprocessing, which is simply not available in XAML.

"There would be lingering XAML markup challenges ... some of which could be addressed with Attached Properties (volunteers?)."

That's actually exactly how I worked around my issue with the Tooltips - wrapping the different SL/WPF differences in a custom attached property, XToolTipService, which routed the calls correctly to the existing ToolTipService.

"Image resources are just completely different"

And MediaElements even more so. In Silverlight you can set the source of a MediaElement to a stream, whereas you can't in WPF. I had to actually write the streams out to a temp file and pass back a file URI to that temp file in order to load packaged MediaElement assets in WPF. Yuck. Silverlight +5.

"I guess you bite the duplication bullet on that one."

Big time! I had significant problems as I needed to pre-load/buffer assets in the Silverlight context without getting in the way of the WPF app. These assets could be a)Packaged in the .xap b)Available via a webservice c) Available via a relative http URI d) Packaged in the WPF .exe as PEXE resources or e) Available via a relative file URI. Accomodating all these different possibilities was actually a bigger PITA than the VSM.

"All in all, these strike me as nits ... PITAs for sure ... but not huge hurdles, especially if you're prepared to bend on the 'no-code behind' mantra."

I would say several are nits, but several are much more - for example I spent days ferreting out why certain Custom Controls worked in SL, but then not in WPF (I should blog a couple of those) that turned out to be silly differences but huge pain. And this is the recommended way (SL->WPF vs WPF->SL). Probably more often than not these issues resulted from mistakes I'd made in the first place (in MeasureOverride and ArrangeOverride specifically), that SL handled gracefully but WPF didn't. But I've found this to be generally the case, that SL tends to be more flexible/resilient to what I'm trying to do than WPF -- within what they're each *capable* of doing, of course.

The different style paradigms caused me pain even though I was more than willing to bend on the no code-behind mantra -- I outright was bent over backward.

Probably I would have had an easier time if I had been willing to break down and put certain things in separate XAML files, rather than insisting on sharing all the XAML. But I had to draw the line somewhere.

Overall, it was a fascinating technical challenge, but certainly only relevant in very specific situations, which my circumstances happened to fit. I look forward the the "When would one *want* to do this?" discussion :-)

Phil said...

>> Interesting idea and not crazy ... but I'm cringing. A homebrew full trust container with JS-based service boundary? Yikes! @Ward

Yeah - I'm cringing too! I guess I'm equally cringing at the alternative :) ... :(

>> .. but I'm still inclined to believe that WPF/SL dual-port development will be easier now, get easier in future, and (perhaps more important) will be the standard approach.

[Some Wishful Thinking Follows:]

It seems to me that MS could really help out here by doing this for us (ie. not "homebrew") and make a "Silverlight Control" that can be hosted within a WPF window, rather than a browser. This would then run the Core-CLR and UI just as though it were in a browser, but it's in a WPF Window, with no cross compiling tricks. Then provide some nice clean interop system (like the way COM Interop is done).

I'd anticipate that someone would argue "But Silverlight intrinsically needs to be hosted in the browser to operate" ... but that's just a design decision. It does it's own rendering. If you can build a plug-in to Safari on the Mac, it should be possible to build a plug-in to a WPF window, right?

This does not present some new kind of security hole for Silverlight just because the control is not in the browser. The usual rules of engagement apply to deploying the full-trust WPF app.

The only reason I can think for Microsoft NOT doing this is to somehow protect the WPF franchise or investment. I could be wrong and missing the point on that as a 'business issue'. I'm not sure WPF needs protecting, so why not just do this? (Or alternatively, why not just stop investing in WPF altogether and put all the resources into moving Silverlight forward).

It may be negative of me, but right now I just see the existence of WPF (as a separate, slightly different thing) as an expensive compatability problem. There's nothing significant it brings to the table to build the UI for LOB apps (and a hell of a lot more than LOB) "IF" you can get some interop from the UI control up into that full-trust container.

[Geez - hope that didn't come off as a rant...I meant it as thoughtful wishful thinking. Hell, it may even be in the cards.]