Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Binding issues with MAUI embedded in iOS when Page uses ControlTemplate #20016

Open
nau-dwb opened this issue Jan 19, 2024 · 6 comments
Open
Assignees
Labels
area-controls-general General issues that span multiple controls, or common base classes such as View or Element migration-compatibility Xamarin.Forms to .NET MAUI Migration, Upgrade Assistant, Try-Convert partner/cat 😻 this is an issue that impacts one of our partners or a customer our advisory team is engaged with platform/iOS 🍎 s/needs-attention Issue has more information and needs another look t/bug Something isn't working t/native-embedding
Milestone

Comments

@nau-dwb
Copy link

nau-dwb commented Jan 19, 2024

Description

This is a truly odd bug and took me awhile to figure out exactly how to reproduce it. I have not seen this behavior in Android, only iOS. We're migrating from a Xamarin Android and Xamarin iOS with embedded Xamarin Forms app to .NET Android and .NET iOS with embedded MAUI and this is but one of a number of things that no longer works.

A little background. As we're using MAUI embedded, we render the pages to native iOS controllers or Android Fragments and then navigate to them through the native navigation stack. Due to this, we hide the native navigation bar and create our own navigation bar layout that we include on the page.

To reduce duplicated XAML code and make development quicker, we created a navigation bar control backed by a view model that controls how the custom navigation bar looks and operates (title text, whether to show Back or Action buttons, commands to fire when buttons pressed, etc.).

To make it easy to include this navigation bar control in every Page, we had created a ControlTemplate similar to

<ResourceDictionary x:Class="OurNamespace.Resources.Templates.NavigationBarPageTemplate"
					xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
					xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
					xmlns:control="clr-namespace:OurNamespace.Controls"
					xmlns:style="clr-namespace:OurNamespace.Resources.Styles">

	<ResourceDictionary.MergedDictionaries>
		<style:NavigationBarStyleDictionary />
	</ResourceDictionary.MergedDictionaries>

	<!--#region Page Control Templates-->

	<!-- Template for configuring a page that contains a custom navigation bar followed by content. -->
	<ControlTemplate x:Key="NavigationBarAndContentPageTemplate">

		<Grid RowDefinitions="Auto,*"
			  ColumnSpacing="0"
			  HorizontalOptions="FillAndExpand"
			  RowSpacing="0"
			  VerticalOptions="FillAndExpand">

			<control:NavigationBarView Grid.Row="0" ConfigurationSource="{TemplateBinding BindingContext.NavigationBar}" />

			<ContentPresenter Grid.Row="1" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
		</Grid>

	</ControlTemplate>

	<!--#endregion Page Control Templates-->

</ResourceDictionary>

Then, in our Resource Dictionary containing common styles, we create an implicit style for ContentPage to use this template like so:

<ResourceDictionary
	x:Class="OurNamespace.Resources.Styles.CoreStyleDictionary"
	xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
	xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
	xmlns:templates="clr-namespace:OurNamespace.Resources.Templates">

	<ResourceDictionary.MergedDictionaries>
		<templates:NavigationBarPageTemplate />
	</ResourceDictionary.MergedDictionaries>

	<!-- Implicit Content Page Style -->
	<Style ApplyToDerivedTypes="True" TargetType="ContentPage">
		<Setter Property="BackgroundColor" Value="{DynamicResource ThemeBackgroundColor}" />
		<Setter Property="ControlTemplate" Value="{StaticResource NavigationBarAndContentPageTemplate}" />
	</Style>

The end result is each page displays the custom navigation bar and the XAML content of each page displays in place of the ContentPresenter in the template.

This all worked great in Xamarin Forms. In MAUI, I started to notice a weird issue where sometimes, none of the bindings on a page seemed to work. If I navigated backwards, and the navigated to the applicable page again, everything displayed correctly and I never would see the broken binding behavior again. I finally tracked down how to repro this.

