diff --git a/docs/data-cloud/database-sqlite.md b/docs/data-cloud/database-sqlite.md index 6c2a00bad..1653e0ec9 100644 --- a/docs/data-cloud/database-sqlite.md +++ b/docs/data-cloud/database-sqlite.md @@ -160,7 +160,7 @@ public class TodoItemDatabase ## Access data -The `TodoItemDatabase` class can be registered as a singleton that can be used throughout the app if you are using dependency injection. For example, you can register your pages and the database access class as services on the `IServiceCollection` object, in **MauiProgram.cs**, with the `AddSingleton` and `AddTransient` methods: +The `TodoItemDatabase` class can be registered as a singleton that can be used throughout the app if you are using dependency injection. For example, you can register your pages and the database access class as services on the object, in **MauiProgram.cs**, with the `AddSingleton` and `AddTransient` methods: ```csharp builder.Services.AddSingleton(); diff --git a/docs/deployment/includes/feature-switches.md b/docs/deployment/includes/feature-switches.md index 707dc398d..ea0195354 100644 --- a/docs/deployment/includes/feature-switches.md +++ b/docs/deployment/includes/feature-switches.md @@ -1,18 +1,19 @@ --- ms.topic: include -ms.date: 10/28/2024 +ms.date: 11/14/2024 --- -.NET MAUI has trimmer directives, known as feature switches, that make it possible to preserve the code for features that aren't trim safe. These trimmer directives can be used when the `$(TrimMode)` build property is set to `full`, as well as for NativeAOT: +.NET MAUI has trimmer directives, known as feature switches, that make it possible to preserve the code for features that aren't trim safe. These trimmer directives can be used when the `$(TrimMode)` build property is set to `full`, as well as for Native AOT: | MSBuild property | Description | | ---------------- | ----------- | -| `MauiEnableVisualAssemblyScanning` | When set to `true`, .NET MAUI will scan assemblies for types implementing `IVisual` and for `[assembly:Visual(...)]` attributes, and will register these types. By default, this build property is set to `false`. | -| `MauiShellSearchResultsRendererDisplayMemberNameSupported` | When set to `false`, the value of `SearchHandler.DisplayMemberName` will be ignored. Instead, you should provide an to define the appearance of results. By default, this build property is set to `true`.| -| `MauiQueryPropertyAttributeSupport` | When set to `false`, `[QueryProperty(...)]` attributes won't be used to set property values when navigating. Instead, you should implement the interface to accept query parameters. By default, this build property is set to `true`. | -| `MauiImplicitCastOperatorsUsageViaReflectionSupport` | When set to `false`, .NET MAUI won't look for implicit conversion operators when converting values from one type to another. This can affect bindings between properties with different types, and setting a property value of a bindable object with a value of a different type. Instead, you should define a for your type and attach it to the type using the attribute. By default, this build property is set to `true`.| +| `MauiEnableVisualAssemblyScanning` | When set to `true`, .NET MAUI will scan assemblies for types implementing `IVisual` and for `[assembly:Visual(...)]` attributes, and will register these types. By default, this build property is set to `false` when full trimming is enabled. | +| `MauiShellSearchResultsRendererDisplayMemberNameSupported` | When set to `false`, the value of `SearchHandler.DisplayMemberName` will be ignored. Instead, you should provide an to define the appearance of results. By default, this build property is set to `false` when full trimming or Native AOT is enabled.| +| `MauiQueryPropertyAttributeSupport` | When set to `false`, `[QueryProperty(...)]` attributes won't be used to set property values when navigating. Instead, you should implement the interface to accept query parameters. By default, this build property is set to `false` when full trimming or Native AOT is enabled. | +| `MauiImplicitCastOperatorsUsageViaReflectionSupport` | When set to `false`, .NET MAUI won't look for implicit conversion operators when converting values from one type to another. This can affect bindings between properties with different types, and setting a property value of a bindable object with a value of a different type. Instead, you should define a for your type and attach it to the type using the attribute. By default, this build property is set to `false` when full trimming or Native AOT is enabled. | | `_MauiBindingInterceptorsSupport` | When set to `false`, .NET MAUI won't intercept any calls to the `SetBinding` methods and won't try to compile them. By default, this build property is set to `true`. | -| `MauiEnableXamlCBindingWithSourceCompilation` | When set to `true`, .NET MAUI will compile all bindings, including those where the `Source` property is used. If you enable this feature ensure that all bindings have the correct `x:DataType` so that they compile, or clear the data type with `x:Data={x:Null}}` if the binding shouldn't be compiled. By default, this build property is only set to `true` when full trimming or Native AOT deployment is enabled. | +| `MauiEnableXamlCBindingWithSourceCompilation` | When set to `true`, .NET MAUI will compile all bindings, including those where the `Source` property is used. If you enable this feature ensure that all bindings have the correct `x:DataType` so that they compile, or clear the data type with `x:Data={x:Null}}` if the binding shouldn't be compiled. By default, this build property is set to `true` when full trimming or Native AOT is enabled. | +| `MauiHybridWebViewSupported` | When set to `false`, the control won't be available. By default, this build property is set to `false` when full trimming or Native AOT is enabled. | These MSBuild properties also have equivalent switches: @@ -22,5 +23,6 @@ These MSBuild properties also have equivalent switches: - The `MauiImplicitCastOperatorsUsageViaReflectionSupport` MSBuild property has an equivalent switch named `Microsoft.Maui.RuntimeFeature.IsImplicitCastOperatorsUsageViaReflectionSupported`. - The `_MauiBindingInterceptorsSupport` MSBuild property has an equivalent switch named `Microsoft.Maui.RuntimeFeature.AreBindingInterceptorsSupported`. - The `MauiEnableXamlCBindingWithSourceCompilation` MSBuild property has an equivalent switch named `Microsoft.Maui.RuntimeFeature.MauiEnableXamlCBindingWithSourceCompilationEnabled`. +- The `MauiHybridWebViewSupported` MSBuild property has an equivalent switch named `Microsoft.Maui.RuntimeFeature.IsHybridWebViewSupported`. The easiest way to consume a feature switch is by putting the corresponding MSBuild property into your app's project file (*.csproj), which causes the related code to be trimmed from the .NET MAUI assemblies. diff --git a/docs/deployment/includes/trimming-incompatibilities.md b/docs/deployment/includes/trimming-incompatibilities.md index 16e4dcf94..0054005a4 100644 --- a/docs/deployment/includes/trimming-incompatibilities.md +++ b/docs/deployment/includes/trimming-incompatibilities.md @@ -1,6 +1,6 @@ --- ms.topic: include -ms.date: 10/23/2024 +ms.date: 11/14/2024 monikerRange: ">=net-maui-9.0" --- @@ -11,3 +11,4 @@ The following .NET MAUI features are incompatible with full trimming and will be - Loading XAML at runtime with the extension method. This XAML can be made trim safe by annotating all types that could be loaded at runtime with the [`DynamicallyAccessedMembers`](xref:System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute) attribute or the [`DynamicDependency`](xref:System.Diagnostics.CodeAnalysis.DynamicDependencyAttribute) attribute. However, this is very error prone and isn't recommended. - Receiving navigation data using the . Instead, you should implement the interface on types that need to accept query parameters. For more information, see [Process navigation data using a single method](~/fundamentals/shell/navigation.md#process-navigation-data-using-a-single-method). - The `SearchHandler.DisplayMemberName` property. Instead, you should provide an to define the appearance of results. For more information, see [Define search results item appearance](~/fundamentals/shell/search.md#define-search-results-item-appearance). +- The control, due to its use of dynamic `System.Text.Json` serialization features. diff --git a/docs/deployment/nativeaot.md b/docs/deployment/nativeaot.md index cc05f58d7..1aee13bda 100644 --- a/docs/deployment/nativeaot.md +++ b/docs/deployment/nativeaot.md @@ -147,7 +147,7 @@ The following table shows the diagnostics support with Native AOT on iOS and Mac | Feature | Fully supported | Partially supported | Not supported | | - | - | - | - | -| [Observability and telemetry](#observability-and-telemetry) | | | Not supported | +| [Observability and telemetry](#observability-and-telemetry) | | Partially supported | | | [Development-time diagnostics](#development-time-diagnostics) | Fully supported | | | | [Native debugging](#native-debugging) | | Partially supported | | | [CPU Profiling](#cpu-profiling) | | Partially supported | | @@ -157,7 +157,7 @@ The following sections provide additional information about this diagnostics sup ### Observability and telemetry -Tracing of .NET MAUI applications on mobile platforms is enabled through [dotnet-dsrouter](/dotnet/core/diagnostics/dotnet-dsrouter) which connects diagnostic tooling with .NET applications running on iOS and Mac Catalyst, over TCP/IP. However, Native AOT is currently not compatible with this scenario as it doesn't support EventPipe/DiagnosticServer components built with the TCP/IP stack. +Tracing of .NET MAUI applications on mobile platforms is enabled through [dotnet-dsrouter](/dotnet/core/diagnostics/dotnet-dsrouter) which connects diagnostic tooling with .NET applications running on iOS and Mac Catalyst, over TCP/IP. However, Native AOT is currently not compatible with this scenario as it doesn't support EventPipe/DiagnosticServer components built with the TCP/IP stack. Observability is still achievable explicitly in the code. ### Development-time diagnostics diff --git a/docs/fundamentals/controltemplate.md b/docs/fundamentals/controltemplate.md index 813a02d5b..6bf44144c 100644 --- a/docs/fundamentals/controltemplate.md +++ b/docs/fundamentals/controltemplate.md @@ -53,19 +53,19 @@ The following XAML example shows a - - + ... ``` -When a is declared as a resource, it must have a key specified with the `x:Key` attribute so that it can be identified in the resource dictionary. In this example, the root element of the `CardViewControlTemplate` is a object. The object uses the [`RelativeSource`](xref:Microsoft.Maui.Controls.Xaml.RelativeSourceExtension) markup extension to set its to the runtime object instance to which the template will be applied, which is known as the *templated parent*. The object uses a combination of controls to define the visual structure of a `CardView` object. The binding expressions of these objects resolve against `CardView` properties, due to inheriting the from the root element. For more information about the [`RelativeSource`](xref:Microsoft.Maui.Controls.Xaml.RelativeSourceExtension) markup extension, see [Relative bindings](~/fundamentals/data-binding/relative-bindings.md). +When a is declared as a resource, it must have a key specified with the `x:Key` attribute so that it can be identified in the resource dictionary. In this example, the root element of the `CardViewControlTemplate` is a object. The object uses the [`RelativeSource`](xref:Microsoft.Maui.Controls.Xaml.RelativeSourceExtension) markup extension to set its to the runtime object instance to which the template will be applied, which is known as the *templated parent*. The object uses a combination of controls to define the visual structure of a `CardView` object. The binding expressions of these objects resolve against `CardView` properties, due to inheriting the from the root element. For more information about the [`RelativeSource`](xref:Microsoft.Maui.Controls.Xaml.RelativeSourceExtension) markup extension, see [Relative bindings](~/fundamentals/data-binding/relative-bindings.md). ## Consume a ControlTemplate @@ -95,7 +95,7 @@ The following example shows the `CardViewControlTemplate` being assigned to the ``` -In this example, the controls in the `CardViewControlTemplate` become part of the visual tree for each `CardView` object. Because the root object for the control template sets its to the templated parent, the and its children resolve their binding expressions against the properties of each `CardView` object. +In this example, the controls in the `CardViewControlTemplate` become part of the visual tree for each `CardView` object. Because the root object for the control template sets its to the templated parent, the and its children resolve their binding expressions against the properties of each `CardView` object. The following screenshot shows the `CardViewControlTemplate` applied to the the `CardView` objects: @@ -132,11 +132,11 @@ The following XAML example shows a - - + ... @@ -197,12 +197,12 @@ For example, the `CardViewUI` custom control defines its user interface using th xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="ControlTemplateDemos.Controls.CardViewUI" x:Name="this"> - - + ``` @@ -384,16 +384,16 @@ In this example, the - - + ``` -In this example, the root element of the is a object. The object uses the [`RelativeSource`](xref:Microsoft.Maui.Controls.Xaml.RelativeSourceExtension) markup extension to set its to the templated parent. The binding expressions of the object and its children resolve against `CardView` properties, due to inheriting the from the root element. The following screenshot shows the page displaying the `People` collection: +In this example, the root element of the is a object. The object uses the [`RelativeSource`](xref:Microsoft.Maui.Controls.Xaml.RelativeSourceExtension) markup extension to set its to the templated parent. The binding expressions of the object and its children resolve against `CardView` properties, due to inheriting the from the root element. The following screenshot shows the page displaying the `People` collection: :::image type="content" source="media/controltemplate/viewmodel-controltemplate.png" alt-text="Screenshot of three templated CardView objects that bind to a viewmodel."::: diff --git a/docs/fundamentals/data-binding/compiled-bindings.md b/docs/fundamentals/data-binding/compiled-bindings.md index afccfa913..d2c60455d 100644 --- a/docs/fundamentals/data-binding/compiled-bindings.md +++ b/docs/fundamentals/data-binding/compiled-bindings.md @@ -1,7 +1,7 @@ --- title: "Compiled bindings" description: "Compiled bindings can be used to improve data binding performance in .NET MAUI applications." -ms.date: 10/29/2024 +ms.date: 11/14/2024 --- # Compiled bindings @@ -120,45 +120,21 @@ Bindings in a are interpreted in the The following example demonstrates correctly setting the `x:DataType` on a : ```xaml - - - ... - - - - - - - - - - - - - - + + + + + + + + + ``` -The `ListView.ItemsSource` property is set to the static `NamedColor.All` property. The `NamedColor` class uses .NET reflection to enumerate all the static public fields in the class, and to store them with their names in a collection that is accessible from the static `All` property. Therefore, the is filled with all of the `NamedColor` instances. For each item in the , the binding context for the item is set to a `NamedColor` object. The and elements in the are bound to `NamedColor` properties. - -The defines the `x:DataType` attribute to be the `NamedColor` type, indicating that any binding expressions in the view hierarchy will be compiled. This can be verified by changing any of the binding expressions to bind to a non-existent `NamedColor` property, which will result in a build error. While this example sets the `x:DataType` attribute to a string literal, it can also be set to a type with the `x:Type` markup extension. For more information about the `x:Type` markup extension, see [x:Type Markup Extension](~/xaml/markup-extensions/consume.md#xtype-markup-extension). - -When the example is first run, the is populated with `NamedColor` instances. When an item in the is selected, the `BoxView.Color` property is set to the color of the selected item in the : - -:::image type="content" source="media/compiled-bindings/compiledcolorlist.png" alt-text="Compiled color list."::: - -Selecting other items in the updates the color of the . +While this example sets the `x:DataType` attribute to a string literal, it can also be set to a type with the `x:Type` markup extension. For more information about the `x:Type` markup extension, see [x:Type Markup Extension](~/xaml/markup-extensions/consume.md#xtype-markup-extension). ::: moniker range=">=net-maui-9.0" @@ -174,8 +150,8 @@ Then, ensure that all your bindings are annotated with the correct `x:DataType` ```xaml - ``` diff --git a/docs/fundamentals/data-binding/media/compiled-bindings/compiledcolorlist.png b/docs/fundamentals/data-binding/media/compiled-bindings/compiledcolorlist.png deleted file mode 100755 index f4f08e5cb..000000000 Binary files a/docs/fundamentals/data-binding/media/compiled-bindings/compiledcolorlist.png and /dev/null differ diff --git a/docs/fundamentals/data-binding/relative-bindings.md b/docs/fundamentals/data-binding/relative-bindings.md index 7d8f6998a..5a0b954d1 100644 --- a/docs/fundamentals/data-binding/relative-bindings.md +++ b/docs/fundamentals/data-binding/relative-bindings.md @@ -126,9 +126,9 @@ The following XAML shows an example of the `TemplatedParent` relative binding mo - ... @@ -139,7 +139,7 @@ The following XAML shows an example of the `TemplatedParent` relative binding mo - + @@ -165,7 +165,7 @@ The following XAML shows an example of the `TemplatedParent` relative binding mo ``` -In this example, the , which is the root element of the , has its `BindingContext` set to the runtime object instance to which the template is applied. Therefore, the and its children resolve their binding expressions against the properties of each `CardView` object: +In this example, the , which is the root element of the , has its `BindingContext` set to the runtime object instance to which the template is applied. Therefore, the and its children resolve their binding expressions against the properties of each `CardView` object: :::image type="content" source="media/relative-bindings/templatedparent-relative-binding.png" alt-text="Screenshot of a TemplatedParent mode relative binding."::: diff --git a/docs/fundamentals/triggers.md b/docs/fundamentals/triggers.md index 319604f92..c6d21d3f0 100644 --- a/docs/fundamentals/triggers.md +++ b/docs/fundamentals/triggers.md @@ -410,18 +410,19 @@ The following XAML example shows a that inc ... - + - + ``` diff --git a/docs/includes/linker-control.md b/docs/includes/linker-control.md index 1bc55a2ab..c008c8801 100644 --- a/docs/includes/linker-control.md +++ b/docs/includes/linker-control.md @@ -102,6 +102,8 @@ If you have a library in your project, or you're a developer of a reusable libra This marks your assembly as "trimmable" and enables trim warnings for that project. Being "trimmable" means your assembly is considered compatible with trimming and should have no trim warnings when the assembly is built. When used in a trimmed app, the assembly's unused members are removed in the final output. +When using Native AOT deployment in .NET 9+, setting the `IsAotCompatible` MSBuild property to `true` also assigns a value of `true` to the `IsTrimmable` property, and enables additional AOT analyzer build properties. For more information about AOT analyzers, see [AOT-compatibility analyzers](/dotnet/core/deploying/native-aot#aot-compatibility-analyzers). For more information about Native AOT deployment for .NET MAUI, see [Native AOT deployment](~/deployment/nativeaot.md). + Setting the `IsTrimmable` MSBuild property to `true` in your project file inserts the [`AssemblyMetadata`](xref:System.Reflection.AssemblyMetadataAttribute) attribute into your assembly: ```csharp diff --git a/docs/ios/capabilities.md b/docs/ios/capabilities.md index c2cf31b82..d1b6dd38a 100644 --- a/docs/ios/capabilities.md +++ b/docs/ios/capabilities.md @@ -86,7 +86,7 @@ A provisioning profile can be created with the following steps: Once you've created a provisioning profile it must be downloaded by Visual Studio, and set as the provisioning profile for your project: -1. In Visual Studio, download the provisioning profile you've just created so that it's available for signing your app. For more information, see [Download provisioning profiles in Visual Studio](~/ios/device-provisioning/manual-provisioning.md#download-provisioning-profiles-in-visual-studio). +1. In Visual Studio, download the provisioning profile you've just created so that it's available for signing your app. For more information, see [Download provisioning profiles](~/ios/device-provisioning/manual-provisioning.md#download-provisioning-profiles). 1. In Visual Studio, enable manual provisioning for your project. For more information, see [Enable manual provisioning](~/ios/device-provisioning/manual-provisioning.md#enable-manual-provisioning). ## Troubleshoot diff --git a/docs/ios/device-provisioning/manual-provisioning.md b/docs/ios/device-provisioning/manual-provisioning.md index c4d3a5fe0..a6db723d9 100644 --- a/docs/ios/device-provisioning/manual-provisioning.md +++ b/docs/ios/device-provisioning/manual-provisioning.md @@ -1,7 +1,7 @@ --- title: "Manual provisioning for .NET MAUI iOS apps" description: "Learn how to use manual provisioning to create development certificates and profiles for .NET MAUI iOS apps." -ms.date: 08/28/2024 +ms.date: 11/25/2024 --- # Manual provisioning for iOS apps @@ -99,7 +99,11 @@ A development provisioning profile can be created with the following steps: 1. In the **Generate a Provisioning Profile** page, you can optionally click the **Download** button to download the provisioning profile. -## Download provisioning profiles in Visual Studio +## Download provisioning profiles + + +# [Visual Studio](#tab/vswin) + After creating a development provisioning profile in your Apple Developer Account, Visual Studio can download it so that it's available for signing your app: @@ -110,6 +114,21 @@ After creating a development provisioning profile in your Apple Developer Accoun The provisioning profiles will be downloaded on Windows, and exported to your Mac build host if the IDE is paired to it. For more information, see [Pair to Mac for iOS development](~/ios/pair-to-mac.md). + +# [Visual Studio Code](#tab/visual-studio-code) + + +After creating a development provisioning profile in your Apple Developer Account, you will need to download it in Xcode so that it's available for signing your app: + +1. Open the **Xcode** app. +2. Select the **Settings...** item in the **Xcode** menu. +3. Select the **@ Accounts** tab. +4. If you haven't already, add your Apple Developer Account. Otherwise, select your account. +5. In the right-hand pane with your account selected, select the appropriate **Team**. +6. Click the **Download Manual Profiles** button. + +--- + ## Enable manual provisioning After manually creating the development provisioning profile, and installing it in Visual Studio, your .NET MAUI app project should be configured to use manual provisioning: diff --git a/docs/ios/includes/distribution-certificate.md b/docs/ios/includes/distribution-certificate.md index 33100e721..c37b44db6 100644 --- a/docs/ios/includes/distribution-certificate.md +++ b/docs/ios/includes/distribution-certificate.md @@ -7,7 +7,7 @@ ms.topic: include A distribution certificate is used to confirm your identity. Before creating a distribution certificate, you should ensure that you've added your Apple Developer Account to Visual Studio. For more information, see [Apple account management](~/ios/apple-account-management.md). -You only need to create a distribution certificate if you don't already one. The distribution certificate must be created using the Apple ID for your Apple Developer Account. +You only need to create a distribution certificate if you don't already have one. The distribution certificate must be created using the Apple ID for your Apple Developer Account. To create a distribution certificate in Visual Studio: diff --git a/docs/ios/wireless-deployment.md b/docs/ios/wireless-deployment.md index 65629989f..c1690210b 100644 --- a/docs/ios/wireless-deployment.md +++ b/docs/ios/wireless-deployment.md @@ -6,7 +6,7 @@ ms.date: 08/27/2024 # Wireless deployment for .NET MAUI iOS apps -Rather than having to use a USB cable to connect an iOS device to your Mac to deploy and debug a .NET Multi-platform App UI (.NET MAUI) app, Visual Studio can deploy .NET MAUI iOS apps to devices wirelessly, and debug them wirelessly. To do this you must pair your iOS device with Xcode on your Mac. Once paired, the device can be selected from the device target list in Visual Studio. +Rather than having to use a USB cable to connect an iOS device to your Mac to deploy and debug a .NET Multi-platform App UI (.NET MAUI) app, Visual Studio can deploy .NET MAUI iOS apps to devices wirelessly, and debug them wirelessly. To do this, you must pair your iOS device with Xcode on your Mac. Once paired, the device can be selected from the device target list in Visual Studio. > [!IMPORTANT] > .NET MAUI iOS apps must have been provisioned before they can be deployed to a device for testing and debugging. For more information, see [Device provisioning for iOS](~/ios/device-provisioning/index.md). diff --git a/docs/macios/xcsync.md b/docs/macios/xcsync.md index 6e1fe1718..408a538c3 100644 --- a/docs/macios/xcsync.md +++ b/docs/macios/xcsync.md @@ -21,6 +21,9 @@ Supported file types include: The tool has two commands: `generate` and `sync`. Use `generate` to create an Xcode project from a .NET project and `sync` to bring changes in the Xcode project back to the .NET project. +> [!NOTE] +> This tool is only available for macOS due to the interactions with Xcode. + # [Visual Studio Code](#tab/visual-studio-code) diff --git a/docs/user-interface/brushes/gradient.md b/docs/user-interface/brushes/gradient.md index 4ccf7f439..7ec6f7886 100644 --- a/docs/user-interface/brushes/gradient.md +++ b/docs/user-interface/brushes/gradient.md @@ -52,6 +52,6 @@ The following XAML example creates a diagonal object and set its `StartPoint` to (0,0) and its `EndPoint` to (1,0). Then, add two or more objects to the `LinearGradientBrush.GradientStops` collection, that specify the colors in the gradient and their positions. -The following XAML example shows a horizontal that's set as the `Background` of a : +The following XAML example shows a horizontal that's set as the `Background` of a : ```xaml - - + + - - + + ``` -In this example, the background of the is painted with a that interpolates from yellow to green horizontally: +In this example, the background of the is painted with a that interpolates from yellow to green horizontally: -:::image type="content" source="media/lineargradient/horizontal.png" alt-text="Screenshot of a Frame painted with a horizontal LinearGradientBrush."::: +:::image type="content" source="media/lineargradient/horizontal.png" alt-text="Screenshot of a Border painted with a horizontal LinearGradientBrush."::: ### Create a vertical linear gradient To create a vertical linear gradient, create a object and set its `StartPoint` to (0,0) and its `EndPoint` to (0,1). Then, add two or more objects to the `LinearGradientBrush.GradientStops` collection, that specify the colors in the gradient and their positions. -The following XAML example shows a vertical that's set as the `Background` of a : +The following XAML example shows a vertical that's set as the `Background` of a : ```xaml - - + + - - + + ``` -In this example, the background of the is painted with a that interpolates from yellow to green vertically: +In this example, the background of the is painted with a that interpolates from yellow to green vertically: -:::image type="content" source="media/lineargradient/vertical.png" alt-text="Screenshot of a Frame painted with a vertical LinearGradientBrush."::: +:::image type="content" source="media/lineargradient/vertical.png" alt-text="Screenshot of a Border painted with a vertical LinearGradientBrush."::: ### Create a diagonal linear gradient To create a diagonal linear gradient, create a object and set its `StartPoint` to (0,0) and its `EndPoint` to (1,1). Then, add two or more objects to the `LinearGradientBrush.GradientStops` collection, that specify the colors in the gradient and their positions. -The following XAML example shows a diagonal that's set as the `Background` of a : +The following XAML example shows a diagonal that's set as the `Background` of a : ```xaml - - + + @@ -109,10 +106,10 @@ The following XAML example shows a diagonal - - + + ``` -In this example, the background of the is painted with a that interpolates from yellow to green diagonally: +In this example, the background of the is painted with a that interpolates from yellow to green diagonally: -:::image type="content" source="media/lineargradient/diagonal.png" alt-text="Screenshot of a Frame painted with a diagonal LinearGradientBrush."::: +:::image type="content" source="media/lineargradient/diagonal.png" alt-text="Screenshot of a Border painted with a diagonal LinearGradientBrush."::: diff --git a/docs/user-interface/brushes/radialgradient.md b/docs/user-interface/brushes/radialgradient.md index 6cbe34981..99ae771b5 100644 --- a/docs/user-interface/brushes/radialgradient.md +++ b/docs/user-interface/brushes/radialgradient.md @@ -28,15 +28,14 @@ A radial gradient brush's gradient stops are positioned along a gradient axis de To create a radial gradient, create a object and set its `Center` and `Radius` properties. Then, add two or more objects to the `RadialGradientBrush.GradientStops` collection, that specify the colors in the gradient and their positions. -The following XAML example shows a that's set as the `Background` of a : +The following XAML example shows a that's set as the `Background` of a : ```xaml - - + + @@ -45,15 +44,15 @@ The following XAML example shows a - - + + ``` -In this example, the background of the is painted with a that interpolates from red to dark blue. The center of the radial gradient is positioned in the center of the : +In this example, the background of the is painted with a that interpolates from red to dark blue. The center of the radial gradient is positioned in the center of the : -:::image type="content" source="media/radialgradient/center.png" alt-text="Screenshot of a Frame painted with a centered RadialGradientBrush."::: +:::image type="content" source="media/radialgradient/center.png" alt-text="Screenshot of a Border painted with a centered RadialGradientBrush."::: -The following XAML example moves the center of the radial gradient to the top-left corner of the : +The following XAML example moves the center of the radial gradient to the top-left corner of the : ```xaml @@ -65,11 +64,11 @@ The following XAML example moves the center of the radial gradient to the top-le ``` -In this example, the background of the is painted with a that interpolates from red to dark blue. The center of the radial gradient is positioned in the top-left of the : +In this example, the background of the is painted with a that interpolates from red to dark blue. The center of the radial gradient is positioned in the top-left of the : -:::image type="content" source="media/radialgradient/top-left.png" alt-text="Screenshot of a Frame painted with a top-left RadialGradientBrush."::: +:::image type="content" source="media/radialgradient/top-left.png" alt-text="Screenshot of a Border painted with a top-left RadialGradientBrush."::: -The following XAML example moves the center of the radial gradient to the bottom-right corner of the : +The following XAML example moves the center of the radial gradient to the bottom-right corner of the : ```xaml @@ -81,6 +80,6 @@ The following XAML example moves the center of the radial gradient to the bottom ``` -In this example, the background of the is painted with a that interpolates from red to dark blue. The center of the radial gradient is positioned in the bottom-right of the : +In this example, the background of the is painted with a that interpolates from red to dark blue. The center of the radial gradient is positioned in the bottom-right of the : -:::image type="content" source="media/radialgradient/bottom-right.png" alt-text="Screenshot of a Frame painted with a bottom-right RadialGradientBrush."::: +:::image type="content" source="media/radialgradient/bottom-right.png" alt-text="Screenshot of a Border painted with a bottom-right RadialGradientBrush."::: diff --git a/docs/user-interface/brushes/solidcolor.md b/docs/user-interface/brushes/solidcolor.md index 970c33152..176538f1a 100644 --- a/docs/user-interface/brushes/solidcolor.md +++ b/docs/user-interface/brushes/solidcolor.md @@ -23,61 +23,58 @@ There are three main techniques for creating a from a value. In XAML, this enables a to be created from a predefined value: ```xaml - + ``` -In this example, the background of the is painted with a dark blue : +In this example, the background of the is painted with a dark blue : -:::image type="content" source="media/solidcolor/predefined-color.png" alt-text="Screenshot of a Frame painted with a predefined color."::: +:::image type="content" source="media/solidcolor/predefined-color.png" alt-text="Screenshot of a Border painted with a predefined color."::: Alternatively, the value can be specified using property tag syntax: ```xaml - - - - - + + + + + ``` -In this example, the background of the is painted with a whose color is specified by setting the `SolidColorBrush.Color` property. +In this example, the background of the is painted with a whose color is specified by setting the `SolidColorBrush.Color` property. ### Use a predefined Brush The class defines a set of commonly used objects. The following example uses one of these predefined objects: ```xaml - + ``` The equivalent C# code is: ```csharp -Frame frame = new Frame +Border border = new Border { Background = Brush.Indigo, - BorderColor = Colors.LightGray, + Stroke = Colors.LightGray, // ... }; ``` -In this example, the background of the is painted with an indigo : +In this example, the background of the is painted with an indigo : -:::image type="content" source="media/solidcolor/predefined-brush.png" alt-text="Screenshot of a Frame painted with a predefined SolidColorBrush."::: +:::image type="content" source="media/solidcolor/predefined-brush.png" alt-text="Screenshot of a Border painted with a predefined SolidColorBrush."::: For a list of predefined objects provided by the class, see [Solid color brushes](#solid-color-brushes). @@ -94,17 +91,16 @@ In addition, a color can be specified as `#aarrggbb` where `aa` specifies the al The following example sets the color value of a using hexadecimal notation: ```xaml - + ``` -In this example, the background of the is painted with a salmon-colored : +In this example, the background of the is painted with a salmon-colored : -:::image type="content" source="media/solidcolor/hex.png" alt-text="Screenshot of a Frame painted with a SolidColorBrush created with hexadecimal notation."::: +:::image type="content" source="media/solidcolor/hex.png" alt-text="Screenshot of a Border painted with a SolidColorBrush created with hexadecimal notation."::: For other ways of describing color, see [Colors](~/user-interface/graphics/colors.md). diff --git a/docs/user-interface/controls/blazorwebview.md b/docs/user-interface/controls/blazorwebview.md index 534460bc2..407cd118c 100644 --- a/docs/user-interface/controls/blazorwebview.md +++ b/docs/user-interface/controls/blazorwebview.md @@ -1,7 +1,7 @@ --- title: "Host a Blazor web app in a .NET MAUI app using BlazorWebView" description: "The .NET MAUI BlazorWebView control enables you to host a Blazor web app in your .NET MAUI app, and integrate the app with device features." -ms.date: 10/01/2024 +ms.date: 11/13/2024 --- # Host a Blazor web app in a .NET MAUI app using BlazorWebView @@ -96,7 +96,7 @@ The process to add a ``` -1. Modify the `CreateMauiApp` method of your `MauiProgram` class to register the control for use in your app. To do this, on the `IServiceCollection` object, call the `AddMauiBlazorWebView` method to add component web view services to the services collection: +1. Modify the `CreateMauiApp` method of your `MauiProgram` class to register the control for use in your app. To do this, on the object, call the `AddMauiBlazorWebView` method to add component web view services to the services collection: ```csharp public static class MauiProgram @@ -123,6 +123,8 @@ The process to add a has a method that can call a specified `Action` asynchronously and pass in the scoped services available in Razor components. This enables code from the native UI to access scoped services such as : @@ -218,20 +220,20 @@ To play inline video in a Blazor hybrid app on iOS, in a fires and forgets the async disposal of the underlying `WebViewManager`. This reduces the potential for disposal deadlocks to occur on Android. -By default, performs async-over-sync disposal, which means that it blocks the thread until the async disposal is complete. However, this can cause deadlocks if the disposal needs to run code on the same thread (because the thread is blocked while waiting). +> [!WARNING] +> This fire-and-forget default behavior means that disposal can return before all objects are disposed, which can cause behavioral changes in your app. The items that are disposed are partially Blazor's own internal types, but also app-defined types such as scoped services used within the portion of your app. -If you encounter hangs on Android with you should enable an switch in the `CreateMauiApp` method in *MauiProgram.cs*: +To opt out of this behavior, you should configure your app to block on dispose via an switch in the `CreateMauiApp` method in your `MauiProgram` class: ```csharp -AppContext.SetSwitch("BlazorWebView.AndroidFireAndForgetAsync", true); +AppContext.SetSwitch("BlazorWebView.AndroidFireAndForgetAsync", false); ``` -This switch enables to fire and forget the async disposal that occurs, and as a result fixes the majority of the disposal deadlocks that occur on Android. - -> [!WARNING] -> Enabling this switch means that disposal can return before all objects are disposed, which can cause behavioral changes in your app. The items that are disposed are partially Blazor's own internal types, but also app-defined types such as scoped services used within the portion of your app. +If your app is configured to block on dispose via this switch, performs async-over-sync disposal, which means that it blocks the thread until the async disposal is complete. However, this can cause deadlocks if the disposal needs to run code on the same thread (because the thread is blocked while waiting). ## Host content using the legacy behavior on iOS and Mac Catalyst diff --git a/docs/user-interface/controls/carouselview/interaction.md b/docs/user-interface/controls/carouselview/interaction.md index c9c83b0d5..3eb22f0de 100644 --- a/docs/user-interface/controls/carouselview/interaction.md +++ b/docs/user-interface/controls/carouselview/interaction.md @@ -316,9 +316,9 @@ The following XAML example shows how to define the `CurrentItem`, `PreviousItem` - + ... - + diff --git a/docs/user-interface/controls/carouselview/layout.md b/docs/user-interface/controls/carouselview/layout.md index 651e5bfe3..4ca89c2d8 100644 --- a/docs/user-interface/controls/carouselview/layout.md +++ b/docs/user-interface/controls/carouselview/layout.md @@ -42,13 +42,13 @@ By default, will display its items h - + - + @@ -110,13 +110,13 @@ This results in a layout that grows horizontally as new items are added. - + - + @@ -216,19 +216,19 @@ This code results in a vertical layout that has a spacing of 20 between items. ## Dynamic resizing of items -Items in a can be dynamically resized at runtime by changing layout related properties of elements within the . For example, the following code example changes the and properties of an object, and the property of its parent : +Items in a can be dynamically resized at runtime by changing layout related properties of elements within the . For example, the following code example changes the and properties of an object, and the property of its parent : ```csharp void OnImageTapped(object sender, EventArgs e) { Image image = sender as Image; image.HeightRequest = image.WidthRequest = image.HeightRequest.Equals(150) ? 200 : 150; - Frame frame = ((Frame)image.Parent.Parent); - frame.HeightRequest = frame.HeightRequest.Equals(300) ? 350 : 300; + Border border = ((Border)image.Parent.Parent); + border.HeightRequest = border.HeightRequest.Equals(360) ? 410 : 360; } ``` -The `OnImageTapped` event handler is executed in response to an object being tapped, and changes the dimensions of the image (and its parent , so that it's more easily viewed: +The `OnImageTapped` event handler is executed in response to an object being tapped, and changes the dimensions of the image (and its parent ), so that it's more easily viewed: :::image type="content" source="media/layout/runtime-resizing.png" alt-text="Screenshot of a CarouselView with dynamic item sizing."::: diff --git a/docs/user-interface/controls/carouselview/populate-data.md b/docs/user-interface/controls/carouselview/populate-data.md index fc0533727..420732f99 100644 --- a/docs/user-interface/controls/carouselview/populate-data.md +++ b/docs/user-interface/controls/carouselview/populate-data.md @@ -55,13 +55,13 @@ The appearance of each item in the c - + - + @@ -114,9 +114,9 @@ carouselView.ItemTemplate = new DataTemplate(() => stackLayout.Add(locationLabel); stackLayout.Add(detailsLabel); - Frame frame = new Frame { ... }; + Border border = new Border { ... }; StackLayout rootStackLayout = new StackLayout(); - rootStackLayout.Add(frame); + rootStackLayout.Add(border); return rootStackLayout; }); @@ -239,13 +239,13 @@ For more information about indicators, see [IndicatorView](~/user-interface/cont - + @@ -269,7 +269,7 @@ For more information about indicators, see [IndicatorView](~/user-interface/cont - + @@ -285,7 +285,7 @@ carouselView.SetBinding(ItemsView.ItemsSourceProperty, "Monkeys"); carouselView.ItemTemplate = new DataTemplate(() => { StackLayout stackLayout = new StackLayout(); - Frame frame = new Frame { ... }; + Border border = new Border { ... }; SwipeView swipeView = new SwipeView(); SwipeItem favoriteSwipeItem = new SwipeItem @@ -311,14 +311,14 @@ carouselView.ItemTemplate = new DataTemplate(() => StackLayout swipeViewStackLayout = new StackLayout { ... }; swipeView.Content = swipeViewStackLayout; - frame.Content = swipeView; - stackLayout.Add(frame); + border.Content = swipeView; + stackLayout.Add(border); return stackLayout; }); ``` -In this example, the content is a that defines the appearance of each item that's surrounded by a in the . The swipe items are used to perform actions on the content, and are revealed when the control is swiped from the bottom and from the top: +In this example, the content is a that defines the appearance of each item that's surrounded by a in the . The swipe items are used to perform actions on the content, and are revealed when the control is swiped from the bottom and from the top: :::image type="content" source="media/populate-data/swipeview-bottom.png" alt-text="Screenshot of a CarouselView bottom context menu item."::: :::image type="content" source="media/populate-data/swipeview-top.png" alt-text="Screenshot of a CarouselView top context menu item."::: diff --git a/docs/user-interface/controls/contentview.md b/docs/user-interface/controls/contentview.md index 4c634a6d3..7dc6baf5e 100644 --- a/docs/user-interface/controls/contentview.md +++ b/docs/user-interface/controls/contentview.md @@ -75,18 +75,18 @@ The custom control UI can be defined in the XAML file for the - ... - - + ``` diff --git a/docs/user-interface/controls/hybridwebview.md b/docs/user-interface/controls/hybridwebview.md index e92bb9547..1b2379e8a 100644 --- a/docs/user-interface/controls/hybridwebview.md +++ b/docs/user-interface/controls/hybridwebview.md @@ -2,7 +2,7 @@ title: HybridWebView description: Learn how to use a HybridWebView to host HTML/JS/CSS content in a WebView, and communicate between that content and .NET. ms.topic: concept-article -ms.date: 10/28/2024 +ms.date: 11/14/2024 monikerRange: ">=net-maui-9.0" #customer intent: As a developer, I want to host HTML/JS/CSS content in a web view so that I can publish the web app as a mobile app. @@ -10,7 +10,7 @@ monikerRange: ">=net-maui-9.0" # HybridWebView - +[![Browse sample.](~/media/code-sample.png) Browse the sample](/samples/dotnet/maui-samples/userinterface-hybridwebview) The .NET Multi-platform App UI (.NET MAUI) enables hosting arbitrary HTML/JS/CSS content in a web view, and enables communication between the code in the web view (JavaScript) and the code that hosts the web view (C#/.NET). For example, if you have an existing React JS app, you could host it in a cross-platform .NET MAUI native app, and build the back-end of the app using C# and .NET. @@ -21,7 +21,7 @@ The .NET Multi-platform App UI (.NET MAUI) defines a event that's raised when a raw message is received. The object that accompanies the event defines a property that contains the message. -Your app's C# code can invoke synchronous and asynchronous JavaScript methods within the with the `InvokeJavaScriptAsync` and `EvaluateJavaScriptAsync` methods. For more information, see [Invoke JavaScript from C#](#invoke-javascript-from-c). +Your app's C# code can invoke synchronous and asynchronous JavaScript methods within the with the and methods. Your app's JavaScript code can also synchronously invoke C# methods. For more information, see [Invoke JavaScript from C#](#invoke-javascript-from-c) and [Invoke C# from JavaScript](#invoke-c-from-javascript). To create a .NET MAUI app with you need: @@ -31,6 +31,9 @@ To create a .NET MAUI app with you The entire app, including the web content, is packaged and runs locally on a device, and can be published to applicable app stores. The web content is hosted within a native web view control and runs within the context of the app. Any part of the app can access external web services, but isn't required to. +> [!IMPORTANT] +> By default, the control won't be available when full trimming or Native AOT is enabled. To change this behavior, see [Trimming feature switches](~/deployment/trimming.md#trimming-feature-switches). + ## Create a .NET MAUI HybridWebView app To create a .NET MAUI app with a : @@ -52,23 +55,89 @@ To create a .NET MAUI app with a : + -

