Jump to: navigation, search

Difference between pages "Mobile XAML" and "HowTo:Convert Xamarin XAMLs to Maui"


(Difference between pages)
 
(Highly Recommended)
 
Line 1: Line 1:
The User Interface of the MAUI/Xamarin Clients (Android, iOS, Windows) can be fundamentally customized using custom XAML definitions. This customization process is heavily influenced by the [[XAML|XAML Customization Process on the WinX Client]].
+
== Information ==
 +
=== Frame to Border === 
 +
'''Use Border instead of Frame.''' 
 +
Frame is deprecated in MAUI. Use Border with `Stroke` instead of `BorderColor`.
  
= Basics =
+
{| class="wikitable"
There are predefined XAML templates available that can be customized. If no customizing is found, the default templates will be used.
+
! Xamarin
 +
! MAUI
 +
|-
 +
| <Frame ... />
 +
| <Border ... />
 +
|}
  
== Default XAMLs ==
+
=== LayoutOptions with "...AndExpand" === 
All default XAML templates that can be customized are available through the Xamarin.UWP client. You need to go to the app settings and click the "Unpack default XAMLs" button.
+
'''Replace "...AndExpand" with Grid layout.''' 
 +
Horizontal/VerticalOptions ending in "...AndExpand" are deprecated. Use Grid with `ColumnDefinition Width="*"` for expansion.
  
== Differences to WinX/UWP ==
+
=== GlyphLabel and Styles === 
* Since it's based on the Xamarin XAML dialect, you CANNOT use any existing XAML customizings for the WinX/UWP app;
+
'''Use Label with new styles.'''
* Namespace definitions must include the corresponding assembly names;
+
Custom controls like `GlyphLabel` are no longer needed. Use `Label` with styles like `UBIKSymbolText`.
* XAML template files have the extension name of '''xamlx''' instead of '''xaml''';
+
* You can deploy only those resources that you want to customize. (This is also the case for newer versions of WinX/UWP. {{Version/WinXSince|3.6}})
+
  
 +
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| controls:GlyphLabel
 +
| Label with UBIKSymbolText style
 +
|}
  
 +
=== SfTabView Virtualization === 
 +
'''Enable virtualization.''' 
 +
Add `EnableVirtualization="True"` to all SfTabView instances or styles.
  
= Overriding default XAMLs=
+
=== Swipe Menu Icons on Android === 
On Windows (WinX/UWP), the <code>[App Package Folder]/LocalState/xaml/</code> folder can be accessed by the user and the customized XAML files directly added. An alternative to this on all platforms (including Android, iOS, etc.) is to use the ''Import Customizing'' button in the app settings to import the templates, styles, images, etc. (file by file, or all files within a zip package). The changes should be reflected in the UI (except the current page) without restarting the app. After directly editing the customizings (Xamarin.UWP), you can also use the "Reload customizing" button without restarting the app.
+
'''Refactor swipe templates and label styles.''' 
 +
Icons in swipe menus were truncated on Android. Fixed via refactoring.
  
In the following, we're going to override the default light theme color of the app:
+
=== Color Style Changes === 
* Unpack the default XAMLs;
+
'''Dark theme adjustments.'''
* Pick and deploy the default UBIKThemes.xamlx to the '''xaml''' folder inside the ''LocalState'' folder;
+
Changes to color styles may affect appearance. Review usage of `UBIKDarkThemeColor`, `UBIKDarkTextColor`, etc.
* Open it with the text editor of your choice and remove everything within the '''ResouceDictionary''' except <code><Color x:Key="UBIKLightThemeColor">blue</Color></code>;
+
* Save the changes and reload. You should see changes successfully implemented.
+
  
{{Attention| For maintainability reasons, it's HIGHLY recommended to deploy only those XAML resources you want to override. For example, if you only want to customize the UBIKChildArea, don't deploy others like the UBIKMenuArea. If you only want to customize the UBIKChildItem, that's the only content you should include in the UBIKThemes.}}
+
=== Strikethrough Converter === 
<br>
+
'''Use `TextDecorations="Strikethrough"` instead of converter.'''
  
== Custom Templates ==
+
{| class="wikitable"
Custom templates can be added, just like on the WinX/UWP client. Make sure to include all '''namespace definitions''' (as attributes of the '''ContentView''' tag), otherwise the custom template will not load.
+
! Xamarin
 +
! MAUI
 +
|-
 +
| Text="{Binding ..., Converter={StaticResource StrikethroughConverter}}"
 +
| Text="{Binding ...}" TextDecorations="Strikethrough"
 +
|}
  
Similar to the ''UBIKThemes.xamlx'' file, custom or overridden templates start with a <code><ContentView></code> tag containing all '''namespaces'''. <code><ContentView.Resources></code> takes a '''ResourceDictionary''' and contains resources like references to converters. Finally, <code><ContentView.Content></code> contains the actual layout content (it's best to start with a '''Grid'''). Again, don't forget to add namespaces that you need!
+
=== Button Disabled Trigger === 
 +
'''Add trigger for disabled buttons.'''
 +
Maui buttons don’t visually change when disabled. Add trigger to style.
  
Templates can also be defined in ''UBIKThemes.xamlx''. In this case, they need to be added into the <code><ResourceDictionary></code> inside the <code><ContentView.Resources></code> template as a <code><DataTemplate></code>. Similarly, if you want to split up the templates into seperate files, you need to make sure to follow the steps mentioned above and get the content of the <code><DataTemplate></code> into the <code><ContentView.Content></code> tag.
+
=== TapGestureRecognizer Replacement === 
 +
'''Use TappedBehavior instead.''
 +
TapGestureRecognizer now intercepts taps. Use `TappedBehavior` to avoid conflicts.
  
{{Attention|Templates defined in separate files will override templates defined in the ''UBIKThemes.xamlx''!}}
+
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| <... .GestureRecognizers><TapGestureRecognizer Command="{Binding ...}" /></... .GestureRecognizers>
 +
| <... .Behaviors><behaviors:TappedBehavior><behaviors:InvokeCommandAction Command="{Binding ...}" /></behaviors:TappedBehavior></... .Behaviors>
 +
|}
  
=== Bindings in resources ===
+
=== TapGestureRecognizer and Swipe Conflict ===
When using bindings such as [[Xamarin_XAML#Content_filtering|this]] in a resource located in a custom template, it is necessary to place the template in a UBIKContentView.
+
'''Remove TapGestureRecognizer from root Grid.''' 
 +
TapGestureRecognizer blocks swipe behavior. Remove it and adapt parent list to trigger navigation.
  
{{Hint|Technically speaking, this is because bindable objects in the ResourceDictionary of a ContentView (the custom template) don't automatically inherit the BindingContext from the container/parent view. UBIKContentView is created to additionally do that.}}
+
=== Navigation in UBIKChildItem === 
 +
'''Use EventHandlerBehavior for navigation.'''
  
<source lang = "xml">
+
{| class="wikitable"
<!-- UBIKCustomView.xamlx -->
+
! Xamarin
<ContentView...
+
! MAUI
     xmlns:controls="clr-namespace:UBIK.CPL.Controls;assembly=UBIK.CPL">
+
|-
     <ContentView.Resources>
+
| [none]
        <ResourceDictionary>
+
| <controls:SfListViewExt.Behaviors><behaviors:EventHandlerBehavior EventName="ItemTapped"><behaviors:InvokeCommandAction Command="{Binding AppStatus.RootList.Items[0].NavigateToChildrenCommand}" /></behaviors:EventHandlerBehavior></controls:SfListViewExt.Behaviors>
            <controls:SfDataSourceExt... ItemsSource="{Binding Children.Items}" />
+
|}
        </ResourceDictionary>
+
== Recommended ==
    </ContentView.Resources>
+
=== Extract DataTemplates from UBIKThemes ===
    ...
+
'''Create separate .xamlx files for the following DataTemplates.'''
</ContentView>
+
It's not so easy about the namespaces:
 +
* In our default templates, it's enough to add the following ones to every new separate .xamlx files at the beginning;
 +
     * xmlns:ctrls="clr-namespace:UBIK.MAUI.Controls;assembly=UBIK.MAUI";
 +
     * xmlns:platform="clr-namespace:UBIK.MAUI.Platform.Renderers;assembly=UBIK.MAUI";
 +
    * xmlns:resources="clr-namespace:UBIK.MAUI.Resources;assembly=UBIK.MAUI";
 +
    * xmlns:services="clr-namespace:UBIK.MAUI.Services;assembly=UBIK.MAUI";
 +
    * xmlns:uiservices="clr-namespace:UBIK.UI.Services;assembly=UBIK.UI".
 +
* But we don't know if other namespaces might be used in the custom templates. So it might be necessary to really analyze the namespaces used and find/copy them from the UBIKThemes file.
 +
{| class="wikitable"
 +
! &nbsp; !! Templates !! &nbsp;
 +
|-
 +
| UBIKMainItem || UBIKChildItem || UBIKTaskItem
 +
|-
 +
| UBIKTaskProperty || UBIKTaskPropertyString || UBIKTaskPropertyDouble
 +
|-
 +
| UBIKTaskPropertyInt || UBIKTaskPropertyNumeric || UBIKTaskPropertyDateTime
 +
|-
 +
| UBIKTaskPropertyGeoData || UBIKTaskPropertyBool || UBIKTaskPropertyPopup
 +
|-
 +
| UBIKTaskPropertyList || UBIKTaskPropertyGuid || UBIKPropertyItem
 +
|-
 +
| UBIKPropertyTextLengthHint || UBIKPropertyDirectEditButtons || UBIKTaskPropertyEditButtons
 +
|-
 +
| UBIKPropertyDirectItemString || UBIKPropertyDirectItemDouble || UBIKPropertyDirectItemInt
 +
|-
 +
| UBIKPropertyDirectItemMinMax || UBIKPropertyDirectItemNumeric || UBIKPropertyDirectItemDateTime
 +
|-
 +
| UBIKPropertyDirectItemGeoData || UBIKPropertyDirectItemBool || UBIKPropertyDirectItemPopup
 +
|-
 +
| UBIKPropertyDirectItemList || UBIKPropertyDirectItemGuid || UBIKDocumentItem
 +
|-
 +
| UBIKSearchResultItem || UBIKDefaultHotSpot || UBIKAngularLinkHotSpot
 +
|-
 +
| UBIKRoundLinkHotSpot || UBIKInputHotSpot || UBIKSignatureHotSpot
 +
|-
 +
| UBIKObjectHotSpot || UBIKMediaHotSpot ||
 +
|}
 +
=== VideoPlayer Adjustments ===
 +
'''This property is repsonsible for the video player regarding how or if it should get scaled up depending on the value of the
 +
Aspect property'''
 +
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| <syntaxhighlight lang="xml">VideoPlayer...</syntaxhighlight>
 +
| <syntaxhighlight lang="xml">VideoPlayer Aspect="AspectFit"</syntaxhighlight>
 +
|}
  
<!-- Container where UBIKCustomView is placed, e.g. UBIKChildArea.xamlx -->
+
'''The UriToVideoSourceConverter is no longer needed because the conversion of the
<ContentView...
+
VideoSource gets done by the MediaElement itself. It is marked as obsolete and should therefore no longer be used.'''
    xmlns:controls="clr-namespace:UBIK.CPL.Controls;assembly=UBIK.CPL">
+
{| class="wikitable"
    ...
+
! Xamarin
    <controls:UBIKContentView Content="{Binding [UBIKCustomView], Source={x:Static services:TemplateService.Instance}}" />
+
! MAUI
</ContentView>
+
|-
</source>
+
| <syntaxhighlight lang="xml"><controls:VideoPlayer... Source="{Binding DocumentViewModel.LocalURI, Converter={StaticResource UriToVideoSource}}"/></syntaxhighlight>
 +
| <syntaxhighlight lang="xml"><controls:VideoPlayer... Source="{Binding DocumentViewModel.LocalURI}"/></syntaxhighlight>
 +
|}
  
 +
'''The StopTimer property is obsolete and no longer needed because the MediaElement behind does the Stop of the VideoPlayer
 +
by itself.'''
 +
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| <syntaxhighlight lang="xml"><controls:VideoPlayer... StopTimer="{Binding Unloaded}" /></syntaxhighlight>
 +
| <syntaxhighlight lang="xml"><controls:VideoPlayer... /></syntaxhighlight>
 +
|}
 +
=== SearchBar Style Fix on iOS ===
 +
'''On iOS, defining a transparent background for the SearchBar control results in a black text on black background appearance.'''
 +
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| <syntaxhighlight lang="xml">[issue on iOS only] UBIKThemes: SearchBar style: <Setter Property="BackgroundColor" Value="Transparent" /></syntaxhighlight>
 +
| <syntaxhighlight lang="xml">Delete this setter</syntaxhighlight>
 +
|}
  
  
 +
=== Remove TapGestureRecognizer from Main Item ===
 +
'''Swiping on the Main items was broken by Maui (Documented in Information). The workaround is to remove the TapGestureRecognizer from UBIKMainItem, and add ListView-based navigation similar to that found in UBIKChildArea (with an extended binding path for technical reasons).'''
 +
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| <syntaxhighlight lang="xml">(In UBIKMainItem)
 +
<Grid.GestureRecognizers>
 +
<TapGestureRecognizer Command="{Binding NavigateToChildrenCommand}" />
 +
</Grid.GestureRecognizers></syntaxhighlight>
 +
| <syntaxhighlight lang="xml">[nothing]</syntaxhighlight>
 +
|}
 +
=== Add Navigation Behavior in Child Area ===
 +
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| <syntaxhighlight lang="xml">(In UBIKChildArea)
 +
[nothing]</syntaxhighlight>
 +
| <syntaxhighlight lang="xml"><controls:SfListViewExt.Behaviors>
 +
<behaviors:EventHandlerBehavior EventName="ItemTapped">
 +
<behaviors:InvokeCommandAction Command="{Binding AppStatus.RootList.Items[0].NavigateToChildrenCommand}" />
 +
</behaviors:EventHandlerBehavior>
 +
</controls:SfListViewExt.Behaviors></syntaxhighlight>
 +
|}
 +
== Highly Recommended ==
 +
=== CircularImage to AvatarView === 
 +
'''Use AvatarView instead of CircularImage.''' 
 +
CircularImage and its wrapping Frame are no longer needed. Use AvatarView from the MAUI Community Toolkit. Add the namespace:
 +
<syntaxhighlight lang="xml">
 +
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
 +
</syntaxhighlight>
  
 +
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| <Frame><CircularImage Source="..." /></Frame>
 +
| <toolkit:AvatarView ImageSource="..." BorderWidth="..." BackgroundColor="..." />
 +
|}
  
 +