So take the navigation pattern of Native iOS Controller -> Maui Page One -> Maui Page Two. Now, presume that before navigating to Maui Page One an alert was displayed communicating we were loading some data before navigating. In this scenario, the bindings on MAUI Page One work OK. When then navigating to MAUI Page Two from MAUI Page One, none of the bindings will work on MAUI Page Two - no data is displayed, no commands work on tap, etc. If we navigate back to Maui Page One and then to Maui Page Two, then all the bindings work fine and we see the data on the page we would expect. I should note the navigation bar was fine on MAUI Page Two, just the expected content of the actual page failed to display.

This ONLY happens on that first navigation to MAUI Page Two from MAUI Page One and ONLY if some form of Modal Dialog controller was displayed first on the Native iOS controller prior to navigating to MAUI Page One.

Steps to Reproduce

Please note I set this up so you can see it repros the failure when:

  • Presenting an alert dialog before navigating
  • Presenting an action sheet before navigating
  • Presenting the rendered MAUI pages in a modal window.

The code is also set up to demonstrate the failure does not repro when:

  • Doing a straight navigation without any kind of alert or action sheet displayed first
  • Presenting the rendered MAUI pages full screen.

Using the provided code in the repo:

  1. In the provided repo, open and run the solution under simple-maui-binding-issue
  2. In the ButtonEventDemoViewController class, assign one of the constants for a failing path to the variable demoPathToRun:
