Changes
/* Xamarin/MAUI XAML Lessons Learned: Improving Binding and Visibility */
{{UnderConstructionStart}}
== Xamarin/MAUI XAML Lessons Learned: Improving Binding and Visibility performance on complex List item templates ==
=== 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: Loading UI content on demand ===It was discovered that UI controls hidden with IsVisible=False still maintain a presence in the UI tree. To improve performance on highly complex item templates, like UBIKTaskItem, rendered repeatedly such as in ListViews, a workaround can be applied to only load the UI content when the visibility binding condition is met.
==== 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>
</source>
<br>
==== Improved Approach ====
To eliminate prevent unnecessary binding warnings and improve clarityloading of UI elements when their visibility is False, we switched it is possible to using a DataTrigger replace the UI content with a ControlTemplatecontent hosting-control, such as a ContentView or ContentControl. The original content should instead be defined as a template in UBIKThemes, as shown below.
<source lang = "xml">
<ContentView Grid.Column="5">
<ContentView.Triggers>
<DataTrigger Binding="{Binding MROViewModel.PreviousValue, Converter={StaticResource NullToNotBool}, TargetNullValue=false, FallbackValue=false}"
</source>
<br>
The important part of the snippet above is the DataTrigger. Through this technique, the ControlTemplate of the content-hosting control is only set once the visibility condition is met, preventing the UI content from being loaded until it is explicitly required.And the associated '''ControlTemplate''', located in UBIKThemes:
<br>
<source lang = "xml">
<ControlTemplate x:Key="PreviousValueTemplate">
<Label VerticalOptions="Center">
<Label.FormattedText>
<FormattedString>
'''Why This Change?'''
* '''Reduced Binding Warnings''': Using DataTrigger prevents unnecessary evaluations when the value is null.
* '''More Modular''': The separation into ControlTemplate discrete templates makes it individual UI elements reusable and maintainable.* '''Better Performance''': Eliminates unnecessary loaded loading of UI elementsin the background, that are not visible to the user.
<br>
=== Lesson 2: Using BindingContext for TemplateContext Reducing background Binding errors === Complex item templates such as UBIKTaskItem were found to produce large numbers of errors while rendering, likely as bindings are being resolved before the relevant viewmodels can be loaded by the client. While these errors are temporary and not visible to the user, the cumulative effect of numerous binding errors raised while rendering items can be felt when navigating to pages with large numbers of items.<br>{{Attention|Unfortunately, these bindings errors are not normally logged in the UBIKDebug.log. Therefore, the recommendation is to use the following technique when using a ContentControl or controls:ContentControl.}}
==== Previous Approach ====
Initially, we used a '''ContentControl''' with a direct '''ContentTemplate''' and '''TemplateContext''', as shown below:
<tabs>
<tab name="Xamarin">
<source lang = "xml">
xmlns:ctrls="clr-namespace:UBIK.CPL.Controls;assembly=UBIK.CPL"
</source>
</tab>
<tab name="MAUI">
<source lang = "xml">
xmlns:ctrls="clr-namespace:UBIK.MAUI.Controls;assembly=UBIK.MAUI"
</source>
</tab>
</tabs>
<source lang = "xml">
<ctrls:ContentControl
ContentTemplate="{StaticResource TaskPropertyTemplateSelector}"
</source>
<br>
==== Improved Approach ====
<source lang = "xml">
<ctrls:ContentControl x:Name="TaskPropertyTempSelCtrl"
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>
The previous example used an MROViewModel binding directly in the TemplateContext property of the '''<ctrls:ContentControl>.'''
However, a workaround that seems to avoid triggering background binding errors is to apply the MROViewModel to the '''<ctrls:ContentControl>''' itself via the BindingContext property, and then request that BindingContext using the x:Reference syntax seen in the TemplateContext property.
'''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 this workaround seems to prevent unnecessary re-evaluations.* '''Improves Maintainability''': The approach is more structured and easier to follow. <br> [[Category:Pages with broken file links|XAML Best practices]][[Category:XAML|XAML Best practices]] === Conclusion === By implementing these lessons:* We reduced unnecessary binding warnings.* Improved UI structure and maintainability.* Optimized the way elements are rendered based on binding valueserrors.
{{UnderConstructionEnd}}