Last modified on 16 September 2025, at 12:59

Difference between revisions of "HowTo:Convert Xamarin XAMLs to Maui"

(Avoid multiple SfPullToRefresh controls: replace the default ControlTemplate with a second DataTrigger in the xaml example)
m (SfPullToRefresh)
 
(14 intermediate revisions by 3 users not shown)
Line 69: Line 69:
 
=== TapGestureRecognizer and Swipe Conflict ===   
 
=== TapGestureRecognizer and Swipe Conflict ===   
 
'''Remove TapGestureRecognizer from root Grid.'''   
 
'''Remove TapGestureRecognizer from root Grid.'''   
TapGestureRecognizer blocks swipe behavior. Remove it and adapt parent list to trigger navigation.
+
TapGestureRecognizer blocks swipe behavior. Remove it and adapt parent list to trigger navigation. An example of such behaviors can be seen in UBIKChildArea.
 +
 
 +
 
 +
 
 +
 
  
 
=== Navigation in UBIKChildItem ===   
 
=== Navigation in UBIKChildItem ===   
Line 160: Line 164:
 
| <syntaxhighlight lang="xml">Delete this setter</syntaxhighlight>
 
| <syntaxhighlight lang="xml">Delete this setter</syntaxhighlight>
 
|}
 
|}
=== Avoid Redundant ControlTemplate in DataTriggers ===
+
 
'''This is about finding ControlTemplates that are set in DataTriggers in customized Xamls. When the same template is already set as a ContentView default ControlTemplate, it is recommended to remove the default ControlTemplate on the ContentView. For a detailed description please refer to sheet "Mandatory" in cell 27A-27C.
+
Behavior that might occur if it remains unchanged: Property editor might not open on Android.'''
+
{| class="wikitable"
+
! Xamarin
+
! MAUI
+
|-
+
| <syntaxhighlight lang="xml"><ContentView x:Name="containerName" ControlTemplate="{StaticResource YourTemplate1}">
+
    <ContentView.Triggers>
+
        <DataTrigger
+
            Binding="{Binding Source={x:Reference containerName}, Path=...}"
+
            TargetType="ContentView"
+
            Value="true">
+
            <Setter Property="ControlTemplate"
+
                    Value="{StaticResource YourTemplate1}" />
+
        </DataTrigger>
+
        ...
+
    </ContentView.Triggers>
+
</ContentView></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 YourTemplate1}" />
+
        </DataTrigger>
+
        ...
+
    </ContentView.Triggers>
+
</ContentView></syntaxhighlight>
+
|}
+
  
 
=== Remove TapGestureRecognizer from Main Item ===
 
=== Remove TapGestureRecognizer from Main Item ===
Line 257: Line 230:
 
| BaseResourceKey="SomeStyle"<br/>Style="{DynamicResource SomeStyle}"
 
| BaseResourceKey="SomeStyle"<br/>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.
 +
 +
{| 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.
 +
 +
 +
 +
 +
 
== Mandatory ==
 
== Mandatory ==
  
Line 430: Line 448:
 
|}
 
|}
  
=== Remove default ControlTemplate in DataTriggers ===
+
=== Remove default ControlTemplate ===
'''To prevent an issue on Android where the direct edit Link to Object editor does not open, there is a change necessary in the UBIKPropertyDirectEditListPopup Xaml:
+
'''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.'''
The default ControlTemplate needs to be removed from the ContentView that contains the 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 [[Xamarin_XAML#Conflicting_Triggers|Conflicting Triggers]].
According to our experience, this issue only occurs to our UBIKPropertyDirectEditListPopup standard Xaml, but we can't guarantee that there is no such issue in customized Xamls.
+
So please, if you encounter an issue on Android where a Property Editor doesn't open, report it the mobile development team, and/or try removing the default ControlTemplate from the affected control.
+
 
+
In our standard Xamls, we removed the default ControlTemplate in every ContentView that contains DataTriggers with ControlTemplates where the same template is already set in a DataTrigger. So please, also remove the default ControlTemplates it in the following Xamls if you use customized ones:
+
- UBIKEditDouble
+
- UBIKEditInt
+
- UBIKEditString
+
- UBIKEditMinMax
+
(For the exact syntax please refer to our latest standard Xamls.)
+
 
+
If the above explained is also used in any of your other custom Xamls, please consider changing it there, too. Otherwise, some property editor might not open.'''
+
 
{| class="wikitable"
 
{| class="wikitable"
 
! Xamarin
 
! Xamarin
Line 478: Line 485:
 
</syntaxhighlight>
 
</syntaxhighlight>
 
|}
 
|}
 +
 +
 +
 +
  
 
=== Remove HeadlessLayout style ===
 
=== Remove HeadlessLayout style ===
Line 516: Line 527:
 
|}
 
|}
  
=== Avoid multiple SfPullToRefresh controls ===
 