const int DemoFailWithLoadingDialogPreNavigation = 1;
    
    const int DemoFailWithPresentingActionSheet = 2;
    
    const int DemoSucessWithSimplePushViewController = 3;
    
    const int DemoFailWithPresentingViewControllerModal = 4;
    
    const int DemoSuccessWithPresentingViewControllerFullScreen = 5;
    
    // Change the option here and run again to explore the different scenarios.
    int demoPathToRun = DemoFailWithLoadingDialogPreNavigation;
    
    switch (demoPathToRun)
  1. Run the app on an iOS simulator (I used an iPad simulator)
  2. Click the "Click Me!" button on the iOS controller to initiate navigation to the first MAUI page. When the first MAUI page loads (SimpleStartPage), tap the label "Tap to go to List Page"
  3. Observe when this second MAUI page loads that there is no list and tapping the "Tap to go back" label does not work either.
  4. Tap the back button in the native navigation bar (I'm not hiding it here in this demo app).
  5. Tap "Tap to go to List Page" again.
  6. Observe how this time the list displays and tapping "Tap to go back" does work OK.

Expected: The bindings on the second page should work and content displays on the first navigation to it.

Actual: The bindings are broken initially on the second page and only work correctly the second time through.

General Steps

  1. Create a ControlTemplate and assign it a key in a resource dictionary
  2. Create an implicit style for ContentPage to use this template
  3. Create two MAUI pages with bindings for data\commands. First MAUI page should have a button\label that when tapped will navigate to the second MAUI page.
  4. Create a native iOS controller with a button. When clicked display an alert dialog or action sheet and then navigate to the first MAUI page.
  5. Observe the first MAUI page renders correctly and bindings are working. Tap the button\label to go to the second MAUI page.
  6. Observe the second MAUI page's bindings do no work - no bound data displayed.
  7. Navigate back to the First MAUI page and tap to go to the second MAUI page again. Observe the bound data now displays as expected on this second navigation.

Link to public reproduction project repository

https://github.com/nau-dwb/maui-embedded-use-issues/tree/main

Version with bug

Unknown/Other

Is this a regression from previous behavior?

Yes, this used to work in Xamarin.Forms

Last version that worked well

Unknown/Other

Affected platforms

iOS, I was not able test on other platforms

Affected platform versions

iOS 14.2+ with .NET 7

Did you find any workaround?

No. As noted, this does not seem to be an issue unless the native IOS controller displayed a modal like an alert first, but we have a pretty common pattern of displaying an alert dialog while we load data or make some service call prior to navigating to an embedded MAUI page.

Another option might be to not use the control template and include the custom control on each page though I have not tried that and it's not really an option for us as that drastically reduces the maintainability of the app and increases duplication.

Relevant log output

No response

@nau-dwb nau-dwb added the t/bug Something isn't working label Jan 19, 2024
@Redth Redth added partner Issue or Request from a partner team t/native-embedding labels Jan 23, 2024
@samhouts samhouts added the migration-compatibility Xamarin.Forms to .NET MAUI Migration, Upgrade Assistant, Try-Convert label Jan 23, 2024
@StephaneDelcroix
Copy link
Contributor

I downloaded your sample, ran it on an iPad simulator, and the list shows up on the first load... could you test again, with an updated version of .net and maui ?

@StephaneDelcroix StephaneDelcroix added the s/needs-info Issue needs more info from the author label Apr 5, 2024
@nau-dwb
Copy link
Author

nau-dwb commented Apr 8, 2024

@StephaneDelcroix - Sorry, I'd posted about some issues getting the maui workloads installed on the Mac but I resolved that.

I did want to confirm whether you updated the sample to .NET8 or just ran it with the latest .NET installed on the machine.

@dotnet-policy-service dotnet-policy-service bot added s/needs-attention Issue has more information and needs another look and removed s/needs-info Issue needs more info from the author labels Apr 8, 2024
@nau-dwb
Copy link
Author

nau-dwb commented Apr 8, 2024

@StephaneDelcroix I was finally able to update to the latest version of the .NET 8 SDK and install the workloads. I ran into the issue trying to pair from Windows to Mac noted here but the workaround of copying the workload sub folder finally got past that. Why is it so incredibly painful to try to be productive with this?

Anyway, after all that I'm still able to repo my issue. Again, the actual project is still targeting net7.0-ios - please let me know if you changed that to target .NET8 but from your post I interpreted it as you downloaded and ran it without changes.

I have a screen shot of the versions installed on Mac and Windows below:

Additional Info:

  • xCode 15.0.1
  • Sonoma 14.1.2
  • Windows 11
  • VS 17.9.2

image

@nau-dwb
Copy link
Author

nau-dwb commented Apr 25, 2024

@StephaneDelcroix - I changed the sample project to target net8.0-ios and can confirm I still experience the issue. The first time I go to the list page, I see no items. Tap back, navigate again, and the three entry items display.

Since I last commented I had to update to xCode 15.1and when I list SDKs on Windows it's at 8.0.204 instead of .203 but otherwise everything else in my prior screen shot is accurate for versions.

@PureWeen PureWeen added partner/cat 😻 this is an issue that impacts one of our partners or a customer our advisory team is engaged with and removed partner Issue or Request from a partner team labels May 1, 2024
@Redth Redth added this to the Backlog milestone May 6, 2024
@Eilon Eilon added the area-controls-general General issues that span multiple controls, or common base classes such as View or Element label May 10, 2024
@nau-dwb
Copy link
Author

nau-dwb commented May 15, 2024

@StephaneDelcroix - Following up from my last post where I'm still able to repro on .NET8 and the listed version of the workload. Is anyone else able to repro?

@PureWeen
Copy link
Member

PureWeen commented Jun 7, 2024

The main issue here is that we aren't assigning a Window to the MAUI controls.

We have a workaround for this in our samples
https://github.com/dotnet/maui-samples/blob/main/8.0/PlatformIntegration/NativeEmbeddingDemo/NativeEmbeddingDemo.Library/EmbeddedWindowHandler.cs

That will fix this scenario, we are hoping to have this resolved in net9

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-controls-general General issues that span multiple controls, or common base classes such as View or Element migration-compatibility Xamarin.Forms to .NET MAUI Migration, Upgrade Assistant, Try-Convert partner/cat 😻 this is an issue that impacts one of our partners or a customer our advisory team is engaged with platform/iOS 🍎 s/needs-attention Issue has more information and needs another look t/bug Something isn't working t/native-embedding
Projects
Status: Todo
Development

No branches or pull requests

6 participants