HybridWebView app!

- + Hybrid sample!
- Messages from C#: + +
+
+ + + + +
+
+ Log: +
+
+ Consider checking out this PDF: sample.pdf
@@ -78,7 +147,7 @@ To create a .NET MAUI app with a : ```js window.HybridWebView = { - "Init": function () { + "Init": function Init() { function DispatchHybridWebViewMessage(message) { const event = new CustomEvent("HybridWebViewMessageReceived", { detail: { message: message } }); window.dispatchEvent(event); @@ -106,11 +175,53 @@ To create a .NET MAUI app with a : } }, - "SendRawMessage": function (message) { - window.HybridWebView.__SendMessageInternal('RawMessage', message); + "SendRawMessage": function SendRawMessage(message) { + window.HybridWebView.__SendMessageInternal('__RawMessage', message); + }, + + "InvokeDotNet": async function InvokeDotNetAsync(methodName, paramValues) { + const body = { + MethodName: methodName + }; + + if (typeof paramValues !== 'undefined') { + if (!Array.isArray(paramValues)) { + paramValues = [paramValues]; + } + + for (var i = 0; i < paramValues.length; i++) { + paramValues[i] = JSON.stringify(paramValues[i]); + } + + if (paramValues.length > 0) { + body.ParamValues = paramValues; + } + } + + const message = JSON.stringify(body); + + var requestUrl = `${window.location.origin}/__hwvInvokeDotNet?data=${encodeURIComponent(message)}`; + + const rawResponse = await fetch(requestUrl, { + method: 'GET', + headers: { + 'Accept': 'application/json' + } + }); + const response = await rawResponse.json(); + + if (response) { + if (response.IsJson) { + return JSON.parse(response.Result); + } + + return response.Result; + } + + return null; }, - "__SendMessageInternal": function (type, message) { + "__SendMessageInternal": function __SendMessageInternal(type, message) { const messageToSend = type + '|' + message; @@ -128,7 +239,7 @@ To create a .NET MAUI app with a : } }, - "InvokeMethod": function (taskId, methodName, args) { + "__InvokeJavaScript": function __InvokeJavaScript(taskId, methodName, args) { if (methodName[Symbol.toStringTag] === 'AsyncFunction') { // For async methods, we need to call the method and then trigger the callback when it's done const asyncPromise = methodName(...args); @@ -144,13 +255,13 @@ To create a .NET MAUI app with a : } }, - "__TriggerAsyncCallback": function (taskId, result) { + "__TriggerAsyncCallback": function __TriggerAsyncCallback(taskId, result) { // Make sure the result is a string if (result && typeof (result) !== 'string') { result = JSON.stringify(result); } - window.HybridWebView.__SendMessageInternal('InvokeMethodCompleted', taskId + '|' + result); + window.HybridWebView.__SendMessageInternal('__InvokeJavaScriptCompleted', taskId + '|' + result); } } @@ -175,6 +286,35 @@ To create a .NET MAUI app with a : ``` +1. Modify the `CreateMauiApp` method of your `MauiProgram` class to enable developer tools on the underlying WebView controls when your app is running in debug configuration. To do this, call the method on the object: + + ```csharp + using Microsoft.Extensions.Logging; + + public static class MauiProgram + { + public static MauiApp CreateMauiApp() + { + var builder = MauiApp.CreateBuilder(); + builder + .UseMauiApp() + .ConfigureFonts(fonts => + { + fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); + fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold"); + }); + + #if DEBUG + builder.Services.AddHybridWebViewDeveloperTools(); + builder.Logging.AddDebug(); + #endif + // Register any app services on the IServiceCollection object + + return builder.Build(); + } + } + ``` + 1. Use the APIs to send messages between the JavaScript and C# code: ```csharp @@ -193,14 +333,14 @@ To create a .NET MAUI app with a : ## Invoke JavaScript from C\# -Your app's C# code can synchronously and asynchronously invoke JavaScript methods within the , with optional parameters and an optional return value. This can be achieved with the `InvokeJavaScriptAsync` and `EvaluateJavaScriptAsync` methods: +Your app's C# code can synchronously and asynchronously invoke JavaScript methods within the , with optional parameters and an optional return value. This can be achieved with the and methods: -- The `EvaluateJavaScriptAsync` method runs the JavaScript code provided via a parameter and returns the result as a string. -- The `InvokeJavaScriptAsync` method invokes a specified JavaScript method, optionally passing in parameter values, and specifies a generic argument that indicates the type of the return value. It returns an object of the generic argument type that contains the return value of the called JavaScript method. Internally, parameters and return values are JSON encoded. +- The method runs the JavaScript code provided via a parameter and returns the result as a string. +- The method invokes a specified JavaScript method, optionally passing in parameter values, and specifies a generic argument that indicates the type of the return value. It returns an object of the generic argument type that contains the return value of the called JavaScript method. Internally, parameters and return values are JSON encoded. ### Invoke synchronous JavaScript -Synchronous JavaScript methods can be invoked with the `EvaluateJavaScriptAsync` and `InvokeJavaScriptAsync` methods. In the following example the `InvokeJavaScriptAsync` method is used to demonstrate invoking JavaScript that's embedded in an app's web content. For example, a simple Javascript method to add two numbers could be defined in your web content: +Synchronous JavaScript methods can be invoked with the and methods. In the following example the method is used to demonstrate invoking JavaScript that's embedded in an app's web content. For example, a simple Javascript method to add two numbers could be defined in your web content: ```javascript function AddNumbers(a, b) { @@ -208,7 +348,7 @@ function AddNumbers(a, b) { } ``` -The `AddNumbers` JavaScript method can be invoked from C# with the `InvokeJavaScriptAsync` method: +The `AddNumbers` JavaScript method can be invoked from C# with the method: ```csharp double x = 123d; @@ -238,7 +378,7 @@ internal partial class HybridSampleJsContext : JsonSerializerContext ### Invoke asynchronous JavaScript -Asynchronous JavaScript methods can be invoked with the `EvaluateJavaScriptAsync` and `InvokeJavaScriptAsync` methods. In the following example the `InvokeJavaScriptAsync` method is used to demonstrate invoking JavaScript that's embedded in an app's web content. For example, a Javascript method that asynchronously retrieves data could be defined in your web content: +Asynchronous JavaScript methods can be invoked with the and methods. In the following example the method is used to demonstrate invoking JavaScript that's embedded in an app's web content. For example, a Javascript method that asynchronously retrieves data could be defined in your web content: ```javascript async function EvaluateMeWithParamsAndAsyncReturn(s1, s2) { @@ -253,7 +393,7 @@ async function EvaluateMeWithParamsAndAsyncReturn(s1, s2) { } ``` -The `EvaluateMeWithParamsAndAsyncReturn` JavaScript method can be invoked from C# with the `InvokeJavaScriptAsync` method: +The `EvaluateMeWithParamsAndAsyncReturn` JavaScript method can be invoked from C# with the method: ```csharp Dictionary asyncResult = await hybridWebView.InvokeJavaScriptAsync>( @@ -280,3 +420,84 @@ internal partial class HybridSampleJSContext : JsonSerializerContext > [!IMPORTANT] > The `HybridSampleJsContext` class must be `partial` so that code generation can provide the implementation when the project is compiled. If the type is nested into another type, then that type must also be `partial`. + +## Invoke C\# from JavaScript + +Your app's JavaScript code within the can synchronously invoke C# methods, with optional parameters and an optional return value. This can be achieved by: + +- Defining public C# methods that will be invoked from JavaScript. +- Calling the method to set the object that will be the target of JavaScript calls from the . +- Calling the C# methods from JavaScript. + +> [!IMPORTANT] +> Asynchronously invoking C# methods from JavaScript isn't currently supported. + +The following example defines four public methods for invoking from JavaScript: + +```csharp +public partial class MainPage : ContentPage +{ + ... + + public void DoSyncWork() + { + Debug.WriteLine("DoSyncWork"); + } + + public void DoSyncWorkParams(int i, string s) + { + Debug.WriteLine($"DoSyncWorkParams: {i}, {s}"); + } + + public string DoSyncWorkReturn() + { + Debug.WriteLine("DoSyncWorkReturn"); + return "Hello from C#!"; + } + + public SyncReturn DoSyncWorkParamsReturn(int i, string s) + { + Debug.WriteLine($"DoSyncWorkParamReturn: {i}, {s}"); + return new SyncReturn + { + Message = "Hello from C#!" + s, + Value = i + }; + } + + public class SyncReturn + { + public string? Message { get; set; } + public int Value { get; set; } + } +} +``` + +You must then call the method to set the object that will be the target of JavaScript calls from the : + +```csharp +public partial class MainPage : ContentPage +{ + public MainPage() + { + InitializeComponent(); + hybridWebView.SetInvokeJavaScriptTarget(this); + } + + ... +} +``` + +The public methods on the object set via the method can then be invoked from JavaScript with the `window.HybridWebView.InvokeDotNet` function: + +```js +await window.HybridWebView.InvokeDotNet('DoSyncWork'); +await window.HybridWebView.InvokeDotNet('DoSyncWorkParams', [123, 'hello']); +const retValue = await window.HybridWebView.InvokeDotNet('DoSyncWorkReturn'); +const retValue = await window.HybridWebView.InvokeDotNet('DoSyncWorkParamsReturn', [123, 'hello']); +``` + +The `window.HybridWebView.InvokeDotNet` JavaScript function invokes a specified C# method, with optional parameters and an optional return value. + +> [!NOTE] +> Invoking the `window.HybridWebView.InvokeDotNet` JavaScript function requires your app to include the *HybridWebView.js* JavaScript library listed earlier in this article. diff --git a/docs/user-interface/layouts/stacklayout.md b/docs/user-interface/layouts/stacklayout.md index 7abfaa835..2aec4a12e 100644 --- a/docs/user-interface/layouts/stacklayout.md +++ b/docs/user-interface/layouts/stacklayout.md @@ -286,8 +286,8 @@ The following XAML shows an example of nesting ... - + - - + + - - + + - + ...
``` -In this example, the parent contains nested objects inside objects. The parent is oriented vertically, while the child objects are oriented horizontally: +In this example, the parent contains nested objects inside objects. The parent is oriented vertically, while the child objects are oriented horizontally: :::image type="content" source="media/stacklayout/nested.png" alt-text="Nested .NET MAUI StackLayouts."::: @@ -340,59 +340,59 @@ public class CombinedStackLayoutPage : ContentPage { Title = "Combined StackLayouts demo"; - Frame frame1 = new Frame + Border border1 = new Border { - BorderColor = Colors.Black, + Stroke = Colors.Black, Padding = new Thickness(5) }; - StackLayout frame1StackLayout = new StackLayout + StackLayout border1StackLayout = new StackLayout { Orientation = StackOrientation.Horizontal, Spacing = 15 }; - frame1StackLayout.Add(new BoxView { Color = Colors.Red, WidthRequest = 40 }); - frame1StackLayout.Add(new Label { Text = "Red", FontSize = 22, VerticalOptions = LayoutOptions.Center }); - frame1.Content = frame1StackLayout; + border1StackLayout.Add(new BoxView { Color = Colors.Red, WidthRequest = 40 }); + border1StackLayout.Add(new Label { Text = "Red", FontSize = 20, VerticalOptions = LayoutOptions.Center }); + border1.Content = border1StackLayout; - Frame frame2 = new Frame + Border border2 = new Border { - BorderColor = Colors.Black, + Stroke = Colors.Black, Padding = new Thickness(5) }; - StackLayout frame2StackLayout = new StackLayout + StackLayout border2StackLayout = new StackLayout { Orientation = StackOrientation.Horizontal, Spacing = 15 }; - frame2StackLayout.Add(new BoxView { Color = Colors.Yellow, WidthRequest = 40 }); - frame2StackLayout.Add(new Label { Text = "Yellow", FontSize = 22, VerticalOptions = LayoutOptions.Center }); - frame2.Content = frame2StackLayout; + border2StackLayout.Add(new BoxView { Color = Colors.Yellow, WidthRequest = 40 }); + border2StackLayout.Add(new Label { Text = "Yellow", FontSize = 20, VerticalOptions = LayoutOptions.Center }); + border2.Content = border2StackLayout; - Frame frame3 = new Frame + Border border3 = new Border { - BorderColor = Colors.Black, + Stroke = Colors.Black, Padding = new Thickness(5) }; - StackLayout frame3StackLayout = new StackLayout + StackLayout border3StackLayout = new StackLayout { Orientation = StackOrientation.Horizontal, Spacing = 15 }; - frame3StackLayout.Add(new BoxView { Color = Colors.Blue, WidthRequest = 40 }); - frame3StackLayout.Add(new Label { Text = "Blue", FontSize = 22, VerticalOptions = LayoutOptions.Center }); - frame3.Content = frame3StackLayout; + border3StackLayout.Add(new BoxView { Color = Colors.Blue, WidthRequest = 40 }); + border3StackLayout.Add(new Label { Text = "Blue", FontSize = 20, VerticalOptions = LayoutOptions.Center }); + border3.Content = border3StackLayout; ... StackLayout stackLayout = new StackLayout { Margin = new Thickness(20) }; stackLayout.Add(new Label { Text = "Primary colors" }); - stackLayout.Add(frame1); - stackLayout.Add(frame2); - stackLayout.Add(frame3); + stackLayout.Add(border1); + stackLayout.Add(border2); + stackLayout.Add(border3); stackLayout.Add(new Label { Text = "Secondary colors" }); - stackLayout.Add(frame4); - stackLayout.Add(frame5); - stackLayout.Add(frame6); + stackLayout.Add(border4); + stackLayout.Add(border5); + stackLayout.Add(border6); Content = stackLayout; } diff --git a/docs/user-interface/layouts/verticalstacklayout.md b/docs/user-interface/layouts/verticalstacklayout.md index 2c0f4048c..2e688fb42 100644 --- a/docs/user-interface/layouts/verticalstacklayout.md +++ b/docs/user-interface/layouts/verticalstacklayout.md @@ -154,8 +154,8 @@ The following XAML shows an example of nesting