• Porting WPF applications to Windows Universal Apps

    by  • May 20, 2015 • .Net, Programming • 1 Comment

    This article is part of a series of articles I have written covering WPF; improving its performance, its quirks and associated workarounds, as well as porting it to WUA Applications:

     

    Windows Presentation Foundation or WPF is one of the few things of the Windows Vista era that has survived, or that people actually use and enjoy. It was originally called Avalon and was the first step away from GDI32 WinForms based developing which was becoming more and more difficult to create rich engaging applications.

    WPF used a XML based UI markup called XAML, with a C# code behind.

    Roll on 2015 and the Microsoft Build Conference, with Windows 10 and Microsoft hopes to evolve WPF. Microsoft’s long term plan is to unify Silverlight, WPF to Big Windows, Windows Phone development and all XAML based development under the banner of Windows Universal Apps (WUA).

    This means one app could potentially run on Windows 10 Phone, Windows 10 IoT and full Windows 10. The notes below were taken while I was porting a WPF application running on an Intel Core I7 to a Raspberry Pi 2 running the Windows 10 IoT stack. Yep that’s right, having an app previously needing an i7 running on a £30 Raspberry Pi.

    This is all great news, apart from the glaring problem. Not all devices are created equal, different devices have different performance characteristics and different accessories e.g. Your PC may not have a GSM module, and your phone doesn’t have a mouse.

    So Microsoft had to comprise and refactor most of the System .NET library and the core Xaml libraries.

    This post goes through some of the issues and challenges I have encountered while porting a Windows desktop based WPF application to a Universal App. Not all the changes here are solely related to WUA but in general changes to the Windows Universal libraries.

    Reference, Library and Using Changes

    The refactoring of the core libraries has pushed most of the XAML and UI libraries to the Windows.UI namespace.

    Note: What would have been System.Windows.Forms is now Windows.UI.Xaml.Controls

    XAML and Graphics differences

    The Visual Class is stored under Windows.UI.Composition

    The Visibility Enum Windows.UI.Xaml.Visibility no longer has a Hidden property, replace with Collapsed.

    Note: that this removes it from the render tree and so can cause lag when re-rendering.

    Can’t set a FrameRate via DesiredFrameRateProperty on a TimeLine.

    Note: Manual control of timeline and animation properties may be required, apps may use more CPU as they could run at a default FPS of the platform (usually 60 FPS.)

    Pre-cached SolidColorBrushes (Brushes) doesn’t exist for each colour, they have to be created manually through the SolidColorBrush constructor with a Colour.

    StreamGeometry is not supported, the closest variant is PathGeometry or just Path. <StreamGeometry>…</StreamGeometry> becomes <PathGeometry Figures=”…” /> Another way is to convert it to a Path object with the figures being the Path.Data. This could be stored as a Static resource and then referenced via a ContentControl binding {Binding Source={StaticResource=…}}

    OpacityMask does not exist. Thus semi transparent controls could be achieved via use of transparent imagebrush, layering shapes or background image (PNG) with transparencies

    EnableClearType is not supported as an attribute to BitmapCache User Control CacheModes. Text may not appear correctly if cache resolution is too small.

    Assets can not be frozen in graphics memory.

    Note: This is due to memory constraints on mobile devices. This is now all done dynamically. Developers will need to make sure that assets are still responsive.

    VisualBrush is not supported, you can clone an image via ImageBrush however it is undetermined yet whether this can render video.

    The Media.Effects namespace has been removed, DropShadows could be achieved via shadow image, interoping to Direct2D or clever use of brushes.

    Note: Pixel Shader effects can not be used in their current form unless using DirectX.

    Accessing control elements across controls requires x:FieldModifier=”public”.

    Note: The compiler generated class for the x:Name is internal now not public

    XAML Style TargetTypes do not need x:Type information, they can be done directly. TargetType=”{x:Type Path}” becomes TargetType=”Path”

    UIElement.Clip in the Windows Runtime API must be a RectangleGeometry. WPF and Silverlight allowed complex non-rectangular shapes via PathGeometry.

    FindResource(…) Resource Dictionary Helper function has been removed, either use This.Resources[…] or App.Current.Resources[…]

    Animation Differences

    DiscreteBooleanKeyFrame has been removed, it is now replaced with a more generic DiscreteObjectKeyFrame to allow users to set object values at discrete points during an animation.

    SetTargetPropertyPath now uses a direct string path for the property rather than a PropertyPath Class

    Threading Differences

    Windows.Thread does not exist, you have to use the ThreadPool or Tasks. This can cause issues with the Dispatcher or accessing the current thread.

    As Threading and as such WaitCallBack is not supported, you can use WorkItemHandler to queue items for invocation.

    .Net has shifted over to the async and await concept with a lot of the new refactored libraries taking advantage of this new technique, this can cause issues with old code (e.g. file access). Below is an example of how to (crudely) wrap a dispatcher call into a new task to allow UI updates.

    // Old Code
    window.Dispatcher.BeginInvoke(
        new PerformSomeUIOperationDelegate(PerformSomeUIOperation),
        true);
    
    // New Code
    Task.Run(async () =>;
    {
        await window.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>;
        {
            PerformSomeUIOperation(true);
        });
    });
    

    Eventing Differences

    StoryBoard EventHandlers are slightly different.

    Note: Now they normally are EventHandler<object> rather than having an EventArgs

    Routed Event Handlers can also now handle already handled events. The default behaviour for porting would be AddHandler(, , false)

    Note: Custom Routed Event Handlers aren’t in general supported in Windows.UI.Xaml and should be carefully thought about before implementation

    Compilation Issues ( MakePRI warning 0xdef00520 )

    During the development of the original application we found that the built in multiple language support didn’t work how we wanted it so we stored resource dictionaries for each supported language with the language prefix on the end. e.g. StringResources.ar-AR.xaml

    This causes a compiler warning:

    5>MakePRI : warning 0xdef00520: Invalid qualifier: AR-AR
    5>MakePRI : warning 0xdef00520: Invalid qualifier: DA-DK
    5>MakePRI : warning 0xdef00520: Invalid qualifier: DE-DE

    etc…

    To fix it you need to rename the files not to include the language code, as the .NET compiler upon seeing the codes expects the app to support it.

    General Changes

    Instead of DesignerProperties.GetIsInDesignMode(this) there is Windows.ApplicationModel.DesignMode.DesignModeEnabled

    UIPropertyMetadata has been renamed to just PropertyMetadata

    BinaryFormatter and the [Serializable] attribute are not available, you have to serialize via a contract (DataContractSerializer)

    The [Browsable(…)] XML attribute for serialisation is not supported, you can use [EditorBrowsableAttribute(EditorBrowsableState.x)] instead to hide values from the properties window.

    Timers are limited (e.g. No System.Timers) thus most have shifted over to DispatcherTimer instead.

    Typography is limited, you can’t use things like the TypeFace or FormattedText class. It may be possible to use the RichTextBox class to calculate sizes of text blocks.

    Settings Files are not available. Settings can either be done via LocalStorage, or IsolatedStorage. The System.Configuration namespace is not available.

    The Invariant Culture has been removed for string comparisons, it may be best to compare via OrdinalIgnoreCase if Culture is not important.

    RenderOptions does not exist, therefore you can not adjust cached image quality or scaling mode.

    Note: All scaling and image caching will now be done at the default scale, this may therefore increase the overall in-memory size of the app.

    Platform Specific APIs

    Platform specific functionality is provided via the Windows Universal SDK. Tests have to be performed for Platform Specific functionality as it will crash the app otherwise on different platforms.

    e.g.

    if (Windows.Foundation.Metadata.ApiInformation.IsTypePresent("Windows.Phone.UI.Input.HardwareButtons"))
    Windows.Phone.UI.Input.HardwareButtons.BackPressed += HardwareButtons_BackPressed;
    

    About

    Software engineer. Tea drinker

    http://MrPfister.com

    One Response to Porting WPF applications to Windows Universal Apps

    1. Silviu
      June 16, 2016 at 8:03 am

      A very important past is missing that the Universal Windows does not support the Style’s Trigger and DataTrigger used heavily in WPF. The recommended approach is to use VisualStateManager.

    Leave a Reply

    Your email address will not be published. Required fields are marked *