How different is WinRT really?
Update 29 February 2012: There is now a post covering the Consumer Preview/Beta changes: How different is Metro Style (WinRT) development really? The beta post
Note: Before you read this post, it is using the public technical preview of Windows 8, VS 11 & .NET 4.5, so I expect some issues will likely be resolved in later releases.
Recently, I have been working on an MVVM framework (it’s the new Hello World project for developers) and wanted to make sure it worked with WPF, Silverlight, and the new hotness: WinRT (or Metro or Windows 8, depending on who you ask). So I started to retrofit the framework and build a demo application at the same time to show it off. I quickly found a bunch of issues you need to take care of when moving to WinRT.
Namespaces
It has been mentioned since //Build that we will have new namespaces, so this should not be a surprise. In my MVVM framework, it looks like this:
#if WINRT
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Core;
using Windows.UI.Xaml.Data;
#else
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Controls.Primitives;
using System.ComponentModel;
#endif
You should be able to spot an almost one-to-one mapping there, except for the last few—but we’ll get to those.
Duplication of INotifyPropertyChanged, ICommand, & INotifyCollectionChanged
This issue will affect every developer in WinRT, and it’s a massive fail on Microsoft’s part. The INotifyPropertyChanged, ICommand, and INotifyCollectionChanged interfaces—used in every XAML-based system—are not the same ones used in WinRT XAML-based systems. In fact, the framework has two of each—one in the old namespace and one in the new namespace. So your databinding will just break with no compiler errors! To fix this, you must switch the namespaces.
ObservableCollection<T> is broken
Hand in hand with the previous issue is that the trusted ObservableCollection<T> no longer works. It implements the old (and wrong) INotifyCollectionChanged, so all bindings to it are broken. You must bind to a class that implements IVector<T> instead. One problem: The framework doesn’t ship with an implementation! So to bind to collections, you need to build your own collection class or grab a shim (as I chose to).
Proxies & WebRequest
😊 😊 I used WebRequest in the demo to pull images from Flickr, and you know what just worked? Proxy settings. The first time ever in .NET! I hope whoever decided that proxies should just work gets an award! 😊 😊
Reflection has added a whole new layer
In non-WinRT, we can get all kinds of info on a type directly by working with that type (e.g., Type.GetType). However, in WinRT, that’s not the case! Type.GetType is no longer enough. Once you’ve gotten the type, you need to call GetTypeInfo() on it to retrieve all the information. Why this extra layer exists is beyond me.
#if WINRT
if (control != null && typeof(ButtonBase).GetTypeInfo().IsAssignableFrom(control.GetType().GetTypeInfo()))
#else
#if SILVERLIGHT
if (control != null && typeof(ButtonBase).IsAssignableFrom(control.GetType()))
#else
if (control != null && control is ICommandSource)
#endif
#endif
MIA Types & Methods
For an MVVM framework, you’ll need a bit of reflection, so the first thing I noticed is that the super-handy Type.EmptyTypes is gone! So to get as close to a single codebase as possible, I’m now doing this nonsense:
🙁
#if WINRT
private readonly Type[] EmptyTypes = new Type[] { };
#else
private readonly Type[] EmptyTypes = Type.EmptyTypes;
#endif
There are a fair number of missing properties and methods—like Type.GetType lacking some of its beloved overloads:
#if WINRT
var viewType = Type.GetType(viewName);
#else
var viewType = Type.GetType(viewName, true, true);
#endif
Lastly, don’t expect familiar methods like GetConstructor, GetMethod, or GetProperty to exist. Now, we must work with collections we can iterate over. I admit it’s a better option, but some helper methods would’ve been useful.
public static MethodInfo GetMethod(this Type type, string methodName, Type[] parameters)
{
var results = from m in type.GetTypeInfo().DeclaredMethods
where m.Name == methodName
let methodParameters = m.GetParameters().Select(_ => _.ParameterType).ToArray()
where methodParameters.Length == parameters.Length &&
!methodParameters.Except(parameters).Any() &&
!parameters.Except(methodParameters).Any()
select m;
return results.FirstOrDefault();
}
So be aware: when working with reflection in WinRT, expect things to work differently.
User Controls must be created on the main thread!
This one I don’t understand at all. In WPF and Silverlight, a background thread can create a user control and pass the content to a Window on the main thread—a super useful feature for creating content and then popping it into the foreground almost instantly! However, WinRT has different ideas: all user controls must be created on the main thread—yuck!
#if WINRT
Window.Current.Dispatcher.Invoke(CoreDispatcherPriority.High, (s, e) =>
{
#endif
shell = (IShell)typeof(TShell).GetConstructor(EmptyTypes).Invoke(null);
#if WINRT
}, this, null);
#endif
Simpson Fail Image from: http://www.thebuzzmedia.com/creator-of-wikipedia-fail-fast-fail-often/
Double face palm image from: http://tvtropes.org/pmwiki/pmwiki.php/Main/FacePalm