Last week I published an article about the x:DefaultBindMode
feature in UWP XAML. Uno Platform, the cross-platform UWP/WinUI bridge for Android, iOS, and WebAssembly, was at that time missing the feature. What happened next, was astounding. In just over a day, the feature was not only implemented but also merged (along with several additional improvements like BindBack
!) and available in the latest prerelease package on NuGet. Just wow.
Example on WASM
Taking one of the code samples from my previous article:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<StackPanel x:DefaultBindMode="OneWay"> | |
<TextBox Text="{x:Bind ViewModel.Name, Mode=TwoWay}" /> | |
<TextBlock Text="{x:Bind ViewModel.Name}" /> | |
</StackPanel> |
It now just works as expected on WebAssembly running Uno Platform – TextBlock
uses OneWay
binding, while TextBox
explicitly overrides this with TwoWay
. The same on all other Uno Platform targets.

How did they do this?
I thought it would be interesting to look into how the Uno Platform team achieved this magically quick implementation of the feature.
The whole feature was implemented in the XamlFileGenerator class, which generates C# code from XAML.
The x:DefaultBindMode
feature works as a “scope,” meaning the declared mode applies to an element and all its children unless overridden somewhere further down the tree. As the XAML file is parsed in a depth-first manner, the most appropriate data structure to maintain the currently “active” bind mode is a LIFO (Last-In-First-Out) data structure like a stack. And that is what Uno Platform uses to maintain the feature’s state:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// The current DefaultBindMode for x:Bind bindings, as set by app code for the current Xaml subtree. | |
/// </summary> | |
private readonly Stack<string> _currentDefaultBindMode = new Stack<string>(new[] { "OneTime" }); |
Notice the stack is initialized with OneTime
– this matches the UWP’s default x:Bind
mode. So unless x:DefaultBindMode
is used in the XAML file, OneTime
is the fallback.
How do we push a new “default mode” in the stack? That’s where the TrySetDefaultBindMode
method comes in:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private IDisposable TrySetDefaultBindMode(XamlObjectDefinition xamlObjectDefinition) | |
{ | |
var definedMode = xamlObjectDefinition.Members.FirstOrDefault(m => m.Member.Name == "DefaultBindMode")?.Value?.ToString(); | |
if (definedMode == null) | |
{ | |
return null; | |
} | |
else if (!IsValid(definedMode)) | |
{ | |
throw new InvalidOperationException( | |
"Invalid value specified for attribute 'DefaultBindMode'. Accepted values are 'OneWay', 'OneTime', or 'TwoWay'."); | |
} | |
else | |
{ | |
_currentDefaultBindMode.Push(definedMode); | |
return new DisposableAction(() => _currentDefaultBindMode.Pop()); | |
} | |
bool IsValid(string mode) | |
{ | |
switch (mode) | |
{ | |
case "OneWay": | |
case "OneTime": | |
case "TwoWay": | |
return true; | |
default: | |
return false; | |
} | |
} | |
} |
First, the code retrieves the DefaultBindMode
attribute from the element’s XAML definition. If this is not defined, the method short-circuits and returns null
. If the bind mode was defined, it is validated against the list of known values (IsValid
) method.
For a valid DefaultBindMode
value, we need to push it onto the stack to start a new scope. The method then returns a DisposableAction
instance, which implements IDisposable
and makes sure to execute the passed in lambda when it is disposed of. And in this case, it pops the top value off the stack to end the current scope.
This greatly aids the readability of the code, as it really signifies the fact that the method creates a new scope when used with the using
block:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using (TrySetDefaultBindMode(xamlObjectDefinition)) | |
{ | |
... | |
} |
Note that in case TrySetDefaultBindMode
evaluates to a null
(for the case when DefaultBindMode
is not set), using
statement just ignores it (and does not attempt to call Dispose()
on null
at the end).
Finally, retrieving the currently set mode is a simple Peek
of the top of the stack:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private string GetDefaultBindMode() => _currentDefaultBindMode.Peek(); |
The GetDefaultBindMode()
method is used, when the x:Bind
evaluation function is constructed:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var modeMember = bindNode.Members | |
.FirstOrDefault(m => m.Member.Name == "Mode")?.Value?.ToString() ?? GetDefaultBindMode(); |
Succinctly said, when the x:Bind
explicitly states a Mode
, it takes precedence. Otherwise the currently active DefaultBindMode
is applied.
That’s all there’s to it! As we can see, the feature was implemented very elegantly, and thanks to Uno Platform, we really have #WinUIEverywhere!
1 thought on “x:DefaultBindMode in Uno Platform & how it’s implemented”