diff --git a/.github/workflows/dependabot-bot.yml b/.github/workflows/dependabot-bot.yml index 10ab968cb..d6f561020 100644 --- a/.github/workflows/dependabot-bot.yml +++ b/.github/workflows/dependabot-bot.yml @@ -30,7 +30,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 # - name: Setup .NET # uses: actions/setup-dotnet@4d4a70f4a5b2a5a5329f13be4ac933f2c9206ac0 # with: diff --git a/.github/workflows/markdownlint.yml b/.github/workflows/markdownlint.yml index ae6938e0f..fc3a3a72d 100644 --- a/.github/workflows/markdownlint.yml +++ b/.github/workflows/markdownlint.yml @@ -26,8 +26,8 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - uses: DavidAnson/markdownlint-cli2-action@992badcdf24e3b8eb7e87ff9287fe931bcb00c6e # v20.0.0 + - uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - uses: DavidAnson/markdownlint-cli2-action@30a0e04f1870d58f8d717450cc6134995f993c63 # v21.0.0 with: config: ".markdownlint-cli2.jsonc" globs: "**/*.md" diff --git a/.markdownlint-cli2.jsonc b/.markdownlint-cli2.jsonc index b5bbfadbb..621ba35a4 100644 --- a/.markdownlint-cli2.jsonc +++ b/.markdownlint-cli2.jsonc @@ -59,7 +59,8 @@ "MD050": { "style": "asterisk" }, - "MD051": false + "MD051": false, + "MD060": false }, "ignores": [ ".github/", diff --git a/docs/data-cloud/aspire-integration.md b/docs/data-cloud/aspire-integration.md index b0bfdb4db..6d2f89b97 100644 --- a/docs/data-cloud/aspire-integration.md +++ b/docs/data-cloud/aspire-integration.md @@ -188,7 +188,7 @@ public static class MauiProgram }); // Add service defaults - builder.Services.AddServiceDefaults(); + builder.AddServiceDefaults(); // Configure HTTP client with service discovery builder.Services.AddHttpClient(client => diff --git a/docs/fundamentals/bindable-properties.md b/docs/fundamentals/bindable-properties.md index d340d7249..e3bf14ec0 100644 --- a/docs/fundamentals/bindable-properties.md +++ b/docs/fundamentals/bindable-properties.md @@ -149,7 +149,7 @@ static bool IsValidValue(BindableObject view, object value) } ``` -Validation callbacks are provided with a value, and should return `true` if the value is valid for the property, otherwise `false`. An exception will be raised if a validation callback returns `false`, which you should handle. A typical use of a validation callback method is constraining the values of integers or doubles when the bindable property is set. For example, the `IsValidValue` method checks that the property value is a `double` within the range 0 to 360. +Validation callbacks are provided with a value, and should return `true` if the value is valid for the property, otherwise `false`. A typical use of a validation callback method is constraining the values of integers or doubles when the bindable property is set. For example, the `IsValidValue` method checks that the property value is a `double` within the range 0 to 360. ### Coerce value callbacks diff --git a/docs/fundamentals/data-binding/commanding.md b/docs/fundamentals/data-binding/commanding.md index c4018a622..f862d5fdb 100644 --- a/docs/fundamentals/data-binding/commanding.md +++ b/docs/fundamentals/data-binding/commanding.md @@ -51,12 +51,15 @@ When the user presses the , the , and when the data binding changes in some way, the calls the `CanExecute` method in the object. If `CanExecute` returns `false`, then the disables itself. This indicates that the particular command is currently unavailable or invalid. -The also attaches a handler on the `CanExecuteChanged` event of . The event is raised from within the viewmodel. When that event is raised, the calls `CanExecute` again. The enables itself if `CanExecute` returns `true` and disables itself if `CanExecute` returns `false`. +The also attaches a handler on the `CanExecuteChanged` event of . The event must be raised manually from within the viewmodel whenever conditions change that affect the `CanExecute` result. When that event is raised, the calls `CanExecute` again. The enables itself if `CanExecute` returns `true` and disables itself if `CanExecute` returns `false`. + +> [!IMPORTANT] +> Unlike some UI frameworks (such as WPF), .NET MAUI does not automatically detect when the return value of `CanExecute` might change. You must manually raise the `CanExecuteChanged` event (or call `ChangeCanExecute()` on the `Command` class) whenever any condition changes that would affect the `CanExecute` result. This is typically done when properties that `CanExecute` depends on are modified. > [!NOTE] > You can also use the `IsEnabled` property of instead of the `CanExecute` method, or in conjunction with it. In .NET MAUI 7 and earlier, it was not possible to use the `IsEnabled` property of while using the command interface, as the `CanExecute` method's return value always overwrote the `IsEnabled` property. This is fixed in .NET MAUI 8 and above; the `IsEnabled` property is now usable on command-based s. However, be aware that the `IsEnabled` property and the `CanExecute` method now must *both* return true in order for the to be enabled (and the parent control must be enabled as well). -When your viewmodel defines a property of type , the viewmodel must also contain or reference a class that implements the interface. This class must contain or reference the `Execute` and `CanExecute` methods, and fire the `CanExecuteChanged` event whenever the `CanExecute` method might return a different value. You can use the `Command` or `Command` class included in .NET MAUI to implement the interface. These classes allow you to specify the bodies of the `Execute` and `CanExecute` methods in class constructors. +When your viewmodel defines a property of type , the viewmodel must also contain or reference a class that implements the interface. This class must contain or reference the `Execute` and `CanExecute` methods, and manually fire the `CanExecuteChanged` event whenever the `CanExecute` method might return a different value. You can use the `Command` or `Command` class included in .NET MAUI to implement the interface. These classes allow you to specify the bodies of the `Execute` and `CanExecute` methods in class constructors. > [!TIP] > Use `Command` when you use the `CommandParameter` property to distinguish between multiple views bound to the same property, and the `Command` class when that isn't a requirement. @@ -296,7 +299,7 @@ public class PersonCollectionViewModel : INotifyPropertyChanged When the user clicks the **New** button, the `execute` function passed to the `Command` constructor is executed. This creates a new `PersonViewModel` object, sets a handler on that object's `PropertyChanged` event, sets `IsEditing` to `true`, and calls the `RefreshCanExecutes` method defined after the constructor. -Besides implementing the interface, the `Command` class also defines a method named `ChangeCanExecute`. A viewmodel should call `ChangeCanExecute` for an property whenever anything happens that might change the return value of the `CanExecute` method. A call to `ChangeCanExecute` causes the `Command` class to fire the `CanExecuteChanged` method. The has attached a handler for that event and responds by calling `CanExecute` again, and then enabling itself based on the return value of that method. +Besides implementing the interface, the `Command` class also defines a method named `ChangeCanExecute`. Your viewmodel must call `ChangeCanExecute` for an property whenever anything happens that might change the return value of the `CanExecute` method. A call to `ChangeCanExecute` causes the `Command` class to fire the `CanExecuteChanged` event. The has attached a handler for that event and responds by calling `CanExecute` again, and then enabling itself based on the return value of that method. When the `execute` method of `NewCommand` calls `RefreshCanExecutes`, the `NewCommand` property gets a call to `ChangeCanExecute`, and the calls the `canExecute` method, which now returns `false` because the `IsEditing` property is now `true`. diff --git a/docs/tutorials/notes-app.md b/docs/tutorials/notes-app.md index 0c56bf6b0..2eb568853 100644 --- a/docs/tutorials/notes-app.md +++ b/docs/tutorials/notes-app.md @@ -346,8 +346,7 @@ The _AppShell.xaml_ defines two tabs, one for the `NotesPage` and another for `A x:Class="Notes.AppShell" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" - xmlns:local="clr-namespace:Notes" - Shell.FlyoutBehavior="Disabled"> + xmlns:local="clr-namespace:Notes"> + xmlns:views="clr-namespace:Notes.Views"> control displays a collection of items, and in this case, is bound to the model's `Notes` property. The way each item is presented by the collection view is set through the `CollectionView.ItemsLayout` and `CollectionView.ItemTemplate` properties. diff --git a/docs/tutorials/snippets/notes-app/allnotes/AppShell.xaml b/docs/tutorials/snippets/notes-app/allnotes/AppShell.xaml index 7f0ebaa4b..3367042b5 100644 --- a/docs/tutorials/snippets/notes-app/allnotes/AppShell.xaml +++ b/docs/tutorials/snippets/notes-app/allnotes/AppShell.xaml @@ -3,8 +3,7 @@ x:Class="Notes.AppShell" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" - xmlns:views="clr-namespace:Notes.Views" - Shell.FlyoutBehavior="Disabled"> + xmlns:views="clr-namespace:Notes.Views"> - + diff --git a/docs/tutorials/snippets/notes-app/navigation/AppShell.xaml b/docs/tutorials/snippets/notes-app/navigation/AppShell.xaml index e12b2137f..a606ee33e 100644 --- a/docs/tutorials/snippets/notes-app/navigation/AppShell.xaml +++ b/docs/tutorials/snippets/notes-app/navigation/AppShell.xaml @@ -3,8 +3,7 @@ x:Class="Notes.AppShell" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" - xmlns:views="clr-namespace:Notes.Views" - Shell.FlyoutBehavior="Disabled"> + xmlns:views="clr-namespace:Notes.Views"> + xmlns:local="clr-namespace:Notes"> + xmlns:local="clr-namespace:Notes"> + xmlns:views="clr-namespace:Notes.Views"> - + diff --git a/docs/tutorials/snippets/notes-mvvm/model/AppShell.xaml b/docs/tutorials/snippets/notes-mvvm/model/AppShell.xaml index 7f0ebaa4b..3367042b5 100644 --- a/docs/tutorials/snippets/notes-mvvm/model/AppShell.xaml +++ b/docs/tutorials/snippets/notes-mvvm/model/AppShell.xaml @@ -3,8 +3,7 @@ x:Class="Notes.AppShell" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" - xmlns:views="clr-namespace:Notes.Views" - Shell.FlyoutBehavior="Disabled"> + xmlns:views="clr-namespace:Notes.Views"> - + diff --git a/docs/tutorials/snippets/notes-mvvm/viewmodel-shared/AppShell.xaml b/docs/tutorials/snippets/notes-mvvm/viewmodel-shared/AppShell.xaml index 7f0ebaa4b..3367042b5 100644 --- a/docs/tutorials/snippets/notes-mvvm/viewmodel-shared/AppShell.xaml +++ b/docs/tutorials/snippets/notes-mvvm/viewmodel-shared/AppShell.xaml @@ -3,8 +3,7 @@ x:Class="Notes.AppShell" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" - xmlns:views="clr-namespace:Notes.Views" - Shell.FlyoutBehavior="Disabled"> + xmlns:views="clr-namespace:Notes.Views"> - + diff --git a/docs/user-interface/controls/image.md b/docs/user-interface/controls/image.md index 27ed9171d..27a5dcc1a 100644 --- a/docs/user-interface/controls/image.md +++ b/docs/user-interface/controls/image.md @@ -225,6 +225,9 @@ Image image = new Image The [`FontImage`](xref:Microsoft.Maui.Controls.Xaml.FontImageExtension) markup extension enables you to display a font icon in any view that can display an . It provides the same functionality as the class, but with a more concise representation. +> [!NOTE] +> The `FontImage` markup extension is deprecated in .NET 10 and will be removed in a future release. Use instead. + The [`FontImage`](xref:Microsoft.Maui.Controls.Xaml.FontImageExtension) markup extension is supported by the class, which defines the following properties: - `FontFamily` of type `string`, the font family to which the font icon belongs. diff --git a/docs/user-interface/fonts.md b/docs/user-interface/fonts.md index cc7d9272b..e38d761d5 100644 --- a/docs/user-interface/fonts.md +++ b/docs/user-interface/fonts.md @@ -267,4 +267,8 @@ The following screenshot shows several font icons being displayed: :::image type="content" source="media/fonts/font-image-source.png" alt-text="Screenshot of three font icons."::: +::: moniker range=">=net-maui-8.0 <=net-maui-9.0" + Alternatively, you can display a font icon with the [`FontImage`](xref:Microsoft.Maui.Controls.Xaml.FontImageExtension) markup extension. For more information, see [Load a font icon](~/user-interface/controls/image.md#load-a-font-icon). + +::: moniker-end diff --git a/docs/user-interface/pages/navigationpage.md b/docs/user-interface/pages/navigationpage.md index 99c533518..5810654aa 100644 --- a/docs/user-interface/pages/navigationpage.md +++ b/docs/user-interface/pages/navigationpage.md @@ -1,7 +1,7 @@ --- title: "NavigationPage" description: "The .NET MAUI NavigationPage is used to perform hierarchical navigation through a stack of last-in, first-out (LIFO) pages." -ms.date: 09/30/2024 +ms.date: 11/28/2025 --- # NavigationPage @@ -196,6 +196,185 @@ In this example, the current page is removed from the modal stack, with the new On Android, you can always return to the previous page by pressing the standard *Back* button on the device. If the modal page requires a self-contained task to be completed before leaving the page, the app must disable the *Back* button. This can be accomplished by overriding the `Page.OnBackButtonPressed` method on the modal page. +## Page navigation events + +The class defines `NavigatedTo`, `NavigatingFrom`, and `NavigatedFrom` navigation events that are raised during page navigation. The `NavigatingFrom` event is raised when the current page is about to be navigated away from. The `NavigatedFrom` event is raised after the current page has been navigated away from. The `NavigatedTo` event is raised after navigating to the current page. + +> [!NOTE] +> On iOS and Mac Catalyst, these events can be raised before native animation completes when navigating between pages. + +::: moniker range=">=net-maui-10.0" + +The `NavigatedToEventArgs` class defines the following properties: + +- `PreviousPage`, of type , represents the page that was navigated from. +- `NavigationType`, of type , represents the type of navigation that occurred. + +The `NavigatingFromEventArgs` class defines the following properties: + +- `DestinationPage`, of type , represents the page being navigated to. +- `NavigationType`, of type , represents the type of navigation that is occurring. + +The `NavigatedFromEventArgs` class defines the following properties: + +- `DestinationPage`, of type , represents the page that was navigated to. +- `NavigationType`, of type , represents the type of navigation that occurred. + +The enumeration defines the following members: + +- `Push`, indicates that a page was pushed onto the navigation stack. +- `Pop`, indicates that a page was popped from the navigation stack. +- `PopToRoot`, indicates that all pages except the root page were popped from the navigation stack. +- `Insert`, indicates that a page was inserted into the navigation stack. +- `Remove`, indicates that a page was removed from the navigation stack. +- `Replace`, indicates that a page was replaced in the navigation stack. + +::: moniker-end + +The following example shows how to subscribe to the navigation events: + +::: moniker range="<=net-maui-9.0" + +```csharp +public partial class MainPage : ContentPage +{ + public MainPage() + { + InitializeComponent(); + + NavigatedTo += OnNavigatedTo; + NavigatingFrom += OnNavigatingFrom; + NavigatedFrom += OnNavigatedFrom; + } + + void OnNavigatedTo(object sender, NavigatedToEventArgs args) + { + // Invoked when the page has been navigated to + } + + void OnNavigatingFrom(object sender, NavigatingFromEventArgs args) + { + // Invoked when the page is being navigated away from + } + + void OnNavigatedFrom(object sender, NavigatedFromEventArgs args) + { + // Invoked when the page has been navigated away from + } +} +``` + +::: moniker-end + +::: moniker range=">=net-maui-10.0" + +```csharp +public partial class MainPage : ContentPage +{ + public MainPage() + { + InitializeComponent(); + + NavigatedTo += OnNavigatedTo; + NavigatingFrom += OnNavigatingFrom; + NavigatedFrom += OnNavigatedFrom; + } + + void OnNavigatedTo(object sender, NavigatedToEventArgs args) + { + // Invoked when the page has been navigated to + Page? previousPage = args.PreviousPage; + NavigationType navigationType = args.NavigationType; + } + + void OnNavigatingFrom(object sender, NavigatingFromEventArgs args) + { + // Invoked when the page is being navigated away from + Page? destinationPage = args.DestinationPage; + NavigationType navigationType = args.NavigationType; + } + + void OnNavigatedFrom(object sender, NavigatedFromEventArgs args) + { + // Invoked when the page has been navigated away from + Page? destinationPage = args.DestinationPage; + NavigationType navigationType = args.NavigationType; + } +} +``` + +::: moniker-end + +Rather than subscribing to the events, a -derived class can override the , , and methods: + +::: moniker range="<=net-maui-9.0" + +```csharp +public partial class MainPage : ContentPage +{ + protected override void OnNavigatedTo(NavigatedToEventArgs args) + { + base.OnNavigatedTo(args); + + // Invoked when the page has been navigated to + } + + protected override void OnNavigatingFrom(NavigatingFromEventArgs args) + { + base.OnNavigatingFrom(args); + + // Invoked when the page is being navigated away from + } + + protected override void OnNavigatedFrom(NavigatedFromEventArgs args) + { + base.OnNavigatedFrom(args); + + // Invoked when the page has been navigated away from + } +} +``` + +::: moniker-end + +::: moniker range=">=net-maui-10.0" + +```csharp +public partial class MainPage : ContentPage +{ + protected override void OnNavigatedTo(NavigatedToEventArgs args) + { + base.OnNavigatedTo(args); + + // Invoked when the page has been navigated to + Page? previousPage = args.PreviousPage; + NavigationType navigationType = args.NavigationType; + } + + protected override void OnNavigatingFrom(NavigatingFromEventArgs args) + { + base.OnNavigatingFrom(args); + + // Invoked when the page is being navigated away from + Page? destinationPage = args.DestinationPage; + NavigationType navigationType = args.NavigationType; + } + + protected override void OnNavigatedFrom(NavigatedFromEventArgs args) + { + base.OnNavigatedFrom(args); + + // Invoked when the page has been navigated away from + Page? destinationPage = args.DestinationPage; + NavigationType navigationType = args.NavigationType; + } +} +``` + +::: moniker-end + +These methods can be overridden to perform work immediately after navigation. For example, in the `OnNavigatedTo` method you might populate a collection of items from a database or web service. + ## Pass data during navigation Sometimes it's necessary for a page to pass data to another page during navigation. Two standard techniques for accomplishing this are passing data through a page constructor, and by setting the new page's `BindingContext` to the data. diff --git a/docs/xaml/markup-extensions/consume.md b/docs/xaml/markup-extensions/consume.md index 9481c6bcd..a8e4af41f 100644 --- a/docs/xaml/markup-extensions/consume.md +++ b/docs/xaml/markup-extensions/consume.md @@ -34,7 +34,7 @@ In addition to the markup extensions discussed in this article, the following ma - [`AppThemeBinding`](xref:Microsoft.Maui.Controls.Xaml.AppThemeBindingExtension) - specifies a resource to be consumed based on the current system theme. For more information, see [AppThemeBinding markup extension](~/user-interface/system-theme-changes.md#appthemebinding-markup-extension). - [`Binding`](xref:Microsoft.Maui.Controls.Xaml.BindingExtension) - establishes a link between properties of two objects. For more information, see [Data binding](~/fundamentals/data-binding/index.md). - [`DynamicResource`](xref:Microsoft.Maui.Controls.Xaml.DynamicResourceExtension) - responds to changes in objects in a resource dictionary. For more information, see [Dynamic styles](~/user-interface/styles/xaml.md#dynamic-styles). -- [`FontImage`](xref:Microsoft.Maui.Controls.Xaml.FontImageExtension) - displays a font icon in any view that can display an . For more information, see [Load a font icon](~/user-interface/controls/image.md#load-a-font-icon). +- [`FontImage`](xref:Microsoft.Maui.Controls.Xaml.FontImageExtension) - displays a font icon in any view that can display an . This markup extension is deprecated in .NET 10. For more information, see [Load a font icon](~/user-interface/controls/image.md#load-a-font-icon). - [`OnIdiom`](xref:Microsoft.Maui.Controls.Xaml.OnIdiomExtension) - customizes UI appearance based on the idiom of the device the application is running on. For more information, see [Customize UI appearance based on the device idiom](~/platform-integration/customize-ui-appearance.md#customize-ui-appearance-based-on-the-device-idiom). - [`OnPlatform`](xref:Microsoft.Maui.Controls.Xaml.OnPlatformExtension) - customizes UI appearance on a per-platform basis. For more information, see [Customize UI appearance based on the platform](~/platform-integration/customize-ui-appearance.md#customize-ui-appearance-based-on-the-platform). - [`RelativeSource`](xref:Microsoft.Maui.Controls.Xaml.RelativeSourceExtension) - sets the binding source relative to the position of the binding target. For more information, see [Relative bindings](~/fundamentals/data-binding/relative-bindings.md). diff --git a/docs/xaml/xamlc.md b/docs/xaml/xamlc.md index 75c06505a..35433aa45 100644 --- a/docs/xaml/xamlc.md +++ b/docs/xaml/xamlc.md @@ -6,7 +6,7 @@ ms.date: 11/14/2025 # XAML Processing -.NET Multi-platform App UI (.NET MAUI) XAML can be processed, and inflated into a tree of objects in different ways explained here. As of net10, default inflation is runtime for debug builds, XamlC (XamlCompilation) for Release. We encourage you to try source generation and use it if it works for you. This will become the future in new projects, then in all projects, soon. +.NET Multi-platform App UI (.NET MAUI) XAML can be processed, and inflated into a tree of objects in different ways explained here. As of .NET 10, default inflation is runtime for debug builds, XamlC (XamlCompilation) for Release. We encourage you to try source generation and use it if it works for you. This will become the future in new projects, then in all projects, soon. ## XAML Compilation @@ -21,13 +21,13 @@ XAML compilation is enabled by default in .NET MAUI apps. For apps built using t > [!IMPORTANT] > Compiled bindings can be enabled to improve data binding performance in .NET MAUI applications. For more information, see [Compiled Bindings](~/fundamentals/data-binding/compiled-bindings.md). -## XAML runtime inflation +## XAML Runtime Inflation -XAML can be inflated at Runtime using reflection. It has advantages, like allowing HotReload scenario, shortening build times, allow the report of diagnostics to IDE. +XAML can be inflated at Runtime using reflection. It has advantages, like allowing Hot Reload scenario, shortening build times, allow the report of diagnostics to IDE. However typically this method should be avoided because it is also the slowest and syntax errors are only caught at runtime. -## XAML Sourcegeneration +## XAML Source Generation -Starting with net10, XAML can be transformed into C# code at compilaiton time. It provides the following benefits: +Starting with .NET 10, XAML can be transformed into C# code at compilaiton time. It provides the following benefits: - Consistency: same generated code used in Debug and Release - Speed: inflation times on device are 10000% (100 times) faster in Debug, and 25% faster on Release. The volume of allocation is reduced in the same proportion @@ -35,7 +35,7 @@ Starting with net10, XAML can be transformed into C# code at compilaiton time. I This is the recommended way going further. It will be enabled by default in the future. -## Enable Source Generation, and per file settings +### Enable Source Generation, and per file settings We no longer recommend using `[XamlCompilation]` attribute to enable or disable per file compilation. @@ -45,14 +45,14 @@ You can enable XAML source generation at the project level, by setting the `Maui SourceGen ``` -This will use source generation for both Release and Debug configurations. +This will use source generation for both Release and Debug configurations, for all files. You can revert to the default per file (or use wildcards) or force another inflator ```xml - + ```