Make Bindings compatible with Multi-Windows.

Make Bindings compatible with Multi-Windows.

When XAML/WinRT was created, applications were only fullscreen, then they became windowed, and recently, more and more applications become multi-windows (Dropbox for example).

The main concern with multi-windows app is that it’s very easy to make them crash, due to a limitation of XAML.

Why does it crash?

Let’s imagine an application with 2 windows (A and B), each window contains a TextBlock element with the Text property binded (using standard or native binding) to the same reference: Artist.FullName.

Now let’s imagine that the window A allows the user to modify the value of FullName, once the value changed, the event PropertyChanged will be launched in order to update other bindings, whether it’s the current window or the other windows.

In XAML/WinRT, each window has its own UI Thread, so when the A window launches PropertyChanged, it will launch this event on its own UI Thread and update its other bindings… but what about the window B?

B will also receive the event, but because this one was launched on the UI Thread of window A and not the one owned by window B, the application will crash, the same way an app crashes when you launch PropertyChanged on a non-UI thread.

What do other apps do?

I spoke with some developers facing this issue recently, asking about how they do, here is what they currently do:

  • use a SQLite database, store all objects inside, if a window updates an object, save it and launch an event telling other windows to retrieve the object and update all XAML bindings.
  • Each window has its own copy of the object, then launch an event UpdatedObjectEvent(<id>, <name property>, <new value>) to sync all copies, performance are not excellent and there are some issues if 2 windows launch events at the same time.
  • Windows share the same object but don’t use bindings, instead, update manually the UI when the object is updated.

All these solutions work, but they are all very annoying, you need to sync manually each objects, it’s hard to maintain, you have a lot of duplicated objects, it requires to write a lot of codes for no reasons, etc…

In order to fix this issue, I made a list of important constraints to follow:

  • Don’t duplicate objects
  • Must work with traditional bindings and native bindings
  • Easy to use
  • No data backup
  • Use very few memory

Introducing CrossUIBinding library

The idea is very simple: the same object will be shared between all windows (in memory), but each window will virtually see different objects (named Clone, a kind of wrapper), so if a window launches a PropertyChanged event, it will not make the application crashes, instead, the library will individually propagate the PropertyChanged event to other windows on their own UI Thread!

Usage

In your ViewModel, first include:

using Huyn.CrossUIBinding;

then modify the properties you want to share between windows to use:

before: 

public double CrossUISliderValue { get; set; } = 1d;

after: 

public CrossUIItem<double> CrossUISliderValue { get; set; } = new CrossUIItem<double>(1d);

or if you want the lib to launch automatically:

public CrossUIItem<double> CrossUISliderValue { get; set; } = new CrossUIItem<double>(1d, true);

Modify your bindings to include the (fake) Clone property:

before: 

{x:Bind ViewModel.CrossUISliderValue, Mode=TwoWay}

after:

{x:Bind ViewModel.CrossUISliderValue.Clone.Value, Mode=TwoWay}

And use CrossUISliderValue.Value or CrossUISliderValue.Clone.Value when you want to change the value of the object.

If you set autoRaisePropertyChanged to false, you can manually raise PropertyChanged with CrossUISliderValue.RaisePropertyChanged()

Limitation

There is only one limitation with the current implementation: you can’t update a property located at the right of the Clone.Value part of your bindings, if you do it, the application will crash the same way than with native bindings.

{x:Bind ViewModel.Cities[1].Attractions.Zoo.Clone.Value.Animals.Count, Mode=TwoWay}

In this example, you can update (with PropertyChanged) every elements of the blue part, but if you raise a PropertyChanged event on one of the red elements, it will crash your app.

My advice will be to use CrossUIItemClone the deepest possible in the object tree, in this case, on Count for example, so you will be able to update whatever you want and write:

{x:Bind ViewModel.Cities[1].Attractions.Zoo.Animals.Count.Clone.Value, Mode=TwoWay}

In this case you are free to update Cities, Attractions, Zoo, Animals, Count, etc…

Open-source and Sample app

This library is open-source, the code as well as samples are available here:
https://github.com/rudyhuyn/xUIBinding

Don’t hesitate to share it or contribute!

How to use the sample?

First download the sample app and click on Create New Window in order to create many other windows.

 

Native Binding

public int Counter { get; set; } = 0;
<Run Text=”{x:Bind ViewModel.Counter, Mode=OneWay}”/>

Click on the increment button in order to test the classic behavior. The application will crash, as expected.

CrossUIBinding: property

public CrossUIBindingProvider<int> CounterCrossUI { get; set; } = new CrossUIBindingProvider<int>(0);
<Run Text=”{x:Bind ViewModel.CounterCrossUI.Clone.Value, Mode=OneWay}”/>

Click on the increment button, the int will be incremented not only in the current window, but also in all other windows!

CrossUIBinding: parent of the property

Same than before, but this time the parent of the property is cross-UI compatible instead of the property.

CrossUIBinding: UI Controls

Test each controls, when you update one of them, other windows are automatically update, without writing a single line of code.

 `

Comments are closed.