=== GlyphSize to FontSize and Alignment === 
 +
'''Use FontSize and alignment properties instead of GlyphSize.''' 
 +
The GlyphSize property is obsolete. Use FontSize and add alignment properties for better layout.
  
== Adding Images ==
+
{| class="wikitable"
Images can't be loaded with the default '''Image''' tag, as the image is on different paths on each operating system.
+
! Xamarin
 +
! MAUI
 +
|-
 +
| <Setter Property="GlyphSize" Value="13.0" />
 +
| <Setter Property="FontSize" Value="16" /><br/><Setter Property="VerticalOptions" Value="Center" /><br/><Setter Property="HorizontalOptions" Value="Center" /><br/><Setter Property="VerticalTextAlignment" Value="Center" /><br/><Setter Property="HorizontalTextAlignment" Value="Center" />
 +
|}
  
Therefore, there's a custom '''FileImage''' that internally overrides the default ''Image'' with two custom parameters:
+
=== StaticResource to DynamicResource === 
* '''FolderName''' ...The name of the '''folder''' in the '''local folder'''. Only the first level inside the ''LocalState'' folder seems to work.
+
'''Use DynamicResource and BaseResourceKey.'''
* '''FileName''' ...The name of the '''file''' in the said folder.  
+
For dynamic theming, use DynamicResource instead of StaticResource. When inheriting styles, use BaseResourceKey.
  
Make sure to implement the proper namespace as well by adding
+
{| class="wikitable"
<code>xmlns:fimage="clr-namespace:UBIK.CPL.Controls;assembly=UBIK.CPL"</code> into the root item's attributes.
+
! Xamarin
 +
! MAUI
 +
|-
 +
| BasedOn="{StaticResource SomeStyle}"<br/>Style="{StaticResource SomeStyle}"
 +
| BaseResourceKey="SomeStyle"<br/>Style="{DynamicResource SomeStyle}"
 +
|}
  
Then, the image can be loaded using:<br/>
+
=== UBIKPropertyDirectEditListPopup and Guid link direct editing ===
<code><fimage:FileImage FileName="image.png" FolderName="xaml"/></code>
+
We have changed the way the content item template is defined and used in UBIKPropertyDirectEditListPopup, which is used in the direct editing of Guid link properties. This change aims to simplify the template and is also due to the fact that the old approach has a poor performance in the new MAUI app.
  
This snipped loads the image called '''image.png''' located in the '''xaml''' folder inside the ''LocalState''.
+
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
|
 +
<syntaxhighlight lang="xml">
 +
<DataTemplate x:Key="PopupFilterQueryItemTemplate">
 +
    ...
 +
</DataTemplate>
 +
</syntaxhighlight>
 +
||
 +
The DataTemplate "PopupFilterQueryItemTemplate" is no longer needed and should be removed.
 +
|-
 +
|
 +
<syntaxhighlight lang="xml">
 +
<controls:SfListViewExt
 +
    x:Name="FilterQueryResultList"
 +
    ...
 +
    ItemTemplate="{StaticResource PopupFilterQueryItemTemplate}" />
 +
</syntaxhighlight>
 +
||
 +
<syntaxhighlight lang="xml">
 +
<controls:SfListViewExt
 +
    x:Name="FilterQueryResultList"
 +
    ...
 +
    ItemTemplate="{Binding [UBIKChildItem], Source={x:Static services:TemplateService.Instance}}">
 +
    <controls:SfListViewExt.Behaviors>
 +
        <behaviors:EventHandlerBehavior EventName="ItemTapped">
 +
            <behaviors:InvokeCommandAction Command="{Binding SetPropertyValueCommand}" Converter="{StaticResource ItemTappedEventArgsToViewModelConverter}" />
 +
            <behaviors:InvokeCommandAction Command="{Binding ConfirmEditCommand}" />
 +
        </behaviors:EventHandlerBehavior>
 +
    </controls:SfListViewExt.Behaviors>
 +
</controls:SfListViewExt>
 +
</syntaxhighlight>
 +
|}
 +
In comparison, the old solution defines a "PopupFilterQueryItemTemplate" DataTemplate which simply wraps the needed selection related behaviors to the existing "UBIKChildItem" template. The new solution reuses the "UBIKChildItem" template directly and add the same but adapted behaviors to the list view instead.
  
Furthermore, the ''FileImage'' tag doesn't seem to support most attributes. Therefore place it inside a '''Grid''' to achieve a perfect layout.
+
[[Category:How-To|Convert Xamarin XAMLs to Maui]]
<br>
+
[[Category:MAUI|Convert Xamarin XAMLs to Maui]]
 +
[[Category:XAML|Convert Xamarin XAMLs to Maui]]
  
== Custom Icons ==
+
== Mandatory ==
Moved to article [[XAML_Tips#Custom_Icons]].
+
<br>
+
  
 +
=== Namespace conversion === 
 +
'''Update all namespaces to MAUI equivalents.''' 
 +
All `xmlns` and `clr-namespace` declarations must be updated to their MAUI counterparts.
  
 +
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| xmlns="http://xamarin.com/schemas/2014/forms"
 +
| xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
 +
|-
 +
| clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core
 +
| clr-namespace:Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific;assembly=Microsoft.Maui.Controls
 +
|-
 +
| clr-namespace:UBIK.CPL.Behaviors;assembly=UBIK.CPL
 +
| clr-namespace:UBIK.MAUI.Behaviors;assembly=UBIK.MAUI
 +
|-
 +
| clr-namespace:UBIK.CPL.Classes;assembly=UBIK.CPL
 +
| clr-namespace:UBIK.MAUI.Classes;assembly=UBIK.MAUI
 +
|-
 +
| clr-namespace:UBIK.CPL.Controls;assembly=UBIK.CPL
 +
| clr-namespace:UBIK.MAUI.Controls;assembly=UBIK.MAUI
 +
|-
 +
| clr-namespace:UBIK.CPL.Converters;assembly=UBIK.CPL
 +
| clr-namespace:UBIK.MAUI.Converters;assembly=UBIK.MAUI
 +
|-
 +
| clr-namespace:UBIK.CPL.Platform.Renderers;assembly=UBIK.CPL
 +
| clr-namespace:UBIK.MAUI.Platform.Controls;assembly=UBIK.MAUI
 +
|-
 +
| clr-namespace:UBIK.CPL.Services;assembly=UBIK.CPL
 +
| clr-namespace:UBIK.MAUI.Services;assembly=UBIK.MAUI
 +
|-
 +
| clr-namespace:UBIK.CPL.Resources;assembly=UBIK.CPL
 +
| clr-namespace:UBIK.MAUI.Resources;assembly=UBIK.MAUI
 +
|-
 +
| clr-namespace:UBIK.Scanning.Forms;assembly=UBIK.Scanning.Forms
 +
| clr-namespace:UBIK.MAUI.Controls.ZXingScanner;assembly=UBIK.MAUI
 +
|-
 +
| clr-namespace:Syncfusion.SfPullToRefresh.XForms;assembly=Syncfusion.SfPullToRefresh.XForms
 +
| clr-namespace:Syncfusion.Maui.Toolkit.PullToRefresh;assembly=Syncfusion.Maui.Toolkit
 +
|-
 +
| clr-namespace:Syncfusion.XForms.TabView;assembly=Syncfusion.SfTabView.Xforms
 +
| clr-namespace:Syncfusion.Maui.Toolkit.TabView;assembly=Syncfusion.Maui.Toolkit
 +
|-
 +
| clr-namespace:Syncfusion.ListView.XForms;assembly=Syncfusion.SfListView.XForms
 +
| clr-namespace:Syncfusion.Maui.ListView;assembly=Syncfusion.Maui.ListView
 +
|-
 +
| clr-namespace:Syncfusion.XForms.TreeView;assembly=Syncfusion.SfTreeView.XForms
 +
| clr-namespace:Syncfusion.Maui.TreeView;assembly=Syncfusion.Maui.TreeView
 +
|-
 +
| clr-namespace:Syncfusion.XForms.TextInputLayout;assembly=Syncfusion.Core.XForms
 +
| clr-namespace:Syncfusion.Maui.Core;assembly=Syncfusion.Maui.Core
 +
|}
  
 +
For classes referenced in the XAML code, please don't forget to also replace "CPL" with "MAUI" in the class namespace.
  
  
  
  
== Converters==
 
:''Main Page: [[Converters_In_Xamarin]]''
 
  
They are used to convert specific data into some desired output, using custom logic.  
+
=== Syncfusion control updates ===
 +
'''There are some other properties that have changed their names on SfListView/SfListViewExt. We don't use them in our standard XAMLs. But if you use them in customizings, please refer to https://help.syncfusion.com/maui/listview/migration for the complete list.'''
 +
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| <syntaxhighlight lang="xml">
 +
SfListViewExt:
 +
    * ItemHolding
 +
</syntaxhighlight>
 +
| ItemLongPress
 +
|}
  
