Difference between revisions of "HowTo:Convert Xamarin XAMLs to Maui"
m (→SfPullToRefresh) |
|||
(26 intermediate revisions by 6 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 131: | Line 135: | ||
| <syntaxhighlight lang="xml">VideoPlayer Aspect="AspectFit"</syntaxhighlight> | | <syntaxhighlight lang="xml">VideoPlayer Aspect="AspectFit"</syntaxhighlight> | ||
|} | |} | ||
− | + | ||
'''The UriToVideoSourceConverter is no longer needed because the conversion of the | '''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.''' | VideoSource gets done by the MediaElement itself. It is marked as obsolete and should therefore no longer be used.''' | ||
Line 141: | Line 145: | ||
| <syntaxhighlight lang="xml"><controls:VideoPlayer... Source="{Binding DocumentViewModel.LocalURI}"/></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 | '''The StopTimer property is obsolete and no longer needed because the MediaElement behind does the Stop of the VideoPlayer | ||
by itself.''' | by itself.''' | ||
Line 160: | Line 164: | ||
| <syntaxhighlight lang="xml">Delete this setter</syntaxhighlight> | | <syntaxhighlight lang="xml">Delete this setter</syntaxhighlight> | ||
|} | |} | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
=== Remove TapGestureRecognizer from Main Item === | === 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).''' | '''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).''' | ||
Line 308: | 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 363: | Line 330: | ||
| clr-namespace:Syncfusion.Maui.Core;assembly=Syncfusion.Maui.Core | | 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 === | === Syncfusion control updates === | ||
Line 386: | Line 359: | ||
|} | |} | ||
− | === | + | === Layout name change === |
{| class="wikitable" | {| class="wikitable" | ||
! Xamarin | ! Xamarin | ||
Line 399: | Line 372: | ||
|} | |} | ||
− | === | + | === TemplateService binding for extracted templates === |
− | '''1. We need to add the following namespace | + | '''1. We need to add the following namespace where the changes are applied: |
xmlns:services="clr-namespace:UBIK.MAUI.Services;assembly=UBIK.MAUI"; | xmlns:services="clr-namespace:UBIK.MAUI.Services;assembly=UBIK.MAUI"; | ||
2. We need to search for both "StaticResource" and "DynamicResource"; | 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 | + | 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" | {| class="wikitable" | ||
! Xamarin | ! Xamarin | ||
Line 410: | Line 383: | ||
| {StaticResource UBIKChildItem} | | {StaticResource UBIKChildItem} | ||
| {Binding [UBIKChildItem], Source={x:Static services:TemplateService.Instance}} | | {Binding [UBIKChildItem], Source={x:Static services:TemplateService.Instance}} | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
|- | |- | ||
| {StaticResource UBIKDocumentItem} | | {StaticResource UBIKDocumentItem} | ||
| {Binding [UBIKDocumentItem], Source={x:Static services:TemplateService.Instance}} | | {Binding [UBIKDocumentItem], Source={x:Static services:TemplateService.Instance}} | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
|- | |- | ||
| {StaticResource UBIKMainItem} | | {StaticResource UBIKMainItem} | ||
Line 431: | Line 392: | ||
=== OnPlatform and OnIdiom syntax changes === | === 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. | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | '''All code containing the OnPlatform | + | |
When using Bindings, Resources or other complex values in OnIdiom or OnPlatform values, it should only be used with the inline syntax. | 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''' | 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''' | ||
Line 457: | Line 409: | ||
TheProperty="{OnPlatform Android={Binding ...}, iOS={Binding ...}}" /> | TheProperty="{OnPlatform Android={Binding ...}, iOS={Binding ...}}" /> | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
|- | |- | ||
| OnIdiom | | OnIdiom | ||
Line 477: | Line 421: | ||
TheProperty="{OnIdiom Desktop={Binding ...}, Phone={Binding ...}}" /> | TheProperty="{OnIdiom Desktop={Binding ...}, Phone={Binding ...}}" /> | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
|- | |- | ||
| WinPhone | | WinPhone | ||
| WinUI | | WinUI | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
|- | |- | ||
| <syntaxhighlight lang="xml"> | | <syntaxhighlight lang="xml"> | ||
Line 518: | Line 448: | ||
|} | |} | ||
− | === | + | === 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 [[Xamarin_XAML#Conflicting_Triggers|Conflicting Triggers]]. | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
{| class="wikitable" | {| class="wikitable" | ||
! Xamarin | ! Xamarin | ||
Line 633: | Line 485: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
|} | |} | ||
+ | |||
+ | |||
+ | |||
+ | |||
=== Remove HeadlessLayout style === | === Remove HeadlessLayout style === | ||
Line 671: | Line 527: | ||
|} | |} | ||
− | === Rename | + | |
− | '''Renamed in Maui | + | |
− | + | === Rename progress colors === | |
+ | '''Renamed in Maui.''' | ||
{| class="wikitable" | {| class="wikitable" | ||
! Xamarin | ! Xamarin | ||
! MAUI | ! MAUI | ||
|- | |- | ||
− | | | + | | ProgressBackgroundColor, ProgressStrokeColor |
− | | | + | | ProgressBackground, ProgressColor |
|} | |} | ||
− | === Rename | + | === 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" | {| class="wikitable" | ||
! Xamarin | ! Xamarin | ||
! MAUI | ! MAUI | ||
|- | |- | ||
− | | | + | | <x:String x:Key="TextFontFamily">inter</x:String> |
− | | | + | | <x:String x:Key="TextFontFamily">Inter</x:String> |
|} | |} | ||
Line 721: | Line 578: | ||
'''Use StringFormatConverter for combined header strings.''' | '''Use StringFormatConverter for combined header strings.''' | ||
Syncfusion MAUI TabView uses a single `Header` property instead of templates. | 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. | ||
{| class="wikitable" | {| class="wikitable" | ||
Line 730: | Line 588: | ||
|} | |} | ||
+ | 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`. | ||
Line 799: | 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
- 1 Information
- 1.1 Frame to Border
- 1.2 LayoutOptions with "...AndExpand"
- 1.3 GlyphLabel and Styles
- 1.4 SfTabView Virtualization
- 1.5 Swipe Menu Icons on Android
- 1.6 Color Style Changes
- 1.7 Strikethrough Converter
- 1.8 Button Disabled Trigger
- 1.9 TapGestureRecognizer Replacement
- 1.10 TapGestureRecognizer and Swipe Conflict
- 1.11 Navigation in UBIKChildItem
- 2 Recommended
- 3 Highly Recommended
- 4 Mandatory
- 4.1 Namespace conversion
- 4.2 Syncfusion control updates
- 4.3 Syncfusion control updates
- 4.4 Layout name change
- 4.5 TemplateService binding for extracted templates
- 4.6 OnPlatform and OnIdiom syntax changes
- 4.7 Remove default ControlTemplate
- 4.8 Remove HeadlessLayout style
- 4.9 Additional mandatory rule
- 4.10 Rename progress colors
- 4.11 Rename Default Font Name
- 5 SfTabView
- 6 SfPullToRefresh
- 7 OnPlatform + OnIdiom backup
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.
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] |
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:
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.