Skip to content

Commit

Permalink
Obsolete setting App.MainPage (#2602)
Browse files Browse the repository at this point in the history
* MainPage updates.

* Fix xrefs.

* MainPage deprecation.

* Fix warning.

* More App.MainPage deprecation.

* More MainPage deprecation.

* MainPage deprecation.

* Remove hard tabs.
  • Loading branch information
davidbritch authored Nov 6, 2024
1 parent 3c8b2c9 commit 2f5df77
Show file tree
Hide file tree
Showing 24 changed files with 303 additions and 50 deletions.
7 changes: 1 addition & 6 deletions docs/android/app-links.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,12 +246,7 @@ namespace MyNamespace;

public partial class App : Application
{
public App()
{
InitializeComponent();

MainPage = new AppShell();
}
...

protected override async void OnAppLinkRequestReceived(Uri uri)
{
Expand Down
24 changes: 22 additions & 2 deletions docs/data-cloud/push-notifications.md
Original file line number Diff line number Diff line change
Expand Up @@ -956,7 +956,7 @@ To create your .NET MAUI app:
IDeviceInstallationService _deviceInstallationService;

IDeviceInstallationService DeviceInstallationService =>
_deviceInstallationService ?? (_deviceInstallationService = Application.Current.MainPage.Handler.MauiContext.Services.GetService<IDeviceInstallationService>());
_deviceInstallationService ?? (_deviceInstallationService = Application.Current.Windows[0].Page.Handler.MauiContext.Services.GetService<IDeviceInstallationService>());

public NotificationRegistrationService(string baseApiUri, string apiKey)
{
Expand Down Expand Up @@ -1214,6 +1214,8 @@ To create the app's UI:
readonly IPushDemoNotificationActionService _actionService;
```

::: moniker range="=net-maui-8.0"

1. In the `App` constructor, resolve the `IPushDemoNotificationActionService` implementation and assign it to the `_actionService` backing field, and subscribe to the `IPushDemoNotificationActionService.ActionTriggered` event:

```csharp
Expand All @@ -1228,6 +1230,24 @@ To create the app's UI:
}
```

::: moniker-end

::: moniker range=">=net-maui-9.0"

1. In the `App` constructor, resolve the `IPushDemoNotificationActionService` implementation and assign it to the `_actionService` backing field, and subscribe to the `IPushDemoNotificationActionService.ActionTriggered` event:

```csharp
public App(IPushDemoNotificationActionService service)
{
InitializeComponent();

_actionService = service;
_actionService.ActionTriggered += NotificationActionTriggered;
}
```

::: moniker-end

1. In the `App` class, implement the event handler for the `IPushDemoNotificationActionService.ActionTriggered` event:

```csharp
Expand All @@ -1240,7 +1260,7 @@ To create the app's UI:
{
MainThread.BeginInvokeOnMainThread(() =>
{
MainPage?.DisplayAlert("Push notifications demo", $"{action} action received.", "OK")
Windows[0].Page?.DisplayAlert("Push notifications demo", $"{action} action received.", "OK")
.ContinueWith((task) =>
{
if (task.IsFaulted)
Expand Down
37 changes: 37 additions & 0 deletions docs/fundamentals/app-lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ In addition to these events, the `Window` class also has the following overridab

To subscribe to the `Window` lifecycle events, override the `CreateWindow` method in your `App` class to create a `Window` instance on which you can subscribe to events:

::: moniker range="=net-maui-8.0"

```csharp
namespace MyMauiApp
{
Expand All @@ -68,6 +70,35 @@ namespace MyMauiApp
MainPage = new MainPage();
}

protected override Window CreateWindow(IActivationState? activationState)
{
Window window = new Window(new AppShell());

window.Created += (s, e) =>
{
// Custom logic
};

return window;
}
}
}
```

::: moniker-end

::: moniker range=">=net-maui-9.0"

```csharp
namespace MyMauiApp
{
public partial class App : Application
{
public App()
{
InitializeComponent();
}

protected override Window CreateWindow(IActivationState activationState)
{
Window window = base.CreateWindow(activationState);
Expand All @@ -83,6 +114,8 @@ namespace MyMauiApp
}
```

::: moniker-end

Alternatively, to consume the lifecycle overrides, create a class that derives from the `Window` class

```csharp
Expand All @@ -108,9 +141,13 @@ namespace MyMauiApp

The `Window`-derived class can then be consumed by overriding the `CreateWindow` method in your `App` class to return a `MyWindow` instance.

::: moniker range="=net-maui-8.0"

> [!WARNING]
> An `InvalidOperationException` will be thrown if the `App.MainPage` property is set and the `CreateWindow` method creates a `Window` object using the override that accepts a <xref:Microsoft.Maui.Controls.Page> argument.
::: moniker-end

## Platform lifecycle events

.NET MAUI defines delegates that are invoked in response to platform lifecycle events being raised. Handlers can be specified for these delegates, using named methods or anonymous functions, which are executed when the delegate is invoked. This mechanism enables apps to be notified when common platform lifecycle events are raised.
Expand Down
72 changes: 72 additions & 0 deletions docs/fundamentals/dependency-injection.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,8 @@ This approach can be useful if you need to resolve a dependency from an <xref:Mi
> [!WARNING]
> The `Handler` property of your `Element` could be `null`, so be aware that you may need to account for this situation. For more information, see [Handler lifecycle](~/user-interface/handlers/index.md#handler-lifecycle).
::: moniker range="=net-maui-8.0"

In a view-model, the dependency injection container can be explicitly accessed through the [`Handler.MauiContext.Service`](xref:Microsoft.Maui.IMauiContext.Services) property of `Application.Current.MainPage`:

```csharp
Expand All @@ -283,6 +285,28 @@ public class MainPageViewModel
}
```

::: moniker-end

::: moniker range=">=net-maui-9.0"

In a view-model, the dependency injection container can be explicitly accessed through the [`Handler.MauiContext.Service`](xref:Microsoft.Maui.IMauiContext.Services) property of the `Window.Page`:

```csharp
public class MainPageViewModel
{
readonly ILoggingService _loggingService;
readonly ISettingsService _settingsService;

public MainPageViewModel()
{
_loggingService = Application.Current.Windows[0].Page.Handler.MauiContext.Services.GetService<ILoggingService>();
_settingsService = Application.Current.Windows[0].Page.Handler.MauiContext.Services.GetService<ISettingsService>();
}
}
```

::: moniker-end

A drawback of this approach is that the view-model now has a dependency on the <xref:Microsoft.Maui.Controls.Application> type. However, this drawback can be eliminated by passing an <xref:System.IServiceProvider> argument to the view-model constructor. The <xref:System.IServiceProvider> is resolved through automatic dependency resolution without having to register it with the dependency injection container. With this approach a type and its <xref:System.IServiceProvider> dependency can be automatically resolved provided that the type is registered with the dependency injection container. The <xref:System.IServiceProvider> can then be used for explicit dependency resolution:

```csharp
Expand All @@ -303,6 +327,8 @@ In addition, an <xref:System.IServiceProvider> instance can be accessed on each

## Limitations with XAML resources

::: moniker range="=net-maui-8.0"

A common scenario is to register a page with the dependency injection container, and use automatic dependency resolution to inject it into the `App` constructor and set it as the value of the `MainPage` property:

```csharp
Expand All @@ -313,10 +339,35 @@ public App(MyFirstAppPage page)
}
```

::: moniker-end

::: moniker range=">=net-maui-9.0"

A common scenario is to register a page with the dependency injection container, and use automatic dependency resolution to inject it into the `App` constructor and set it as the first page to be displayed in the app:

```csharp
MyFirstAppPage _firstPage;

public App(MyFirstAppPage page)
{
InitializeComponent();
_firstPage = page;
}

protected override Window CreateWindow(IActivationState? activationState)
{
return new Window(_firstPage);
}
```

::: moniker-end

However, in this scenario if `MyFirstAppPage` attempts to access a `StaticResource` that's been declared in XAML in the `App` resource dictionary, a <xref:Microsoft.Maui.Controls.Xaml.XamlParseException> will be thrown with a message similar to `Position {row}:{column}. StaticResource not found for key {key}`. This occurs because the page resolved through constructor injection has been created before the application-level XAML resources have been initialized.

A workaround for this issue is to inject an <xref:System.IServiceProvider> into your `App` class and then use it to resolve the page inside the `App` class:

::: moniker range="=net-maui-8.0"

```csharp
public App(IServiceProvider serviceProvider)
{
Expand All @@ -325,4 +376,25 @@ public App(IServiceProvider serviceProvider)
}
```

::: moniker-end

::: moniker range=">=net-maui-9.0"

```csharp
MyFirstAppPage _firstPage;

public App(IServiceProvider serviceProvider)
{
InitializeComponent();
_firstPage = serviceProvider.GetService<MyFirstAppPage>();
}

protected override Window CreateWindow(IActivationState? activationState)
{
return new Window(_firstPage);
}
```

::: moniker-end

This approach forces the XAML object tree to be created and initialized before the page is resolved.
4 changes: 2 additions & 2 deletions docs/fundamentals/shell/navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The <xref:Microsoft.Maui.Controls.Shell> class defines the following navigation-
- `CurrentItem`, of type `ShellItem`, the currently selected item.
- `CurrentPage`, of type <xref:Microsoft.Maui.Controls.Page>, the currently presented page.
- `CurrentState`, of type `ShellNavigationState`, the current navigation state of the <xref:Microsoft.Maui.Controls.Shell>.
- `Current`, of type <xref:Microsoft.Maui.Controls.Shell>, a type-casted alias for `Application.Current.MainPage`.
- `Current`, of type <xref:Microsoft.Maui.Controls.Shell>, which provides access to the current Shell.

The <xref:Microsoft.Maui.Controls.BackButtonBehavior>, `CurrentItem`, and `CurrentState` properties are backed by <xref:Microsoft.Maui.Controls.BindableProperty> objects, which means that these properties can be targets of data bindings.

Expand Down Expand Up @@ -118,7 +118,7 @@ This example enables contextual page navigation, where navigating to the `detail
## Perform navigation

To perform navigation, a reference to the <xref:Microsoft.Maui.Controls.Shell> subclass must first be obtained. This reference can be obtained by casting the `App.Current.MainPage` property to a <xref:Microsoft.Maui.Controls.Shell> object, or through the `Shell.Current` property. Navigation can then be performed by calling the <xref:Microsoft.Maui.Controls.Shell.GoToAsync%2A> method on the <xref:Microsoft.Maui.Controls.Shell> object. This method navigates to a `ShellNavigationState` and returns a `Task` that will complete once the navigation animation has completed. The `ShellNavigationState` object is constructed by the <xref:Microsoft.Maui.Controls.Shell.GoToAsync%2A> method, from a `string`, or a `Uri`, and it has its `Location` property set to the `string` or `Uri` argument.
To perform navigation, a reference to the <xref:Microsoft.Maui.Controls.Shell> subclass must first be obtained. This reference can be obtained through the `Shell.Current` property. Navigation can then be performed by calling the <xref:Microsoft.Maui.Controls.Shell.GoToAsync%2A> method on the <xref:Microsoft.Maui.Controls.Shell> object. This method navigates to a `ShellNavigationState` and returns a `Task` that will complete once the navigation animation has completed. The `ShellNavigationState` object is constructed by the <xref:Microsoft.Maui.Controls.Shell.GoToAsync%2A> method, from a `string`, or a `Uri`, and it has its `Location` property set to the `string` or `Uri` argument.

> [!IMPORTANT]
> When a route from the Shell visual hierarchy is navigated to, a navigation stack isn't created. However, when a page that's not in the Shell visual hierarchy is navigated to, a navigation stack is created.
Expand Down
26 changes: 21 additions & 5 deletions docs/fundamentals/shell/search.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,28 @@ public class AnimalSearchHandler : SearchHandler
{
base.OnItemSelected(item);

// Let the animation complete
await Task.Delay(1000);
Animal animal = item as Animal;
string navigationTarget = GetNavigationTarget();

ShellNavigationState state = (App.Current.MainPage as Shell).CurrentState;
// The following route works because route names are unique in this app.
await Shell.Current.GoToAsync($"{GetNavigationTarget()}?name={((Animal)item).Name}");
if (navigationTarget.Equals("catdetails") || navigationTarget.Equals("dogdetails"))
{
// Navigate, passing a string
await Shell.Current.GoToAsync($"{navigationTarget}?name={((Animal)item).Name}");
}
else
{
string lowerCasePropertyName = navigationTarget.Replace("details", string.Empty);
// Capitalise the property name
string propertyName = char.ToUpper(lowerCasePropertyName[0]) + lowerCasePropertyName.Substring(1);

var navigationParameters = new Dictionary<string, object>
{
{ propertyName, animal }
};

// Navigate, passing an object
await Shell.Current.GoToAsync($"{navigationTarget}", navigationParameters);
}
}

string GetNavigationTarget()
Expand Down
27 changes: 27 additions & 0 deletions docs/fundamentals/single-project.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@ public static class MauiProgram

The `App` class derives from the `Application` class:

::: moniker range="=net-maui-8.0"

```csharp
namespace MyMauiApp;

Expand All @@ -236,3 +238,28 @@ public class App : Application
```

In the preceding example, the `MainPage` property is set to the `AppShell` object. `AppShell` is a subclassed <xref:Microsoft.Maui.Controls.Shell> class that describes the visual hierarchy of the app. For more information, see [Create a .NET MAUI Shell app](shell/create.md).

::: moniker-end

::: moniker range=">=net-maui-9.0"

```csharp
namespace MyMauiApp;

public class App : Application
{
public App()
{
InitializeComponent();
}

protected override Window CreateWindow(IActivationState? activationState)
{
return new Window(new AppShell());
}
}
```

In the preceding example, a new <xref:Microsoft.Maui.Controls.Window> is created whose initial content is set to the `AppShell` object. `AppShell` is a subclassed <xref:Microsoft.Maui.Controls.Shell> class that describes the visual hierarchy of the app. For more information, see [Create a .NET MAUI Shell app](shell/create.md).

::: moniker-end
30 changes: 30 additions & 0 deletions docs/fundamentals/windows.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ The <xref:Microsoft.Maui.Controls.VisualElement> class has a `Window` property t

## Create a Window

::: moniker range="=net-maui-8.0"

By default, .NET MAUI creates a <xref:Microsoft.Maui.Controls.Window> object when you set the `MainPage` property to a <xref:Microsoft.Maui.Controls.Page> object in your `App` class. However, you can also override the `CreateWindow` method in your `App` class to create a <xref:Microsoft.Maui.Controls.Window> object:

```csharp
Expand Down Expand Up @@ -81,6 +83,34 @@ namespace MyMauiApp

While the <xref:Microsoft.Maui.Controls.Window> class has a default constructor and a constructor that accepts a <xref:Microsoft.Maui.Controls.Page> argument, which represents the root page of the app, you can also call the base `CreateWindow` method to return the .NET MAUI created <xref:Microsoft.Maui.Controls.Window> object.

::: moniker-end

::: moniker range=">=net-maui-9.0"

By default, your .NET MAUI app overrides the `CreateWindow` method in your `App` class to create a <xref:Microsoft.Maui.Controls.Window> object:

```csharp
namespace MyMauiApp
{
public partial class App : Application
{
public App()
{
InitializeComponent();
}

protected override Window CreateWindow(IActivationState? activationState)
{
return new Window(new AppShell());
}
}
}
```

::: moniker-end

The <xref:Microsoft.Maui.Controls.Window> class has a default constructor and a constructor that accepts a <xref:Microsoft.Maui.Controls.Page> argument, which represents the root page of the app.

In addition, you can also create your own <xref:Microsoft.Maui.Controls.Window>-derived object:

```csharp
Expand Down
10 changes: 6 additions & 4 deletions docs/ios/platform-specifics/navigation-bar-translucent.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,18 @@ using Microsoft.Maui.Controls.PlatformConfiguration;
using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;
...

(App.Current.MainPage as Microsoft.Maui.Controls.NavigationPage).BackgroundColor = Colors.Blue;
(App.Current.MainPage as Microsoft.maui.Controls.NavigationPage).On<iOS>().EnableTranslucentNavigationBar();
// Assume the app has a single window
(App.Current.Windows[0].Page as Microsoft.Maui.Controls.NavigationPage).BackgroundColor = Colors.Blue;
(App.Current.Windows[0].Page as Microsoft.maui.Controls.NavigationPage).On<iOS>().EnableTranslucentNavigationBar();
```

The `NavigationPage.On<iOS>` method specifies that this platform-specific will only run on iOS. The `NavigationPage.EnableTranslucentNavigationBar` method, in the `Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific` namespace, is used to make the navigation bar translucent. In addition, the <xref:Microsoft.Maui.Controls.NavigationPage> class in the `Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific` namespace also has a `DisableTranslucentNavigationBar` method that restores the navigation bar to its default state, and a `SetIsNavigationBarTranslucent` method which can be used to toggle the navigation bar transparency by calling the `IsNavigationBarTranslucent` method:

```csharp
(App.Current.MainPage as Microsoft.Maui.Controls.NavigationPage)
// Assume the app has a single window
(App.Current.Windows[0].Page as Microsoft.Maui.Controls.NavigationPage)
.On<iOS>()
.SetIsNavigationBarTranslucent(!(App.Current.MainPage as Microsoft.Maui.Controls.NavigationPage).On<iOS>().IsNavigationBarTranslucent());
.SetIsNavigationBarTranslucent(!(App.Current.Windows[0].Page as Microsoft.Maui.Controls.NavigationPage).On<iOS>().IsNavigationBarTranslucent());
```

The result is that the transparency of the navigation bar can be changed:
Expand Down
Loading

0 comments on commit 2f5df77

Please sign in to comment.