Changes
==== Alternatives to using Multiple Controls ====
== Templating ==
{{UnderConstructionEnd}}
== Performance ==
== Testing of UI ==
XAML can be sometimes very weird, there are dependencies or default values that you don’t see immediately. So, a good way to prevent millions of fixes for the customer, because the environment, different device or even a different windows version destroys your UI, is to write a test plan where you test your implementation. It’s important to see exactly your controls in action on their own and acting with each other, so you can prevent doing lots of ‘easy’ fixes.
{{UnderConstructionStart}}
== Xamarin/MAUI XAML Lessons Learned: Improving Binding and Visibility ==
=== Overview ===
While working on XAML-based UI development, we encountered issues related to binding errors and visibility conditions. These binding warnings were mostly harmless in terms of performance but cluttered the logs and could lead to confusion. To mitigate these issues, we refined our XAML practices. This document details the two primary lessons learned and how we improved our implementations.
=== Lesson 1: Improving Element Visibility Binding ===
==== Previous Approach ====
Previously, we used IsVisible directly with a binding condition. The approach looked like this:
<source lang = "xml">
<Label
Grid.Column="5"
IsVisible="{Binding MROViewModel.PreviousValue, Converter={StaticResource NullToNotBool}}"
VerticalOptions="Center">
<Label.FormattedText>
<FormattedString>
<Span Text="{Binding MROViewModel.PreviousValue}" />
<Span Text=" " />
<Span Text="{Binding MROViewModel.Unit}" />
</FormattedString>
</Label.FormattedText>
</Label>
</source>
<br>
While this method worked, it caused unnecessary binding warnings when the '''PreviousValue''' was '''null'''.
==== Improved Approach ====
To eliminate unnecessary binding warnings and improve clarity, we switched to using a DataTrigger with a ControlTemplate, as shown below.
<source lang = "xml">
<ContentView Grid.Column="5">
<ContentView.Triggers>
<DataTrigger Binding="{Binding MROViewModel.PreviousValue, Converter={StaticResource NullToNotBool}, TargetNullValue=false, FallbackValue=false}"
Value="True" TargetType="ContentView">
<Setter Property="ControlTemplate" Value="{StaticResource PreviousValueTemplate}" />
</DataTrigger>
</ContentView.Triggers>
</ContentView>
</source>
<br>
And the associated '''ControlTemplate''':
<br>
<source lang = "xml">
<ControlTemplate x:Key="PreviousValueTemplate">
<Label VerticalOptions="Center">
<Label.FormattedText>
<FormattedString>
<Span Text="{TemplateBinding BindingContext.MROViewModel.PreviousValue}" />
<Span Text=" " />
<Span Text="{TemplateBinding BindingContext.MROViewModel.Unit}" />
</FormattedString>
</Label.FormattedText>
</Label>
</ControlTemplate>
</source>
<br>
'''Why This Change?'''
* '''Reduced Binding Warnings''': Using DataTrigger prevents unnecessary evaluations when the value is null.
* '''More Modular''': The separation into ControlTemplate makes it reusable and maintainable.
* '''Better Performance''': Eliminates unnecessary loaded UI elements.
<br>
=== Lesson 2: Using BindingContext for TemplateContext ===
==== Previous Approach ====
Initially, we used a '''ContentControl''' with a direct '''ContentTemplate''' and '''TemplateReselectTrigger''', as shown below:
<source lang = "xml">
<ctrls:ContentControl
ContentTemplate="{StaticResource TaskPropertyTemplateSelector}"
HorizontalOptions="End"
TemplateContext="{Binding MROViewModel}"
TemplateReselectTrigger="{Binding IsLocked}" />
</source>
<br>
==== Improved Approach ====
We switched to using '''BindingContext''' as the '''TemplateContext''', which improved binding resolution and reduced errors:
<source lang = "xml">
<ctrls:ContentControl x:Name="TaskPropertyTempSelCtrl"
HorizontalOptions="End"
BindingContext="{Binding MROViewModel}"
TemplateContext="{Binding BindingContext, Source={x:Reference TaskPropertyTempSelCtrl}}"
TemplateReselectTrigger="{Binding IsLocked}">
<ctrls:ContentControl.Triggers>
<DataTrigger Binding="{Binding NotApplicable, Converter={StaticResource BoolToNotBool}, FallbackValue=false, TargetNullValue=false}"
TargetType="ctrls:ContentControl" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource TaskPropertyTemplateSelector}" />
</DataTrigger>
</ctrls:ContentControl.Triggers>
</ctrls:ContentControl>
</source>
<br>
'''Why This Change?'''
* '''Avoids Additional Binding Errors''': Using BindingContext ensures that the data context remains consistent.
* '''Better Handling of Re-selection''': The TemplateContext is explicitly set to prevent unnecessary re-evaluations.
* '''Improves Maintainability''': The approach is more structured and easier to follow.
<br>
=== Conclusion ===
By implementing these lessons:
* We reduced unnecessary binding warnings.
* Improved UI structure and maintainability.
* Optimized the way elements are rendered based on binding values.
These refinements help maintain a cleaner, more efficient XAML structure while improving performance and usability.
{{UnderConstructionEnd}}
[[Category:Pages with broken file links|XAML Best practices]]
[[Category:XAML|XAML Best practices]]