Consider the following example:<br/>
+
=== Syncfusion control updates ===
A label saying ''There are no children items available!'' should only be visible if the number of children-items is 0.
+
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| * LeftSwipeTemplate, RightSwipeTemplate
 +
| StartSwipeTemplate, EndSwipeTemplate
 +
|}
  
 +
=== Layout name change ===
 +
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| <syntaxhighlight lang="xml">
 +
* <sync:SfListView.LayoutManager>
 +
</syntaxhighlight>
 +
| <syntaxhighlight lang="xml">
 +
<sync:SfListView.ItemsLayout>
 +
</syntaxhighlight>
 +
|}
  
 +
=== TemplateService binding for extracted templates ===
 +
'''1. We need to add the following namespace where the changes are applied:
 +
xmlns:services="clr-namespace:UBIK.MAUI.Services;assembly=UBIK.MAUI";
 +
2. We need to search for both "StaticResource" and "DynamicResource";
 +
3. We only listed some examples here. But the same rule applies to all those templates exacted from UBIKThemes (see the first "Recommended" point).'''
 +
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| {StaticResource UBIKChildItem}
 +
| {Binding [UBIKChildItem], Source={x:Static services:TemplateService.Instance}}
 +
|-
 +
| {StaticResource UBIKDocumentItem}
 +
| {Binding [UBIKDocumentItem], Source={x:Static services:TemplateService.Instance}}
 +
|-
 +
| {StaticResource UBIKMainItem}
 +
| {Binding [UBIKMainItem], Source={x:Static services:TemplateService.Instance}}
 +
|}
  
 +
=== OnPlatform and OnIdiom syntax changes ===
 +
'''All code containing the OnPlatform and OnIdiom functionalities needs to be refactored as the syntax changed. It is recommended to use the inline syntax (see example) as a default wherever possible, but there are also other approaches.
 +
When using Bindings, Resources or other complex values in OnIdiom or OnPlatform values, it should only be used with the inline syntax.
 +
Please rely to the wiki for more details and possibilities (e.g. if the value contains multiple elements): https://wiki.augmensys.com/index.php?title=XAML_Tips#Platform_and_Device-specific_UI_with_OnPlatform_and_OnIdiom'''
 +
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| OnPlatform
 +
| <syntaxhighlight lang="xml">
 +
<TheControl
 +
        ...
 +
        TheProperty="{OnPlatform Android=False, iOS=True}" />
  
 +
<TheControl
 +
    ...
 +
    TheProperty="{OnPlatform Android={Binding ...}, iOS={Binding ...}}" />
 +
</syntaxhighlight>
  
 +
|-
 +
| OnIdiom
 +
| <syntaxhighlight lang="xml">
 +
<TheControl
 +
        ...
 +
        TheProperty="{OnIdiom Phone=False, Desktop=True}" />
  
 +
<TheControl
 +
    ...
 +
    TheProperty="{OnIdiom Desktop={Binding ...}, Phone={Binding ...}}" />
 +
</syntaxhighlight>
 +
|-
 +
| WinPhone
 +
| WinUI
 +
|-
 +
| <syntaxhighlight lang="xml">
 +
e.g.
 +
<ColumnDefinition.Width>
 +
  <OnIdiom
 +
        ...
 +
        Desktop="Auto"
 +
        Phone="*" />
 +
</ColumnDefinition.Width>
 +
or
 +
<RowDefinition.Height>
 +
  <OnPlatform
 +
        ...
 +
        Android="Auto"
 +
        iOS="*" />
 +
</RowDefinition.Height>
 +
</syntaxhighlight>
 +
| <syntaxhighlight lang="xml">
 +
<ColumnDefinition Width="{OnIdiom Desktop='Auto', Phone='*'}" />
  
= Advanced =
+
<RowDefinition Height="{OnPlatform Android='Auto', iOS='*'}" />
 +
</syntaxhighlight>
 +
|}
  
== Feature related ==
+
=== Remove default ControlTemplate ===
 
+
'''The default ControlTemplate should be removed from the ContentView if the ControlTemplate property of that ContentView is determined by DataTriggers. The same applies to ContentControls. The default ContentTemplate should be removed from the controls:ContentControl if the ContentTemplate property of that ContentControl is determined by DataTriggers.'''
=== Customizing Menu Button {{Version/XamarinSince|4.6.0}} ===  
+
Using only DataTriggers without a default ControlTemplate ensures that content is created only after the trigger evaluation is complete. If a default ControlTemplate is set, it is always loaded immediately, even if another template is applied right after.
 
