Limitations of WinRT platform

WinRT has a huge amount of new functionality that makes it an exciting development platform and from GUI developer perspective it has a lot of significant benefits over WPF such as the ability to combine XAML and DirectX together and better managed and unmanaged code integration with new C++/CX compiler and COM based WinRT components. But from other side, WinRT has a number of limitations that developers who migrate their code from WPF to WinRT should be aware of. Below I briefly described those limitations.

COM based components limitations

WinRT components actually are not classes they are COM objects, so they can not be inherited and can only be used as a set of interfaces, that is why we use sealed keyword with the component class declaration in C# and C++. In my opinion this is significant disadvantage that breaks the object oriented paradigm.

The lack of synchronous methods in API

WinRT asynchronous API is useful when I have an button click handler that opens a file, for example. In this case async/await keywords help me to write a code that does not block UI thread. But what if I already have a separate non-UI thread that opens a file? Why should I use asynchronous API if my separate thread does not block anything?

The possible solution in this case is to use old C-style API like ::CreateFile2, for example.

Superfluous multithreading with top-level windows.

WinRT application, starting from Windows 8.1, can have multiple top-level windows displayed on a single monitor or on multiple monitors , but each top-level window in WinRT has its own Dispatcher and runs on its own thread so each top-level window needs it own separate view model because there is no way to bind a single view model to multiple windows living in different threads. I think that it is a major inconvenience for developers who use MVVM pattern.

The lack of MultiBinding and RelativeSource Mode=FindAncestor in XAML

WinRT XAML does not have MultiBinding so you can not, for example, multiply a FontSize property of some control by a value provided by the view model in XAML, you can only multiply the FontSize by a constant (see How to multiply a value by a constant in WinRT XAML) because MultiplicationConverter described here cannot be a IMultiValueConvertor.

Also WinRT XAML does not support RelativeSource Mode=FindAncestor, as a possible workaround we can give the ancestor a name and than use ElementName=theAncestor.

XAML triggers are replaced with VisualStateManager in WinRT

Probably it is not a limitation, but nevertheless there are no triggers in WinRT XAML anymore. Instead of them we now use something like this in a templated control:

<Style TargetType="local:SomeControl" >
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:SomeControl">
                <Grid>
                    <TextBox Text="Some text, for example."/>
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal">
                                <Storyboard>
                                    <DoubleAnimation Storyboard.TargetProperty="Opacity" Storyboard.TargetName="borderEllipse" To="0.5"/>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="PointerOver">
                                <Storyboard>
                                    <DoubleAnimation Storyboard.TargetProperty="Opacity" Storyboard.TargetName="borderEllipse" To="1"/>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Pressed">
                                <VisualState.Setters>
                                    <Setter Target="borderEllipse.Fill" Value="Red"/>
                                </VisualState.Setters>
                            </VisualState>
                            <VisualState x:Name="Disabled">
                                <VisualState.Setters>
                                    <Setter Target="borderEllipse.Fill" Value="Gray"/>
                                </VisualState.Setters>
                            </VisualState>
                            <VisualState x:Name="PointerDown">
                                <Storyboard>
                                    <PointerDownThemeAnimation TargetName="borderEllipse" />
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="PointerUp">
                                <Storyboard>
                                    <PointerUpThemeAnimation TargetName="borderEllipse" />
                                </Storyboard>
                            </VisualState>
                            <VisualStateGroup.Transitions>
                                <VisualTransition To="Normal" GeneratedDuration="00:00:00.2"/>
                                <VisualTransition To="PointerOver" GeneratedDuration="00:00:00.2"/>
                                <VisualTransition To="Pressed" GeneratedDuration="00:00:00.2"/>
                                <VisualTransition To="Disabled" GeneratedDuration="00:00:00.2"/>
                            </VisualStateGroup.Transitions>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

and in C++ code we should manually switch those visual states:

void SomeControl::OnApplyTemplate()
{
    __super::ApplyTemplate();

    VisualStateManager::GoToState(this, "Normal", false);
}

void SomeControl::OnPointerEntered(PointerRoutedEventArgs ^ e)
{
    VisualStateManager::GoToState(this, "PointerOver", true);
}

void SomeControl::OnPointerExited(PointerRoutedEventArgs ^ e)
{
    VisualStateManager::GoToState(this, "Normal", true);
}

void SomeControl::OnPointerPressed(PointerRoutedEventArgs^ e)
{
    CapturePointer(e->Pointer);

    VisualStateManager::GoToState(this, "PointerDown", true);
}

void SomeControl::OnPointerReleased(PointerRoutedEventArgs^ e)
{
    VisualStateManager::GoToState(this, "PointerUp", true);

    ReleasePointerCapture(e->Pointer);
}

Content of a ContentControl cannot be a resource, it should be either a control or a text

The following code that previously worked with WPF will throw Windows.UI.Xaml.Markup.XamlParseException:

<UserControl
    ...
    <UserControl.Resources>
        <Viewbox x:Key="someIcon" Width="20" Height="20">
            <Path ... />
        </Viewbox>
    </UserControl.Resources>
    <Grid>
        <Button x:Name="someButton" Content="{StaticResource someIcon}"/>
    </Grid>
</UserControl>

and even this C# code will not work:

var icon = Resources["someIcon"];

someButton.Content = icon;

the actual type of this icon is Viewbox, so it is not clear, what is the difference between Viewbox loaded from a resource and Viewbox created with new operator.

The lack of DataGrid, TreeView and GridSplitter controls.

WinRT does not have DataGrid and TreeView controls and probably it is not so bad, because, in my opinion, WPS’s DataGrid and even ItemsControl were written inaccurately and I wasted a lot of time trying to make some specific code work, see Implementation of WPF’s ICollectionView interface. In WinRT ItemsControl.ItemsSource uses IObservableVector interface so it works slightly different, and probably there is no glitch that I fought with in WPF. There are WinRT XAML Toolkit on the internet that has TreeView, GridSplitter and a lot of other controls, but it is not clear yet where can I get DataGrid control.

PS

I am still a newbie in WinRT and probably I did not understand something correctly, so feel free to leave your comments if you have some useful ideas.

Leave a Reply

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