'''To prevent an issue that causes the app to freeze, avoid placing more then 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 a 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>
 
        <Grid x:Name="ContentA"/>
 
    </pullToRefresh:SfPullToRefresh.PullableContent>
 
</pullToRefresh:SfPullToRefresh>
 
 
<pullToRefresh:SfPullToRefresh  IsVisible = {Binding ShowContentB}>
 
    <pullToRefresh:SfPullToRefresh.PullableContent>
 
        <Grid x:Name="ContentB"/>
 
    </pullToRefresh:SfPullToRefresh.PullableContent>
 
</pullToRefresh:SfPullToRefresh>
 
</syntaxhighlight>
 
| <syntaxhighlight lang="xml">
 
<Grid>
 
    <Grid.Resources>
 
        <ResourceDictionary>
 
            <DataTemplate x:Key="TemplateA">
 
                <Grid x:Name="ContentA"/>
 
            </DataTemplate>
 
            <DataTemplate x:Key="TemplateB">
 
                <Grid 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>
 
</syntaxhighlight>
 
|}
 
 
[[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
 
|}
 
  
 
=== Rename progress colors ===
 
=== Rename progress colors ===
Line 648: Line 590:
 
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.
 
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`.
 
⚠️ Avoid using bindings in `Parameter1` and `Parameter2` — use static strings only. For complex cases, use `EvalExpression`.
 
 
 
 
  
 
=== FontImageSource Styling ===
 
=== FontImageSource Styling ===
Line 720: Line 658:
  
 
⚠️ Refer to [https://help.syncfusion.com/maui/tabview/migration Syncfusion migration guide] for full property list.
 
⚠️ Refer to [https://help.syncfusion.com/maui/tabview/migration Syncfusion migration guide] for full property list.
 +
 +
 +
== SfPullToRefresh ==
 +
 +
'''It is recommended to get rid of all SfPullToRefresh controls due to some issues that might occur.''' For more information refer to [[Mobile_XAML#Issues_with_SfPullToRefresh_in_MAUI|this article]]. In our Maui standard client, the PullToRefresh control has already been replaced by a Refresh button in the Context Menu, so there's no need for a custom Refresh button.
 +
 +
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| <syntaxhighlight lang="xml">
 +
<pullToRefresh:SfPullToRefresh>
 +
    <pullToRefresh:SfPullToRefresh.PullableContent>
 +
        <controls:SfListViewExt
 +
            .../>
 +
    </pullToRefresh:SfPullToRefresh.PullableContent>
 +
</pullToRefresh:SfPullToRefresh>
 +
</syntaxhighlight>
 +
| <syntaxhighlight lang="xml">
 +
<controls:SfListViewExt
 +
    .../>
 +
</syntaxhighlight>
 +
|}
 +
<br>
 +
 +
⚠️ '''If SfPullToRefresh is still required, the following points should be considered.'''
 +
 +
=== Namespace Update ===
 +
'''Update Syncfusion TabView namespace.'''
 +
Replace the Xamarin namespace with the MAUI equivalent.
 +
 +
{| class="wikitable"
 +
! Xamarin
 +
! MAUI
 +
|-
 +
| clr-namespace:Syncfusion.SfPullToRefresh.XForms;assembly=Syncfusion.SfPullToRefresh.XForms
 +
| clr-namespace:Syncfusion.Maui.Toolkit.PullToRefresh;assembly=Syncfusion.Maui.Toolkit
 +
|}
 +
 +
=== 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
 +
|}
 +
 +
=== 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.
 +
 +
{| 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"
 +
            .../>
 +
    </pullToRefresh:SfPullToRefresh.PullableContent>
 +
</pullToRefresh:SfPullToRefresh>
 +
</syntaxhighlight>
 +
| <syntaxhighlight lang="xml">
 +
<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>
 +
</syntaxhighlight>
 +
|}
 +
 +
=== 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.
 +
 +
{| 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>
 +
 +
    <pullToRefresh:SfPullToRefresh>
 +
      <pullToRefresh:SfPullToRefresh.PullableContent>
 +
          <controls:ContentControl ContentTemplate="{DynamicResource ListViewTemplate}" />
 +
      </pullToRefresh:SfPullToRefresh.PullableContent>
 +
    </pullToRefresh:SfPullToRefresh>
 +
</Grid>
 +
</syntaxhighlight>
 +
|}
 +
 +
[[Category:How-To|Convert Xamarin XAMLs to Maui]]
 +
[[Category:MAUI|Convert Xamarin XAMLs to Maui]]
 +
[[Category:XAML|Convert Xamarin XAMLs to Maui]]
  
 
== OnPlatform + OnIdiom backup ==
 
== OnPlatform + OnIdiom backup ==

Latest revision as of 12:59, 16 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. An example of such behaviors can be seen in UBIKChildArea.



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" />


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.


SfPullToRefresh

It is recommended to get rid of all SfPullToRefresh controls due to some issues that might occur. For more information refer to this article. In our Maui standard client, the PullToRefresh control has already been replaced by a Refresh button in the Context Menu, so there's no need for a custom Refresh button.

Xamarin MAUI
<pullToRefresh:SfPullToRefresh>
    <pullToRefresh:SfPullToRefresh.PullableContent>
        <controls:SfListViewExt
           .../>
    </pullToRefresh:SfPullToRefresh.PullableContent>
</pullToRefresh:SfPullToRefresh>
<controls:SfListViewExt
    .../>


⚠️ If SfPullToRefresh is still required, the following points should be considered.

Namespace Update

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

Xamarin MAUI
clr-namespace:Syncfusion.SfPullToRefresh.XForms;assembly=Syncfusion.SfPullToRefresh.XForms clr-namespace:Syncfusion.Maui.Toolkit.PullToRefresh;assembly=Syncfusion.Maui.Toolkit

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

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>

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.