+
For more details refer to [[Xamarin_XAML#Conflicting_Triggers|Conflicting Triggers]].
For the Xamarin Client, it is possible to show/hide the '''Menu button''' of the client.<br>
+
{| class="wikitable"
To customize the visibility of the '''Menu button''' you have to edit the default XAML '''Themes''' file.
+
! Xamarin
 
+
! MAUI
* Search for "'''ShowMenuToolBar'''" in the Themes file.
+
|-
<br>
+
| <syntaxhighlight lang="xml">
If there isn't any "'''ShowMenuToolBar'''" in your XAML Themes file, you just have to create one, like this:
+
<ContentView x:Name="containerName" ControlTemplate="{StaticResource YourTemplate}">
 
+
     <ContentView.Triggers>
<source lang = "xml">
+
         <DataTrigger
<x:Boolean x:Key="ShowMenuToolBar">true</x:Boolean>
+
             Binding="{Binding Source={x:Reference containerName}, Path=...}"
</source>
+
             TargetType="ContentView"
 
+
            Value="true">
* Set the value to "true" to show the '''Menu button''' (default value).
+
            <Setter Property="ControlTemplate"
* Set the value to "false" to hide the '''Menu button'''.
+
                    Value="{StaticResource YourTemplate}" />
 
+
         </DataTrigger>
<gallery widths="300" heights="500">
+
        ...
File:menu_button_show.png|Menu Button shown
+
     </ContentView.Triggers>
File:menu_button_hiddenV3.png|Menu Button hidden
+
</gallery>
+
 
+
 
+
 
+
 
+
 
+
 
+
 
+
=== Content filtering ===
+
For the Xamarin clients, an "SfDataSourceExt" type is available for filtering list of items using defined expressions.<br />
+
The following example demonstrates how to filter for child items having certain property values. The filtered list and its count can be displayed in the UI.<br />
+
<syntaxhighlight lang="xml">
+
<ContentView
+
    xmlns:controls="clr-namespace:UBIK.CPL.Controls;assembly=UBIK.CPL"
+
    ...>
+
     <ContentView.Resources>
+
         <ResourceDictionary>
+
             <x:String x:Key="Expresssion">Item.Values[&quot;MP_YEAR&quot;].Contains(&quot;2019&quot;)</x:String>
+
             <controls:SfDataSourceExt  x:Key="FilteredList" Expression="{StaticResource Expresssion}" ItemsSource="{Binding Children.Items}" Unloaded="{Binding SkipFiltering}" />
+
        </ResourceDictionary>
+
    </ContentView.Resources>
+
    <StackLayout Orientation="Vertical">
+
        <Label Text="{Binding DisplayItemsCount, Source={StaticResource FilteredList}}" />
+
         <controls:SfListViewExt ItemsSource="{Binding DisplayItems, Source={StaticResource FilteredList}}" />
+
     </StackLayout>
+
 
</ContentView>
 
</ContentView>
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
| <syntaxhighlight lang="xml">
 +
<ContentView x:Name="containerName">
 +
    <ContentView.Triggers>
 +
        <DataTrigger
 +
            Binding="{Binding Source={x:Reference containerName}, Path=...}"
 +
            TargetType="ContentView"
 +
            Value="true">
 +
            <Setter Property="ControlTemplate"
 +
                    Value="{StaticResource YourTemplate}" />
 +
        </DataTrigger>
 +
        ...
 +
    </ContentView.Triggers>
 +
</ContentView>
 +
</syntaxhighlight>
 +
|}
  
==== Notes on using bindings in ResourceDictionary ====
 
In the example shown above, you can see <code>{Binding Children.Items}</code> is used. For this to work, the SfDataSourceExt control inside the ResourceDictionary must inherit the binding data context properly.
 
* In this case, that data context comes from where this template (UBIKChildArea.xamlx) is used, namely the UBIKContentArea.xamlx;
 
* When referencing this template in a ContentView control, the binding data context is '''NOT''' automatically inherited by the controls inside the ResourceDictioinary;
 
* That's why we use a UBIKContentView control instead. You can find its example usage in the default UBIKContentArea.xamlx template.
 
  
  
  
  
 +
=== Remove HeadlessLayout style ===
 +
'''This Style has no longer any effect in MAUI and even degrades app performance. We are keeping it in our standard UBIKThemes under the Obsolete section to maintain backward compatibility.'''
 +
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| <syntaxhighlight lang="xml">
 +
<Style x:Key="HeadlessLayout" TargetType="Layout">
 +
            <Setter Property="CompressedLayout.IsHeadless" Value="{OnPlatform False, Android=True}" />
 +
</Style>
 +
</syntaxhighlight>
 +
| Delete it from your custom UBIKThemes file
 +
|}
  
 
+
=== Additional mandatory rule ===
 
+
'''In the customizings, however, it can and should be removed entirely, as it negatively affects performance, as mentioned before. To remove the style, you can simply use VS Code or any other editor with a search function and look through all custom .xaml files for usages of {StaticResource HeadlessLayout} or {DynamicResource HeadlessLayout}. These references should be removed from the respective elements or controls.'''
=== Search ===
+
{| class="wikitable"
In the default UBIKMenuArea.xamlx (where the search UI is hosted), there are two commands associated with two events. See below.
+
! Xamarin
<syntaxhighlight lang="xml">
+
! MAUI
<SearchBar ...>
+
|-
     <SearchBar.Behaviors>
+
| <syntaxhighlight lang="xml">
        <behaviors:EventToCommandBehavior
+
<ContentView
            Command="{Binding FreeTextSearchCommand}"
+
     Grid.Column="2"
            CommandParameter="{Binding Path=Text, Source={x:Reference SearchField}}"
+
    ControlTemplate="{DynamicResource ChildCountBadge}"
            EventName="SearchButtonPressed" />
+
    HorizontalOptions="Start"
        <behaviors:EventToCommandBehavior Command="{Binding DelayedFreeTextSearchCommand}" EventName="TextChanged" />
+
    Style="{DynamicResource HeadlessLayout}"
    </SearchBar.Behaviors>
+
    VerticalOptions="Start" />
</SearchBar>
+
 
</syntaxhighlight>
 
</syntaxhighlight>
<br />
+
| <syntaxhighlight lang="xml">
The one for <code>SearchButtonPressed</code> is responsible for executing searches after the user confirms the input (e.g. pressing {{{keypress|Enter}}} or the search button).
+
<ContentView
<br />
+
    Grid.Column="2"
The one for <code>TextChanged</code> is responsible for the "search as you type" behavior. Namely it triggers an automatic search shortly (half second) after the user stops changing the text in the search bar. It can be turned off by simply removing that particular behavior.
+
    ControlTemplate="{DynamicResource ChildCountBadge}"
 +
    HorizontalOptions="Start"
 +
    VerticalOptions="Start" />
 +
</syntaxhighlight>
 +
|}
  
=== DisplayViewCommand ===
+
=== Avoid multiple SfPullToRefresh controls ===
This command can be used to [[Custom_View_(Client)|display custom views]].
+
'''To prevent an issue that causes the app to freeze, avoid placing more than one SfPullToRefresh controls as siblings in a xaml hierarchy'''. If switching between contents in a SfPullToRefresh control is necessary use separate DataTemplates for the contents and use e.g. a DataTrigger to set one of them as the ControlTemplate of a ContentControl inside the SfPullToRefresh control.
  
 +
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| <syntaxhighlight lang="xml">
 +
<pullToRefresh:SfPullToRefresh IsVisible = {Binding ShowContentB, Converter={StaticResource BoolToNotBool}}>
 +
    <pullToRefresh:SfPullToRefresh.PullableContent>
 +
        <controls:SfListViewExt
 +
            x:Name="ContentA"
 +
            .../>
 +
    </pullToRefresh:SfPullToRefresh.PullableContent>
 +
</pullToRefresh:SfPullToRefresh>
  
 
+
<pullToRefresh:SfPullToRefresh  IsVisible = {Binding ShowContentB}>
 
+
    <pullToRefresh:SfPullToRefresh.PullableContent>
 
+
        <controls:SfListViewExt
 
+
            x:Name="ContentB"
 
+
            .../>
== Performance ==
+
    </pullToRefresh:SfPullToRefresh.PullableContent>
To get a good performance in the app UI when using your Xaml customizings, it is recommended to try the following.
+
</pullToRefresh:SfPullToRefresh>
* Always keep your UI structure simple. Choose the most efficient layouts for the scenarios and avoid unnecessary UI elements. Please refer to [https://docs.microsoft.com/en-us/xamarin/xamarin-forms/deploy-test/performance#choose-the-correct-layout "choose the correct layout"] and [https://docs.microsoft.com/en-us/xamarin/xamarin-forms/deploy-test/performance#reduce-the-visual-tree-size "reduce the visual tree size"];
+
* Turn on [https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/layouts/layout-compression layout compression] on wrapping elements that don't have any visual parameters set (reasons stated in the linked documentation).
+
{{Hint|There is a default "HeadlessLayout" style available in the app you can use on elements such as Grids, StackLayouts, ContentViews, etc. It turns on [[Xamarin_XAML#Layout_compression_examples|layout compression]] on the applied elements in Xamarin.Android (since we find it not worth the effort in Xamarin.iOS).}}
+
{{Attention|If possible, one should always favor designing the UI with less wrapping elements over turning on layout compression on unnecessary ones.}}
+
 
+
=== General performance tips ===
+
* Reduce number of views per page
+
* Don’t bind things if they could be set static easily
+
* If you do not change the default, don’t set it explicit. For example, [https://docs.microsoft.com/en-us/dotnet/api/Xamarin.Forms.StackLayout.Orientation?view=xamarin-forms Orientation of StackLayout] is “Vertical” by default. No need to set it manually if you don’t want to change it.
+
* Transparency is expensive.
+
* Use async/await to avoid blocking user interface.
+
* Do not put ListViews into ScrollViews.
+
* To stack elements, create a grid and add them to the same cell one after another. It’s cheaper than RelativeLayout.
+
* Use Margins instead of Paddings.
+
 
+
=== Layouts ===
+
* Choose correct Layout, e.g. no need to add a StackLayout if it only has 1 child.
+
* [https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/layouts/layout-options LayoutOptions].Fill or .FillAndExpand are best choice in most cases. And they are already default, so no need to set.
+
* Autosize of rows and columns in grids should used as few as possible.
+
* RelativeLayout is very expensive. Try to avoid.
+
* In StackLayout, make sure there is only 1 LayoutOptions Expand.
+
 
+
=== Labels ===
+
* Use [https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/text/label#formatted-text FormattedText] instead of multiple labels.
+
* Use [https://docs.microsoft.com/en-us/dotnet/api/xamarin.forms.linebreakmode?view=xamarin-forms Linebreakmode] NoWrap (which is already the default).
+
* Avoid [https://docs.microsoft.com/en-us/dotnet/api/Xamarin.Forms.Label.VerticalTextAlignment?view=xamarin-forms VerticalTextAlignment]. Anyway, if needed, use VerticalTextAligment instead of
+
VerticalOptions.
+
* Use the "TextTransform" attribute to convert the value of the "Text" attribute to either uppercase or lowercase.
+
<syntaxhighlight lang="xml">
+
<Label
+
      TextTransform="Uppercase"
+
      Text="{Binding Properties.AllItems[DESCR].DisplayValue}" />
+
</syntaxhighlight >
+
 
+
 
+
 
+
 
+
 
+
 
+
 
+
=== Images ===
+
* Provide images in optimized width and size. Provide multiple resolutions for different usage.
+
* Set [https://docs.microsoft.com/en-us/dotnet/api/Xamarin.Forms.Image.IsOpaque?view=xamarin-forms IsOpaque] to true if image is opaque.
+
 
+
=== Layout compression examples ===
+
<syntaxhighlight lang="xml">
+
<ContentView ControlTemplate="{DynamicResource ChangedSymbol}" Style="{DynamicResource HeadlessLayout}" />
+
 
</syntaxhighlight>
 
</syntaxhighlight>
The code above removes one wrapping element added automatically by the ContentView and demonstrates a good reason to use layout compression because it can not be avoided otherwise.
+
| <syntaxhighlight lang="xml">
<syntaxhighlight lang="xml">
+
<Grid>
<Grid Margin="2" Style="{DynamicResource HeadlessLayout}">
+
    <Grid.Resources>
    <Label Margin="5" Text="{Binding Title}" />
+
        <ResourceDictionary>
 +
            <DataTemplate x:Key="TemplateA">
 +
                <controls:SfListViewExt
 +
                    x:Name="ContentA"
 +
                    .../>
 +
            </DataTemplate>
 +
            <DataTemplate x:Key="TemplateB">
 +
                <controls:SfListViewExt
 +
                    x:Name="ContentB"
 +
                    .../>
 +
            </DataTemplate>
 +
        </ResourceDictionary>
 +
    </Grid.Resources>
 +
  <pullToRefresh:SfPullToRefresh>
 +
      <pullToRefresh:SfPullToRefresh.PullableContent>
 +
          <controls:ContentControl>
 +
              <controls:ContentControl.Triggers>
 +
                  <DataTrigger
 +
                      Binding="{Binding ShowContentB}}"
 +
                      TargetType="controls:ContentControl"
 +
                      Value="false">
 +
                      <Setter Property="ContentTemplate" Value="{DynamicResource TemplateA}" />
 +
                  </DataTrigger>
 +
                  <DataTrigger
 +
                      Binding="{Binding ShowContentB}}"
 +
                      TargetType="controls:ContentControl"
 +
                      Value="true">
 +
                      <Setter Property="ContentTemplate" Value="{DynamicResource TemplateB}" />
 +
                  </DataTrigger>
 +
              </controls:ContentControl.Triggers>
 +
          </controls:ContentControl>
 +
      </pullToRefresh:SfPullToRefresh.PullableContent>
 +
    </pullToRefresh:SfPullToRefresh>
 
</Grid>
 
</Grid>
 
</syntaxhighlight>
 
</syntaxhighlight>
This example, on the other hand, demonstrates a bad usage of layout compression because it can be easily achieved by better designs such as using only the Label with a merged margin.
+
|}
  
Sometimes the content inside a compressed layout appears on a wrong z-index level. For eample:
+
[[Category:How-To|Convert Xamarin XAMLs to Maui]]
<syntaxhighlight lang="xml">
+
[[Category:MAUI|Convert Xamarin XAMLs to Maui]]
<BoxView CornerRadius="14" HeightRequest="28" WidthRequest="28" Color="Red" />
+
[[Category:XAML|Convert Xamarin XAMLs to Maui]]
<ContentView ControlTemplate="{DynamicResource Badge}" Style="{DynamicResource HeadlessLayout}">
+
    <Label Text="{Binding Title}" />
+
</ContentView >
+
</syntaxhighlight>
+
According to the order of the BoxView and the Label, the latter should appear on top of the former (later ones have higher z-index levels). However, this can be disturbed by layout compression, causing the exact opposite.
+
In this case, you can add a <code>xmlns:android="clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"</code> namespace to your Xaml file and manually elevate the Label by specifying <code>android:VisualElement.Elevation="X"</code> on it accordingly (X is the delta of the z-index level you want).
+
  
=== SfListViewExt ===
+
=== Avoid using SyncFusion ListViews directly in SfPullToRefresh controls ===
For performance reasons, we created an extended version of the Syncfusion SfListView control and it should be used for all lists in customizings. You need to add a namespace like <code>xmlns:controls="clr-namespace:UBIK.CPL.Controls;assembly=UBIK.CPL"</code> to your Xaml file and use it like <code><controls:SfListViewExt ... /></code>.
+
'''To prevent an issue on some Android devices that can cause some items in a SfListViewExt to be rendered incorrectly or not at all after triggering a PullToRefresh, avoid placing a SfListViewExt directly in a SfPullToRefresh control'''.
{{Hint|Technically speaking, SfListViewExt informs item view models when their corresponding item views appear on / disappear from the screen. This way, view models can skip a lot of unnecessary work (on the UI) when their views are not visible. '''If SfListView is used instead, the item views will not reflect content changes until the page is reloaded/refreshed.'''}}
+
Instead, wrap the ListView in a DataTemplate and use it in a ContentControl as a child of the SfPullToRefresh control, as shown in the Xaml example below.
  
It is possible to configure the ListView, that the ScrollPosition is remembered when navigating away from the page, to do so, the property RembemberScrollPosition needs to be set to true. If unset, the value defaults to false and the scroll positions are not remembered.
+
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| <syntaxhighlight lang="xml">
 +
<pullToRefresh:SfPullToRefresh>
 +
    <pullToRefresh:SfPullToRefresh.PullableContent>
 +
        <controls:SfListViewExt
 +
            .../>
 +
    </pullToRefresh:SfPullToRefresh.PullableContent>
 +
</pullToRefresh:SfPullToRefresh>
 +
</syntaxhighlight>
 +
| <syntaxhighlight lang="xml">
 +
<Grid>
 +
    <Grid.Resources>
 +
        <ResourceDictionary>
 +
            <DataTemplate x:Key="ListViewTemplate">
 +
                <controls:SfListViewExt
 +
                    .../>
 +
            </DataTemplate>
 +
        </ResourceDictionary>
 +
    </Grid.Resources>
  
<syntaxhighlight lang="xml">
+
    <pullToRefresh:SfPullToRefresh>
<controls:SfListViewExt RememberScrollPosition="True"/>
+
      <pullToRefresh:SfPullToRefresh.PullableContent>
 +
          <controls:ContentControl ContentTemplate="{DynamicResource ListViewTemplate}" />
 +
      </pullToRefresh:SfPullToRefresh.PullableContent>
 +
    </pullToRefresh:SfPullToRefresh>
 +
</Grid>
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
|}
  
When using multiple Lists on one page (e.g. in a TabbedView), it is necessary to set the AutomationId property uniquely for each list on the page, to support the remembering of the ScrollPosition.
+
[[Category:How-To|Convert Xamarin XAMLs to Maui]]
 +
[[Category:MAUI|Convert Xamarin XAMLs to Maui]]
 +
[[Category:XAML|Convert Xamarin XAMLs to Maui]]
  
 +
=== Rename SfPullToRefresh properties ===
 +
'''Renamed in Maui, however, currently these properties cause an issue and should not be included in customizings.'''
 +
SfPullToRefresh:
 +
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| RefreshContentHeight, RefreshContentWidth
 +
| RefreshViewHeight, RefreshViewWidth
 +
|}
  
= Known issues =
+
=== Rename progress colors ===
 +
'''Renamed in Maui.'''
 +
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| ProgressBackgroundColor, ProgressStrokeColor
 +
| ProgressBackground, ProgressColor
 +
|}
  
=== Headlesslayout style in custom UBIKThemes causing problems ===
+
=== Rename Default Font Name  ===
 +
'''The name of the default font was slightly renamed in the UBIKThemes file in MAUI — the first letter now needs to be capitalized. Otherwise, on iOS, bold or italic styles are not applied correctly, and changing a property in Direct Editing, for example, would result in no visible change.'''
 +
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| <x:String x:Key="TextFontFamily">inter</x:String>
 +
| <x:String x:Key="TextFontFamily">Inter</x:String>
 +
|}
  
In our default '''UBIKThemes''', we are currently using this style for a headless layout:
+
== SfTabView ==
  
<syntaxhighlight lang="xml">
+
=== Namespace Update ===
<Style x:Key="HeadlessLayout" TargetType="Layout">
+
'''Update Syncfusion TabView namespace.'''
    <Setter Property="CompressedLayout.IsHeadless">
+
Replace the Xamarin namespace with the MAUI equivalent.
        <Setter.Value>
+
            <OnPlatform
+
                x:TypeArguments="x:Boolean"
+
                Android="True"
+
                WinPhone="False"
+
                iOS="False" />
+
        </Setter.Value>
+
    </Setter>
+
</Style>
+
</syntaxhighlight>
+
  
<br/>
+
{| class="wikitable"
This style is used for better UI performance on Android.
+
! Xamarin
Using this style in a custom '''UBIKThemes''' could cause an exception & an app crash.
+
! MAUI
The best case would be not to have it in the custom themes file because it's unnecessary if you don't want to change anything here.
+
|-
 +
| clr-namespace:Syncfusion.XForms.TabView;assembly=Syncfusion.SfTabView.XForms
 +
| clr-namespace:Syncfusion.Maui.Toolkit.TabView;assembly=Syncfusion.Maui.Toolkit
 +
|}
  
'''This issue is only known on tablet devices with Android 14.'''
+
=== Remove Obsolete Converters ===
<br>
+
'''Remove ItemCountToBoolConverter from Resources.'''
<br>
+
This converter is no longer usable in TabView headers.
=== DataTrigger Issues ===
+
==== Conflicting Triggers ====
+
DataTriggers in Xamarin/Maui function better than the equivalent DataTriggerBehavior in UWP as they automatically handle the opposite case when a Boolean attribute value is set. This means that if a boolean DataTrigger is set, such as IsVisible=True, the False visibility will automatically be set if the Binding/Value condition is not met.
+
  
However, this means that stacking multiple DataTriggers on the same attribute can lead to unexpected results. For this reason, it is recommended to use as few DataTriggers as possible.
+
{| class="wikitable"
* Avoid setting the same attribute more than once; do not set the same value as a default attribute and in the DataTrigger (ControlTemplate in the below example).
+
! Xamarin
* Ideally, use only DataTriggers without a default ControlTemplate. This ensures that content is created only after the trigger evaluation is complete. If a default ControlTemplate is set, it is always loaded immediately, even if another template is applied right after.
+
! MAUI
* It is acceptable, but not recommended, to combine a single DataTrigger with an opposite default value, but only if the default value is different from all values set in triggers.
+
|-
 +
| <syntaxhighlight lang="xml"><converters:ItemCountToBoolConverter x:Key="ItemCountToBool" /></syntaxhighlight>
 +
| [remove]
 +
|}
  
<tabs>
+
=== Add StringFormatConverter ===
<tab name="Don't Do">
+
'''Use StringFormatConverter for combined header strings.'''
<source lang = "xml">
+
Syncfusion MAUI TabView uses a single `Header` property instead of templates.
<ContentView x:Name="editorValueContainer" ControlTemplate="{StaticResource SelListTemplate}">
+
For all tab headers, formatters should be specified to assemble the text. As a best practice, even for simple header strings.
  <ContentView.Triggers>
+
    <DataTrigger Binding="{Binding Source={x:Reference editorValueContainer}, Path=BindingContext.PropertyViewModel.ShowComboBox}" TargetType="ContentView" Value="true">
+
      <Setter Property="ControlTemplate" Value="{StaticResource SelListTemplate}" />
+
    </DataTrigger>
+
    <DataTrigger Binding="{Binding Source={x:Reference editorValueContainer}, Path=BindingContext.PropertyViewModel.ShowComboBox}" TargetType="ContentView" Value="false">
+
      <Setter Property="ControlTemplate" Value="{StaticResource TextTemplate}" />
+
    </DataTrigger>
+
  </ContentView.Triggers>
+
</ContentView>
+
</source>
+
<br>
+
</tab>
+
  
<tab name="Instead Do">
+
{| class="wikitable"
<source lang = "xml">
+
! Xamarin
<ContentView x:Name="editorValueContainer">
+
! MAUI
  <ContentView.Triggers>
+
|-
    <DataTrigger Binding="{Binding Source={x:Reference editorValueContainer}, Path=BindingContext.PropertyViewModel.ShowComboBox}" TargetType="ContentView" Value="true">
+
| [template-based header]
      <Setter Property="ControlTemplate" Value="{StaticResource SelListTemplate}" />
+
| <syntaxhighlight lang="xml"><converters:StringFormatConverter x:Key="ChildrenTabFormatter" Parameter1="{resources:Translate ChildrenTitle}" /></syntaxhighlight>
    </DataTrigger>
+
|}
    <DataTrigger Binding="{Binding Source={x:Reference editorValueContainer}, Path=BindingContext.PropertyViewModel.ShowComboBox}" TargetType="ContentView" Value="false">
+
      <Setter Property="ControlTemplate" Value="{StaticResource TextTemplate}" />
+
    </DataTrigger>
+
  </ContentView.Triggers>
+
</ContentView>
+
</source>
+
</tab>
+
  
<tab name="Acceptable, but not recommended">
+
In the above example, a previously defined formatter (that must be also added to the resource dictionary) is used to fill in the header attribute value.
<source lang = "xml">
+
⚠️ Avoid using bindings in `Parameter1` and `Parameter2` — use static strings only. For complex cases, use `EvalExpression`.
<ContentView x:Name="editorValueContainer" ControlTemplate="{StaticResource SelListTemplate}">
+
  <ContentView.Triggers>
+
    <DataTrigger Binding="{Binding Source={x:Reference editorValueContainer}, Path=BindingContext.PropertyViewModel.ShowComboBox}" TargetType="ContentView" Value="false">
+
      <Setter Property="ControlTemplate" Value="{StaticResource TextTemplate}" />
+
    </DataTrigger>
+
  </ContentView.Triggers>
+
</ContentView>
+
</source>
+
</tab>
+
</tabs>
+
  
  
Line 365: Line 708:
  
  
 +
=== FontImageSource Styling ===
 +
'''Add local style for FontImageSource.'''
 +
Global styles are not supported.
  
 +
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| [global FontImageSource style]
 +
| <syntaxhighlight lang="xml">
 +
<Style TargetType="FontImageSource">
 +
  <Setter Property="FontFamily" Value="{DynamicResource UBIKSymbols}" />
 +
  <Setter Property="Color" Value="{DynamicResource UBIKLightThemeColor}" />
 +
</Style>
 +
</syntaxhighlight>
 +
|}
  
==== DataTrigger to set Footer Properties in a ListView ====
+
=== TabView Structure ===
 +
'''Use Grid to wrap TabView.'''
 +
MAUI TabView requires a different structure and property set.
  
When using a DataTrigger to set the properties of a footer for a ListView, the sequence of the properties inside the DataTrigger might matter in some cases.
+
{| class="wikitable"
It seems to occur since Version 4.4 due to a Syncfusion update and could lead to the page not being rendered properly and appearing empty.
+
! Xamarin
 
+
! MAUI
If you encounter such an issue, please workaround by adding the IsStickyFooter Property Setter on top inside the DataTrigger.
+
|-
Also, new customizings should consider this workaround as a preventive action.
+
| <syntaxhighlight lang="xml">
 
+
<tabView:SfTabView VisibleHeaderCount="3">
<syntaxhighlight lang="xml">
+
  ...
<!-- THIS DOESN'T WORK IN CERTAIN CASES -->
+
</tabView:SfTabView>
<controls:SfListViewExt ...>
+
<controls:SfListViewExt.Triggers>
+
<DataTrigger TargetType="controls:SfListViewExt" Binding="{Binding Title}" Value="Administration">
+
<Setter Property="FooterTemplate" Value="{DynamicResource AddLoginFooter}" />
+
<Setter Property="FooterSize" Value="64" />
+
<Setter Property="IsStickyFooter" Value="True" />
+
</DataTrigger>
+
</controls:SfListViewExt.Triggers>
+
</controls:SfListViewExt>
+
 
</syntaxhighlight>
 
</syntaxhighlight>
 
+
| <syntaxhighlight lang="xml">
<br/>
+
<Grid>
<syntaxhighlight lang="xml">
+
  <tabView:SfTabView>
<!-- THIS WORKS -->
+
    <tabView:SfTabItem Header="string example header">
<controls:SfListViewExt ...>
+
      <tabView:SfTabItem.ImageSource>
<controls:SfListViewExt.Triggers>
+
        <FontImageSource Glyph="{x:Static resources:UBIKIcons.XXX}" />
<DataTrigger TargetType="controls:SfListViewExt" Binding="{Binding Title}" Value="Administration">
+
      </tabView:SfTabItem.ImageSource>
<Setter Property="IsStickyFooter" Value="True" />
+
      <tabView:SfTabItem.Content>
<Setter Property="FooterTemplate" Value="{DynamicResource AddLoginFooter}" />
+
        <controls:UBIKContentView x:Name="ChildContainer" Converter="{StaticResource ChildAreaTemplateConverter}" />
<Setter Property="FooterSize" Value="64" />
+
      </tabView:SfTabItem.Content>
</DataTrigger>
+
    </tabView:SfTabItem>
</controls:SfListViewExt.Triggers>
+
  </tabView:SfTabView>
</controls:SfListViewExt>
+
</Grid>
 
</syntaxhighlight>
 
</syntaxhighlight>
<br>
+
|}
<br>
+
=== Incorrect glyphs may be shown on buttons ===
+
  
When using glyphs on buttons in Xamarin, sometimes a different glyph than the expected one will show up when viewing the app on Android.
+
=== Property Changes ===
They only seem to appear incorrectly when used as the Text property of the button (with FontFamily set to UBIKSymbols, such as the UBIKIconButton style.)
+
'''Update renamed or removed properties.'''
  
If you encounter this issue please workaround by supplying the icon through the FontImageSource property of the button.
+
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| VisibleHeaderCount
 +
| [not available yet]
 +
|-
 +
| UBIKCenterButtonSettings
 +
| CenterButtonSettings
 +
|-
 +
| SelectionIndicatorSettings
 +
| IndicatorPlacement, IndicatorStrokeThickness, IndicatorBackground
 +
|-
 +
| SelectionColor
 +
| Use VisualStateManager instead
 +
|}
  
<syntaxhighlight lang="xml">
+
⚠️ Refer to [https://help.syncfusion.com/maui/tabview/migration Syncfusion migration guide] for full property list.
<Button Command="{Binding BulkOperation.InvokeOnItemsCommand}">
+
    <Button.ImageSource>
+
          <FontImageSource
+
              FontFamily="{DynamicResource UBIKSymbols}"
+
              Glyph="{x:Static resources:UBIKIcons.MobileDelete}"
+
              Color="{DynamicResource UBIKDarkThemeColor}"
+
              Size="22"/>
+
    </Button.ImageSource>
+
    <Button.CommandParameter>
+
          <classes:KeyValueList>
+
                    <classes:KeyValueParameter Key="Command" Value="DiscardContentCommand" />
+
          </classes:KeyValueList>
+
    </Button.CommandParameter>
+
</Button>
+
</syntaxhighlight>
+
<br>
+
<br>
+
=== Buttons and gesture recognizers ===
+
In Xamarin XAML, you can use gesture recognizers on many UI elements such as a Grid to enable the latter to interact with user inputs.
+
  
However, we've already made quite a lot of experience that gesture recognizers do not always work on Android and iOS when used on a Button control.
+
== OnPlatform + OnIdiom backup ==
For example, the following Button won't properly invoke the command on said platforms.
+
  
<syntaxhighlight lang="xml">
+
=== Syntax Changes ===
<!-- THIS DOESN'T WORK!!! -->
+
'''Use inline or multi-tag syntax.'''
<Button ...>
+
Single-tag syntax is outdated and may not build.
    <Button.GestureRecognizers>
+
        <TapGestureRecognizer Command="{Binding ReplicateAsDataCommand}" />
+
    </Button.GestureRecognizers>
+
</Button>
+
</syntaxhighlight>
+
  
<br/>
+
{| class="wikitable"
Therefore, you should always seek alternatives for Buttons.
+
! Xamarin
'''If only one command is needed''', use the standard Command property on the Button, e.g.
+
! MAUI
 +
|-
 +
| <syntaxhighlight lang="xml"><OnPlatform Android="False" iOS="True" /></syntaxhighlight>
 +
| <syntaxhighlight lang="xml"><TheControl TheProperty="{OnPlatform Android=False, iOS=True}" /></syntaxhighlight>
 +
|}
  
<syntaxhighlight lang="xml">
+
{| class="wikitable"
<!-- THIS WORKS -->
+
! Xamarin
<Button Command="{Binding ReplicateAsDataCommand}" .../>
+
! MAUI
 +
|-
 +
| <syntaxhighlight lang="xml">
 +
<OnPlatform x:TypeArguments="TheType">
 +
  <On Platform="Android" Value="False" />
 +
  <On Platform="iOS" Value="True" />
 +
</OnPlatform>
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
| Required for multi-tag syntax
 +
|}
  
<br/>
+
=== WinPhone to WinUI ===
Or if multiple commands are needed, use Behaviors '''instead''', e.g.
+
'''Replace WinPhone with WinUI.'''
  
<syntaxhighlight lang="xml">
+
{| class="wikitable"
<!-- THIS ALSO WORKS -->
+
! Xamarin
<Button xmlns:behaviors="clr-namespace:UBIK.CPL.Behaviors;assembly=UBIK.CPL" ...>
+
! MAUI
    <Button.Behaviors>
+
|-
        <behaviors:EventToCommandBehavior Command="{Binding ReplicateAsDataCommand}" EventName="Clicked" />
+
| WinPhone
        <behaviors:EventToCommandBehavior Command="{Binding NavigateBackCommand}" EventName="Clicked" />
+
| WinUI
    </Button.Behaviors>
+
|}
</Button>
+
</syntaxhighlight>
+
<br>
+
<br>
+
=== No dynamic reloading of Document Viewer ===
+
[[File:PDFViewerFixWiki.JPG]]
+
  
In a customizing that combines a single-selection list of documents with some kind of viewer (especially UBIKDocumentContentArea), it is generally the case that switching the document causes the document viewer to then appear blank. The technical reason is that the PDFViewer used in the UBIKDocumentContentArea is not reloaded once the document content is reloaded.  
+
=== GridLength and TypeArguments ===
 +
'''Avoid x:TypeArguments with GridLength.'''
  
An easy fix is to apply a DataTrigger that unloads the ContentView when the document content is null, as the ContentView is forced to rerender itself when the trigger condition is no longer met.
+
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| <syntaxhighlight lang="xml">
 +
<ColumnDefinition.Width>
 +
  <OnIdiom x:TypeArguments="GridLength" Desktop="Auto" Phone="*" />
 +
</ColumnDefinition.Width>
 +
</syntaxhighlight>
 +
| <syntaxhighlight lang="xml"><ColumnDefinition Width="{OnIdiom Desktop='Auto', Phone='*'}" /></syntaxhighlight>
 +
|}
  
<syntaxhighlight lang="xml">
+
⚠️ Inline syntax is more reliable when using bindings or resources.
<!-- Document Viewer -->
+
<controls:UBIKContentView
+
    x:Name="DoucmentViewer"
+
    BindingContext="{Binding SelectedItem, Source={x:Reference DocumentList}}"
+
    Content="{Binding [UBIKDocumentContentArea], Source={x:Static services:TemplateService.Instance}}">
+
  
    <controls:UBIKContentView.Triggers>
+
=== Special Characters ===
        <DataTrigger TargetType="controls:UBIKContentView" Binding="{Binding SelectedItem.DocumentViewModel.DocumentContent, Source={x:Reference DocumentList}, Converter={StaticResource NullToBool}}" Value="True">
+
'''Wrap values in quotes if they contain commas or strings.'''
            <Setter Property="Content" Value="{x:Null}" />
+
        </DataTrigger>
+
    </controls:UBIKContentView.Triggers>
+
</controls:UBIKContentView>
+
</syntaxhighlight>
+
  
{{UnderConstructionStart}}
+
{| class="wikitable"
=== Issues with SfPullToRefresh in combination with SfListView in MAUI ===
+
! Xamarin
Due to a third-party issue, problems may occur on MAUI for Android when using the SfPullToRefresh control in combination with the SfListViewExt. This can result in navigation sometimes requiring multiple clicks.
+
! MAUI
 +
|-
 +
| Margin={OnIdiom 10,2, Phone=2,0}
 +
| Margin={OnIdiom '10,2', Phone='2,0'}
 +
|}
  
As a workaround, we recommend replacing the SfPullToRefresh control with a custom Refresh button that executes the RefreshCommand, for example directly in the UBIKNavigationBar (as shown in the example above) or in the ToolBarMenu.
+
=== UWP to WinUI ===
The CommandParameter specifies whether the update expiry should be ignored. We highly recommend setting it to True, otherwise, there is no guarantee that the refresh will be executed properly.
+
'''Replace UWP with WinUI in all OnPlatform scopes.'''
  
<syntaxhighlight lang="xml">
+
{| class="wikitable"
<!--  Refresh  -->
+
! Xamarin
<Button
+
! MAUI
      Grid.Column="3"
+
|-
      BackgroundColor="{DynamicResource UBIKDarkThemeColor}"
+
| UWP
      BorderColor="Transparent"
+
| WinUI
      Command="{Binding RefreshCommand}"
+
|}
      CommandParameter="True"
+
      FontSize="16"
+
      IsVisible="{Binding CanExecuteRefreshCommand}"
+
      Style="{DynamicResource UBIKIconButton}"
+
      Text="{x:Static resources:UBIKIcons.ProgressRing}"
+
      TextColor="{DynamicResource UBIKLightTextColor}" />
+
</syntaxhighlight>
+
  
{{UnderConstructionStart}}
+
⚠️ For more details, refer to the [https://wiki.augmensys.com/index.php?title=XAML_Tips#Device_and_Platform_Responsiveness Wiki documentation].
  
[[Category:Client|Xamarin XAML]]
+
[[Category:How-To|Convert Xamarin XAMLs to Maui]]
[[Category:Pages with broken file links|Xamarin XAML]]
+
[[Category:MAUI|Convert Xamarin XAMLs to Maui]]
[[Category:Styling|Xamarin XAML]]
+
[[Category:XAML|Convert Xamarin XAMLs to Maui]]
[[Category:XAML|Xamarin XAML]]
+
[[Category:Xamarin|Xamarin XAML]]
+
[[Category:MAUI|Mobile XAML]]
+

Revision as of 11:30, 5 September 2025

Contents

Information

Frame to Border

Use Border instead of Frame. Frame is deprecated in MAUI. Use Border with `Stroke` instead of `BorderColor`.

Xamarin MAUI
<Frame ... /> <Border ... />

LayoutOptions with "...AndExpand"

Replace "...AndExpand" with Grid layout. Horizontal/VerticalOptions ending in "...AndExpand" are deprecated. Use Grid with `ColumnDefinition Width="*"` for expansion.

GlyphLabel and Styles

Use Label with new styles. Custom controls like `GlyphLabel` are no longer needed. Use `Label` with styles like `UBIKSymbolText`.

Xamarin MAUI
controls:GlyphLabel Label with UBIKSymbolText style

SfTabView Virtualization

Enable virtualization. Add `EnableVirtualization="True"` to all SfTabView instances or styles.

Swipe Menu Icons on Android

Refactor swipe templates and label styles. Icons in swipe menus were truncated on Android. Fixed via refactoring.

Color Style Changes

Dark theme adjustments. Changes to color styles may affect appearance. Review usage of `UBIKDarkThemeColor`, `UBIKDarkTextColor`, etc.

Strikethrough Converter

Use `TextDecorations="Strikethrough"` instead of converter.

Xamarin MAUI
Text="{Binding ..., Converter={StaticResource StrikethroughConverter}}" Text="{Binding ...}" TextDecorations="Strikethrough"

Button Disabled Trigger

Add trigger for disabled buttons. Maui buttons don’t visually change when disabled. Add trigger to style.

TapGestureRecognizer Replacement

Use TappedBehavior instead. TapGestureRecognizer now intercepts taps. Use `TappedBehavior` to avoid conflicts.

Xamarin MAUI
<... .GestureRecognizers><TapGestureRecognizer Command="{Binding ...}" /></... .GestureRecognizers> <... .Behaviors><behaviors:TappedBehavior><behaviors:InvokeCommandAction Command="{Binding ...}" /></behaviors:TappedBehavior></... .Behaviors>

TapGestureRecognizer and Swipe Conflict

Remove TapGestureRecognizer from root Grid. TapGestureRecognizer blocks swipe behavior. Remove it and adapt parent list to trigger navigation.

Navigation in UBIKChildItem

Use EventHandlerBehavior for navigation.

Xamarin MAUI
[none] <controls:SfListViewExt.Behaviors><behaviors:EventHandlerBehavior EventName="ItemTapped"><behaviors:InvokeCommandAction Command="{Binding AppStatus.RootList.Items[0].NavigateToChildrenCommand}" /></behaviors:EventHandlerBehavior></controls:SfListViewExt.Behaviors>

Recommended

Extract DataTemplates from UBIKThemes

Create separate .xamlx files for the following DataTemplates. It's not so easy about the namespaces:

  • In our default templates, it's enough to add the following ones to every new separate .xamlx files at the beginning;
   * xmlns:ctrls="clr-namespace:UBIK.MAUI.Controls;assembly=UBIK.MAUI";
   * xmlns:platform="clr-namespace:UBIK.MAUI.Platform.Renderers;assembly=UBIK.MAUI";
   * xmlns:resources="clr-namespace:UBIK.MAUI.Resources;assembly=UBIK.MAUI";
   * xmlns:services="clr-namespace:UBIK.MAUI.Services;assembly=UBIK.MAUI";
   * xmlns:uiservices="clr-namespace:UBIK.UI.Services;assembly=UBIK.UI".
  • But we don't know if other namespaces might be used in the custom templates. So it might be necessary to really analyze the namespaces used and find/copy them from the UBIKThemes file.
  Templates  
UBIKMainItem UBIKChildItem UBIKTaskItem
UBIKTaskProperty UBIKTaskPropertyString UBIKTaskPropertyDouble
UBIKTaskPropertyInt UBIKTaskPropertyNumeric UBIKTaskPropertyDateTime
UBIKTaskPropertyGeoData UBIKTaskPropertyBool UBIKTaskPropertyPopup
UBIKTaskPropertyList UBIKTaskPropertyGuid UBIKPropertyItem
UBIKPropertyTextLengthHint UBIKPropertyDirectEditButtons UBIKTaskPropertyEditButtons
UBIKPropertyDirectItemString UBIKPropertyDirectItemDouble UBIKPropertyDirectItemInt
UBIKPropertyDirectItemMinMax UBIKPropertyDirectItemNumeric UBIKPropertyDirectItemDateTime
UBIKPropertyDirectItemGeoData UBIKPropertyDirectItemBool UBIKPropertyDirectItemPopup
UBIKPropertyDirectItemList UBIKPropertyDirectItemGuid UBIKDocumentItem
UBIKSearchResultItem UBIKDefaultHotSpot UBIKAngularLinkHotSpot
UBIKRoundLinkHotSpot UBIKInputHotSpot UBIKSignatureHotSpot
UBIKObjectHotSpot UBIKMediaHotSpot

VideoPlayer Adjustments

This property is repsonsible for the video player regarding how or if it should get scaled up depending on the value of the Aspect property

Xamarin MAUI
VideoPlayer...
VideoPlayer Aspect="AspectFit"

The UriToVideoSourceConverter is no longer needed because the conversion of the VideoSource gets done by the MediaElement itself. It is marked as obsolete and should therefore no longer be used.

Xamarin MAUI
<controls:VideoPlayer... Source="{Binding DocumentViewModel.LocalURI, Converter={StaticResource UriToVideoSource}}"/>
<controls:VideoPlayer... Source="{Binding DocumentViewModel.LocalURI}"/>

The StopTimer property is obsolete and no longer needed because the MediaElement behind does the Stop of the VideoPlayer by itself.

Xamarin MAUI
<controls:VideoPlayer... StopTimer="{Binding Unloaded}" />
<controls:VideoPlayer... />

SearchBar Style Fix on iOS

On iOS, defining a transparent background for the SearchBar control results in a black text on black background appearance.

Xamarin MAUI
[issue on iOS only] UBIKThemes: SearchBar style: <Setter Property="BackgroundColor" Value="Transparent" />
Delete this setter


Remove TapGestureRecognizer from Main Item

Swiping on the Main items was broken by Maui (Documented in Information). The workaround is to remove the TapGestureRecognizer from UBIKMainItem, and add ListView-based navigation similar to that found in UBIKChildArea (with an extended binding path for technical reasons).

Xamarin MAUI
(In UBIKMainItem)
<Grid.GestureRecognizers>
        <TapGestureRecognizer Command="{Binding NavigateToChildrenCommand}" />
</Grid.GestureRecognizers>
[nothing]

Add Navigation Behavior in Child Area

Xamarin MAUI
(In UBIKChildArea)
[nothing]
<controls:SfListViewExt.Behaviors>
        <behaviors:EventHandlerBehavior EventName="ItemTapped">
                <behaviors:InvokeCommandAction Command="{Binding AppStatus.RootList.Items[0].NavigateToChildrenCommand}" />
        </behaviors:EventHandlerBehavior>
</controls:SfListViewExt.Behaviors>

Highly Recommended

CircularImage to AvatarView

Use AvatarView instead of CircularImage. CircularImage and its wrapping Frame are no longer needed. Use AvatarView from the MAUI Community Toolkit. Add the namespace:

xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
Xamarin MAUI
<Frame><CircularImage Source="..." /></Frame> <toolkit:AvatarView ImageSource="..." BorderWidth="..." BackgroundColor="..." />

GlyphSize to FontSize and Alignment

Use FontSize and alignment properties instead of GlyphSize. The GlyphSize property is obsolete. Use FontSize and add alignment properties for better layout.

Xamarin MAUI
<Setter Property="GlyphSize" Value="13.0" /> <Setter Property="FontSize" Value="16" />
<Setter Property="VerticalOptions" Value="Center" />
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalTextAlignment" Value="Center" />
<Setter Property="HorizontalTextAlignment" Value="Center" />

StaticResource to DynamicResource

Use DynamicResource and BaseResourceKey. For dynamic theming, use DynamicResource instead of StaticResource. When inheriting styles, use BaseResourceKey.

Xamarin MAUI
BasedOn="{StaticResource SomeStyle}"
Style="{StaticResource SomeStyle}"
BaseResourceKey="SomeStyle"
Style="{DynamicResource SomeStyle}"

UBIKPropertyDirectEditListPopup and Guid link direct editing

We have changed the way the content item template is defined and used in UBIKPropertyDirectEditListPopup, which is used in the direct editing of Guid link properties. This change aims to simplify the template and is also due to the fact that the old approach has a poor performance in the new MAUI app.

Xamarin MAUI
<DataTemplate x:Key="PopupFilterQueryItemTemplate">
    ...
</DataTemplate>

The DataTemplate "PopupFilterQueryItemTemplate" is no longer needed and should be removed.

<controls:SfListViewExt
   x:Name="FilterQueryResultList"
   ...
   ItemTemplate="{StaticResource PopupFilterQueryItemTemplate}" />
<controls:SfListViewExt
   x:Name="FilterQueryResultList"
   ...
   ItemTemplate="{Binding [UBIKChildItem], Source={x:Static services:TemplateService.Instance}}">
    <controls:SfListViewExt.Behaviors>
        <behaviors:EventHandlerBehavior EventName="ItemTapped">
            <behaviors:InvokeCommandAction Command="{Binding SetPropertyValueCommand}" Converter="{StaticResource ItemTappedEventArgsToViewModelConverter}" />
            <behaviors:InvokeCommandAction Command="{Binding ConfirmEditCommand}" />
        </behaviors:EventHandlerBehavior>
    </controls:SfListViewExt.Behaviors>
</controls:SfListViewExt>

In comparison, the old solution defines a "PopupFilterQueryItemTemplate" DataTemplate which simply wraps the needed selection related behaviors to the existing "UBIKChildItem" template. The new solution reuses the "UBIKChildItem" template directly and add the same but adapted behaviors to the list view instead.

Mandatory

Namespace conversion

Update all namespaces to MAUI equivalents. All `xmlns` and `clr-namespace` declarations must be updated to their MAUI counterparts.

Xamarin MAUI
xmlns="http://xamarin.com/schemas/2014/forms" xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core clr-namespace:Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific;assembly=Microsoft.Maui.Controls
clr-namespace:UBIK.CPL.Behaviors;assembly=UBIK.CPL clr-namespace:UBIK.MAUI.Behaviors;assembly=UBIK.MAUI
clr-namespace:UBIK.CPL.Classes;assembly=UBIK.CPL clr-namespace:UBIK.MAUI.Classes;assembly=UBIK.MAUI
clr-namespace:UBIK.CPL.Controls;assembly=UBIK.CPL clr-namespace:UBIK.MAUI.Controls;assembly=UBIK.MAUI
clr-namespace:UBIK.CPL.Converters;assembly=UBIK.CPL clr-namespace:UBIK.MAUI.Converters;assembly=UBIK.MAUI
clr-namespace:UBIK.CPL.Platform.Renderers;assembly=UBIK.CPL clr-namespace:UBIK.MAUI.Platform.Controls;assembly=UBIK.MAUI
clr-namespace:UBIK.CPL.Services;assembly=UBIK.CPL clr-namespace:UBIK.MAUI.Services;assembly=UBIK.MAUI
clr-namespace:UBIK.CPL.Resources;assembly=UBIK.CPL clr-namespace:UBIK.MAUI.Resources;assembly=UBIK.MAUI
clr-namespace:UBIK.Scanning.Forms;assembly=UBIK.Scanning.Forms clr-namespace:UBIK.MAUI.Controls.ZXingScanner;assembly=UBIK.MAUI
clr-namespace:Syncfusion.SfPullToRefresh.XForms;assembly=Syncfusion.SfPullToRefresh.XForms clr-namespace:Syncfusion.Maui.Toolkit.PullToRefresh;assembly=Syncfusion.Maui.Toolkit
clr-namespace:Syncfusion.XForms.TabView;assembly=Syncfusion.SfTabView.Xforms clr-namespace:Syncfusion.Maui.Toolkit.TabView;assembly=Syncfusion.Maui.Toolkit
clr-namespace:Syncfusion.ListView.XForms;assembly=Syncfusion.SfListView.XForms clr-namespace:Syncfusion.Maui.ListView;assembly=Syncfusion.Maui.ListView
clr-namespace:Syncfusion.XForms.TreeView;assembly=Syncfusion.SfTreeView.XForms clr-namespace:Syncfusion.Maui.TreeView;assembly=Syncfusion.Maui.TreeView
clr-namespace:Syncfusion.XForms.TextInputLayout;assembly=Syncfusion.Core.XForms clr-namespace:Syncfusion.Maui.Core;assembly=Syncfusion.Maui.Core

For classes referenced in the XAML code, please don't forget to also replace "CPL" with "MAUI" in the class namespace.



Syncfusion control updates

There are some other properties that have changed their names on SfListView/SfListViewExt. We don't use them in our standard XAMLs. But if you use them in customizings, please refer to https://help.syncfusion.com/maui/listview/migration for the complete list.

Xamarin MAUI
SfListViewExt:
    * ItemHolding
ItemLongPress

Syncfusion control updates

Xamarin MAUI
* LeftSwipeTemplate, RightSwipeTemplate StartSwipeTemplate, EndSwipeTemplate

Layout name change

Xamarin MAUI
* <sync:SfListView.LayoutManager>
<sync:SfListView.ItemsLayout>

TemplateService binding for extracted templates

1. We need to add the following namespace where the changes are applied: xmlns:services="clr-namespace:UBIK.MAUI.Services;assembly=UBIK.MAUI"; 2. We need to search for both "StaticResource" and "DynamicResource"; 3. We only listed some examples here. But the same rule applies to all those templates exacted from UBIKThemes (see the first "Recommended" point).

Xamarin MAUI
{StaticResource UBIKChildItem} {Binding [UBIKChildItem], Source={x:Static services:TemplateService.Instance}}
{StaticResource UBIKDocumentItem} {Binding [UBIKDocumentItem], Source={x:Static services:TemplateService.Instance}}
{StaticResource UBIKMainItem} {Binding [UBIKMainItem], Source={x:Static services:TemplateService.Instance}}

OnPlatform and OnIdiom syntax changes

All code containing the OnPlatform and OnIdiom functionalities needs to be refactored as the syntax changed. It is recommended to use the inline syntax (see example) as a default wherever possible, but there are also other approaches. When using Bindings, Resources or other complex values in OnIdiom or OnPlatform values, it should only be used with the inline syntax. Please rely to the wiki for more details and possibilities (e.g. if the value contains multiple elements): https://wiki.augmensys.com/index.php?title=XAML_Tips#Platform_and_Device-specific_UI_with_OnPlatform_and_OnIdiom

Xamarin MAUI
OnPlatform
<TheControl
       ...
       TheProperty="{OnPlatform Android=False, iOS=True}" />

<TheControl
    ...
    TheProperty="{OnPlatform Android={Binding ...}, iOS={Binding ...}}" />
OnIdiom
<TheControl
       ...
       TheProperty="{OnIdiom Phone=False, Desktop=True}" />

<TheControl
    ...
    TheProperty="{OnIdiom Desktop={Binding ...}, Phone={Binding ...}}" />
WinPhone WinUI
e.g.
<ColumnDefinition.Width>
   <OnIdiom
       ...
       Desktop="Auto"
       Phone="*" />
</ColumnDefinition.Width>
or
<RowDefinition.Height>
   <OnPlatform
       ...
       Android="Auto"
       iOS="*" />
</RowDefinition.Height>
<ColumnDefinition Width="{OnIdiom Desktop='Auto', Phone='*'}" />

<RowDefinition Height="{OnPlatform Android='Auto', iOS='*'}" />

Remove default ControlTemplate

The default ControlTemplate should be removed from the ContentView if the ControlTemplate property of that ContentView is determined by DataTriggers. The same applies to ContentControls. The default ContentTemplate should be removed from the controls:ContentControl if the ContentTemplate property of that ContentControl is determined by DataTriggers. Using only DataTriggers without a default ControlTemplate ensures that content is created only after the trigger evaluation is complete. If a default ControlTemplate is set, it is always loaded immediately, even if another template is applied right after. For more details refer to Conflicting Triggers.

Xamarin MAUI
<ContentView x:Name="containerName" ControlTemplate="{StaticResource YourTemplate}">
    <ContentView.Triggers>
        <DataTrigger
           Binding="{Binding Source={x:Reference containerName}, Path=...}"
           TargetType="ContentView"
           Value="true">
            <Setter Property="ControlTemplate"
                   Value="{StaticResource YourTemplate}" />
        </DataTrigger>
        ...
    </ContentView.Triggers>
</ContentView>
<ContentView x:Name="containerName">
    <ContentView.Triggers>
        <DataTrigger
           Binding="{Binding Source={x:Reference containerName}, Path=...}"
           TargetType="ContentView"
           Value="true">
            <Setter Property="ControlTemplate"
                   Value="{StaticResource YourTemplate}" />
        </DataTrigger>
        ...
    </ContentView.Triggers>
</ContentView>



Remove HeadlessLayout style

This Style has no longer any effect in MAUI and even degrades app performance. We are keeping it in our standard UBIKThemes under the Obsolete section to maintain backward compatibility.

Xamarin MAUI
<Style x:Key="HeadlessLayout" TargetType="Layout">
            <Setter Property="CompressedLayout.IsHeadless" Value="{OnPlatform False, Android=True}" />
</Style>
Delete it from your custom UBIKThemes file

Additional mandatory rule

In the customizings, however, it can and should be removed entirely, as it negatively affects performance, as mentioned before. To remove the style, you can simply use VS Code or any other editor with a search function and look through all custom .xaml files for usages of {StaticResource HeadlessLayout} or {DynamicResource HeadlessLayout}. These references should be removed from the respective elements or controls.

Xamarin MAUI
<ContentView
   Grid.Column="2"
   ControlTemplate="{DynamicResource ChildCountBadge}"
   HorizontalOptions="Start"
   Style="{DynamicResource HeadlessLayout}"
   VerticalOptions="Start" />
<ContentView
   Grid.Column="2"
   ControlTemplate="{DynamicResource ChildCountBadge}"
   HorizontalOptions="Start"
   VerticalOptions="Start" />

Avoid multiple SfPullToRefresh controls

To prevent an issue that causes the app to freeze, avoid placing more than one SfPullToRefresh controls as siblings in a xaml hierarchy. If switching between contents in a SfPullToRefresh control is necessary use separate DataTemplates for the contents and use e.g. a DataTrigger to set one of them as the ControlTemplate of a ContentControl inside the SfPullToRefresh control.

Xamarin MAUI
<pullToRefresh:SfPullToRefresh IsVisible = {Binding ShowContentB, Converter={StaticResource BoolToNotBool}}>
    <pullToRefresh:SfPullToRefresh.PullableContent>
        <controls:SfListViewExt
           x:Name="ContentA"
           .../>
    </pullToRefresh:SfPullToRefresh.PullableContent>
</pullToRefresh:SfPullToRefresh>

<pullToRefresh:SfPullToRefresh  IsVisible = {Binding ShowContentB}>
    <pullToRefresh:SfPullToRefresh.PullableContent>
        <controls:SfListViewExt
           x:Name="ContentB"
           .../>
    </pullToRefresh:SfPullToRefresh.PullableContent>
</pullToRefresh:SfPullToRefresh>
<Grid>
    <Grid.Resources>
        <ResourceDictionary>
            <DataTemplate x:Key="TemplateA">
                <controls:SfListViewExt
                   x:Name="ContentA"
                   .../>
            </DataTemplate>
            <DataTemplate x:Key="TemplateB">
                <controls:SfListViewExt
                   x:Name="ContentB"
                   .../>
            </DataTemplate>
        </ResourceDictionary>
    </Grid.Resources>
   <pullToRefresh:SfPullToRefresh>
       <pullToRefresh:SfPullToRefresh.PullableContent>
           <controls:ContentControl>
               <controls:ContentControl.Triggers>
                   <DataTrigger
                      Binding="{Binding ShowContentB}}"
                      TargetType="controls:ContentControl"
                      Value="false">
                       <Setter Property="ContentTemplate" Value="{DynamicResource TemplateA}" />
                   </DataTrigger>
                   <DataTrigger
                      Binding="{Binding ShowContentB}}"
                      TargetType="controls:ContentControl"
                      Value="true">
                       <Setter Property="ContentTemplate" Value="{DynamicResource TemplateB}" />
                   </DataTrigger>
               </controls:ContentControl.Triggers>
           </controls:ContentControl>
       </pullToRefresh:SfPullToRefresh.PullableContent>
    </pullToRefresh:SfPullToRefresh>
</Grid>

Avoid using SyncFusion ListViews directly in SfPullToRefresh controls

To prevent an issue on some Android devices that can cause some items in a SfListViewExt to be rendered incorrectly or not at all after triggering a PullToRefresh, avoid placing a SfListViewExt directly in a SfPullToRefresh control. Instead, wrap the ListView in a DataTemplate and use it in a ContentControl as a child of the SfPullToRefresh control, as shown in the Xaml example below.

Xamarin MAUI
<pullToRefresh:SfPullToRefresh>
    <pullToRefresh:SfPullToRefresh.PullableContent>
        <controls:SfListViewExt
           .../>
    </pullToRefresh:SfPullToRefresh.PullableContent>
</pullToRefresh:SfPullToRefresh>
<Grid>
    <Grid.Resources>
        <ResourceDictionary>
            <DataTemplate x:Key="ListViewTemplate">
                <controls:SfListViewExt
                   .../>
            </DataTemplate>
        </ResourceDictionary>
    </Grid.Resources>

    <pullToRefresh:SfPullToRefresh>
       <pullToRefresh:SfPullToRefresh.PullableContent>
           <controls:ContentControl ContentTemplate="{DynamicResource ListViewTemplate}" />
       </pullToRefresh:SfPullToRefresh.PullableContent>
    </pullToRefresh:SfPullToRefresh>
</Grid>

Rename SfPullToRefresh properties

Renamed in Maui, however, currently these properties cause an issue and should not be included in customizings. SfPullToRefresh:

Xamarin MAUI
RefreshContentHeight, RefreshContentWidth RefreshViewHeight, RefreshViewWidth

Rename progress colors

Renamed in Maui.

Xamarin MAUI
ProgressBackgroundColor, ProgressStrokeColor ProgressBackground, ProgressColor

Rename Default Font Name

The name of the default font was slightly renamed in the UBIKThemes file in MAUI — the first letter now needs to be capitalized. Otherwise, on iOS, bold or italic styles are not applied correctly, and changing a property in Direct Editing, for example, would result in no visible change.

Xamarin MAUI
<x:String x:Key="TextFontFamily">inter</x:String> <x:String x:Key="TextFontFamily">Inter</x:String>

SfTabView

Namespace Update

Update Syncfusion TabView namespace. Replace the Xamarin namespace with the MAUI equivalent.

Xamarin MAUI
clr-namespace:Syncfusion.XForms.TabView;assembly=Syncfusion.SfTabView.XForms clr-namespace:Syncfusion.Maui.Toolkit.TabView;assembly=Syncfusion.Maui.Toolkit

Remove Obsolete Converters

Remove ItemCountToBoolConverter from Resources. This converter is no longer usable in TabView headers.

Xamarin MAUI
<converters:ItemCountToBoolConverter x:Key="ItemCountToBool" />
[remove]

Add StringFormatConverter

Use StringFormatConverter for combined header strings. Syncfusion MAUI TabView uses a single `Header` property instead of templates. For all tab headers, formatters should be specified to assemble the text. As a best practice, even for simple header strings.

Xamarin MAUI
[template-based header]
<converters:StringFormatConverter x:Key="ChildrenTabFormatter" Parameter1="{resources:Translate ChildrenTitle}" />

In the above example, a previously defined formatter (that must be also added to the resource dictionary) is used to fill in the header attribute value. ⚠️ Avoid using bindings in `Parameter1` and `Parameter2` — use static strings only. For complex cases, use `EvalExpression`.



FontImageSource Styling

Add local style for FontImageSource. Global styles are not supported.

Xamarin MAUI
[global FontImageSource style]
<Style TargetType="FontImageSource">
  <Setter Property="FontFamily" Value="{DynamicResource UBIKSymbols}" />
  <Setter Property="Color" Value="{DynamicResource UBIKLightThemeColor}" />
</Style>

TabView Structure

Use Grid to wrap TabView. MAUI TabView requires a different structure and property set.

Xamarin MAUI
<tabView:SfTabView VisibleHeaderCount="3">
  ...
</tabView:SfTabView>
<Grid>
  <tabView:SfTabView>
    <tabView:SfTabItem Header="string example header">
      <tabView:SfTabItem.ImageSource>
        <FontImageSource Glyph="{x:Static resources:UBIKIcons.XXX}" />
      </tabView:SfTabItem.ImageSource>
      <tabView:SfTabItem.Content>
        <controls:UBIKContentView x:Name="ChildContainer" Converter="{StaticResource ChildAreaTemplateConverter}" />
      </tabView:SfTabItem.Content>
    </tabView:SfTabItem>
  </tabView:SfTabView>
</Grid>

Property Changes

Update renamed or removed properties.

Xamarin MAUI
VisibleHeaderCount [not available yet]
UBIKCenterButtonSettings CenterButtonSettings
SelectionIndicatorSettings IndicatorPlacement, IndicatorStrokeThickness, IndicatorBackground
SelectionColor Use VisualStateManager instead

⚠️ Refer to Syncfusion migration guide for full property list.

OnPlatform + OnIdiom backup

Syntax Changes

Use inline or multi-tag syntax. Single-tag syntax is outdated and may not build.

Xamarin MAUI
<OnPlatform Android="False" iOS="True" />
<TheControl TheProperty="{OnPlatform Android=False, iOS=True}" />
Xamarin MAUI
<OnPlatform x:TypeArguments="TheType">
  <On Platform="Android" Value="False" />
  <On Platform="iOS" Value="True" />
</OnPlatform>
Required for multi-tag syntax

WinPhone to WinUI

Replace WinPhone with WinUI.

Xamarin MAUI
WinPhone WinUI

GridLength and TypeArguments

Avoid x:TypeArguments with GridLength.

Xamarin MAUI
<ColumnDefinition.Width>
  <OnIdiom x:TypeArguments="GridLength" Desktop="Auto" Phone="*" />
</ColumnDefinition.Width>
<ColumnDefinition Width="{OnIdiom Desktop='Auto', Phone='*'}" />

⚠️ Inline syntax is more reliable when using bindings or resources.

Special Characters

Wrap values in quotes if they contain commas or strings.

Xamarin MAUI
Margin={OnIdiom 10,2, Phone=2,0} Margin={OnIdiom '10,2', Phone='2,0'}

UWP to WinUI

Replace UWP with WinUI in all OnPlatform scopes.

Xamarin MAUI
UWP WinUI

⚠️ For more details, refer to the Wiki documentation.