Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
O .NET Multi-platform App UI (.NET MAUI) HybridWebView permite hospedar conteúdo HTML/JS/CSS arbitrário em uma exibição da Web e permite a comunicação entre o código na exibição da Web (JavaScript) e o código que hospeda a exibição da Web (C#/.NET). Por exemplo, se você tiver um aplicativo React JS existente, poderá hospedá-lo em um aplicativo nativo .NET MAUI de plataforma cruzada e criar o back-end do aplicativo usando C# e .NET.
HybridWebView define as seguintes propriedades:
-
DefaultFile, do tipo
string?
, que especifica o arquivo dentro do HybridRoot que deve ser servido como o arquivo padrão. O valor padrão é index.html. -
HybridRoot, do tipo
string?
, que é o caminho dentro dos recursos de ativos não processados da aplicação que contêm o conteúdo da aplicação web. O valor padrão é wwwroot, que mapeia para Resources/Raw/wwwroot.
Além disso, HybridWebView define um evento RawMessageReceived que é gerado quando uma mensagem bruta é recebida. O objeto HybridWebViewRawMessageReceivedEventArgs que acompanha o evento define uma propriedade Message que contém a mensagem.
O código C# do seu aplicativo pode invocar métodos JavaScript síncronos e assíncronos dentro do HybridWebView com os métodos InvokeJavaScriptAsync e EvaluateJavaScriptAsync. O código JavaScript do seu aplicativo também pode invocar métodos C# síncronos e assíncronos. Para obter mais informações, consulte Invoke JavaScript from C# e Invoke C# from JavaScript.
Para criar um aplicativo .NET MAUI com HybridWebView você precisa:
- O conteúdo da Web do aplicativo, que consiste em HTML estático, JavaScript, CSS, imagens e outros arquivos.
- Um controle HybridWebView como parte da interface do usuário do aplicativo. Isso pode ser conseguido fazendo referência a ele no XAML do aplicativo.
- Código no conteúdo da Web e em C#/.NET, que usa as APIs HybridWebView para enviar mensagens entre os dois componentes.
Todo o aplicativo, incluindo o conteúdo da Web, é empacotado e executado localmente em um dispositivo, e pode ser publicado em lojas de aplicativos aplicáveis. O conteúdo da Web é hospedado em um controle de exibição da Web nativo e é executado dentro do contexto do aplicativo. Qualquer parte da aplicação pode aceder a serviços Web externos, mas não é obrigada a fazê-lo.
Importante
Por padrão, o controle HybridWebView não estará disponível quando o corte completo ou o AOT nativo estiver habilitado. Para alterar esse comportamento, consulte Cortar opções de recurso.
Criar um aplicativo .NET MAUI HybridWebView
Para criar um aplicativo .NET MAUI com um HybridWebView:
Abra um projeto de aplicativo .NET MAUI existente ou crie um novo projeto de aplicativo .NET MAUI.
Adicione seu conteúdo da Web ao projeto do aplicativo .NET MAUI.
O conteúdo da Web do seu aplicativo deve ser incluído como parte de um projeto .NET MAUI como ativos brutos. Um ativo bruto é qualquer ficheiro na pasta Resources\Raw da aplicação e inclui subpastas. Para um HybridWebViewpadrão, o conteúdo da web deve ser colocado na pasta Resources\Raw\wwwroot, com o arquivo principal chamado index.html.
Um aplicativo simples pode ter os seguintes arquivos e conteúdo:
Recursos\Raw\wwwroot\index.html com conteúdo para a interface do usuário principal:
<!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta charset="utf-8" /> <title></title> <link rel="icon" href="data:,"> <link rel="stylesheet" href="styles/app.css"> <script src="scripts/HybridWebView.js"></script> <script> function LogMessage(msg) { var messageLog = document.getElementById("messageLog"); messageLog.value += '\r\n' + msg; } window.addEventListener( "HybridWebViewMessageReceived", function (e) { LogMessage("Raw message: " + e.detail.message); }); function AddNumbers(a, b) { var result = { "result": a + b, "operationName": "Addition" }; return result; } var count = 0; async function EvaluateMeWithParamsAndAsyncReturn(s1, s2) { const response = await fetch("/asyncdata.txt"); if (!response.ok) { throw new Error(`HTTP error: ${response.status}`); } var jsonData = await response.json(); jsonData[s1] = s2; const msg = 'JSON data is available: ' + JSON.stringify(jsonData); window.HybridWebView.SendRawMessage(msg) return jsonData; } async function InvokeDoSyncWork() { LogMessage("Invoking DoSyncWork"); await window.HybridWebView.InvokeDotNet('DoSyncWork'); LogMessage("Invoked DoSyncWork"); } async function InvokeDoSyncWorkParams() { LogMessage("Invoking DoSyncWorkParams"); await window.HybridWebView.InvokeDotNet('DoSyncWorkParams', [123, 'hello']); LogMessage("Invoked DoSyncWorkParams"); } async function InvokeDoSyncWorkReturn() { LogMessage("Invoking DoSyncWorkReturn"); const retValue = await window.HybridWebView.InvokeDotNet('DoSyncWorkReturn'); LogMessage("Invoked DoSyncWorkReturn, return value: " + retValue); } async function InvokeDoSyncWorkParamsReturn() { LogMessage("Invoking DoSyncWorkParamsReturn"); const retValue = await window.HybridWebView.InvokeDotNet('DoSyncWorkParamsReturn', [123, 'hello']); LogMessage("Invoked DoSyncWorkParamsReturn, return value: message=" + retValue.Message + ", value=" + retValue.Value); } async function InvokeDoAsyncWork() { LogMessage("Invoking DoAsyncWork"); await window.HybridWebView.InvokeDotNet('DoAsyncWork'); LogMessage("Invoked DoAsyncWork"); } async function InvokeDoAsyncWorkParams() { LogMessage("Invoking DoAsyncWorkParams"); await window.HybridWebView.InvokeDotNet('DoAsyncWorkParams', [123, 'hello']); LogMessage("Invoked DoAsyncWorkParams"); } async function InvokeDoAsyncWorkReturn() { LogMessage("Invoking DoAsyncWorkReturn"); const retValue = await window.HybridWebView.InvokeDotNet('DoAsyncWorkReturn'); LogMessage("Invoked DoAsyncWorkReturn, return value: " + retValue); } async function InvokeDoAsyncWorkParamsReturn() { LogMessage("Invoking DoAsyncWorkParamsReturn"); const retValue = await window.HybridWebView.InvokeDotNet('DoAsyncWorkParamsReturn', [123, 'hello']); LogMessage("Invoked DoAsyncWorkParamsReturn, return value: message=" + retValue.Message + ", value=" + retValue.Value); } </script> </head> <body> <div> Hybrid sample! </div> <div> <button onclick="window.HybridWebView.SendRawMessage('Message from JS! ' + (count++))">Send message to C#</button> </div> <div> <button onclick="InvokeDoSyncWork()">Call C# sync method (no params)</button> <button onclick="InvokeDoSyncWorkParams()">Call C# sync method (params)</button> <button onclick="InvokeDoSyncWorkReturn()">Call C# method (no params) and get simple return value</button> <button onclick="InvokeDoSyncWorkParamsReturn()">Call C# method (params) and get complex return value</button> </div> <div> <button onclick="InvokeDoAsyncWork()">Call C# async method (no params)</button> <button onclick="InvokeDoAsyncWorkParams()">Call C# async method (params)</button> <button onclick="InvokeDoAsyncWorkReturn()">Call C# async method (no params) and get simple return value</button> <button onclick="InvokeDoAsyncWorkParamsReturn()">Call C# async method (params) and get complex return value</button> </div> <div> Log: <textarea readonly id="messageLog" style="width: 80%; height: 10em;"></textarea> </div> <div> Consider checking out this PDF: <a href="docs/sample.pdf">sample.pdf</a> </div> </body> </html>
Recursos\Raw\wwwroot\scripts\HybridWebView.js com a biblioteca JavaScript padrão HybridWebView:
window.HybridWebView = { "Init": function Init() { function DispatchHybridWebViewMessage(message) { const event = new CustomEvent("HybridWebViewMessageReceived", { detail: { message: message } }); window.dispatchEvent(event); } if (window.chrome && window.chrome.webview) { // Windows WebView2 window.chrome.webview.addEventListener('message', arg => { DispatchHybridWebViewMessage(arg.data); }); } else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop) { // iOS and MacCatalyst WKWebView window.external = { "receiveMessage": message => { DispatchHybridWebViewMessage(message); } }; } else { // Android WebView window.addEventListener('message', arg => { DispatchHybridWebViewMessage(arg.data); }); } }, "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 __SendMessageInternal(type, message) { const messageToSend = type + '|' + message; if (window.chrome && window.chrome.webview) { // Windows WebView2 window.chrome.webview.postMessage(messageToSend); } else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop) { // iOS and MacCatalyst WKWebView window.webkit.messageHandlers.webwindowinterop.postMessage(messageToSend); } else { // Android WebView hybridWebViewHost.sendMessage(messageToSend); } }, "__InvokeJavaScript": async function __InvokeJavaScript(taskId, methodName, args) { try { var result = null; if (methodName[Symbol.toStringTag] === 'AsyncFunction') { result = await methodName(...args); } else { result = methodName(...args); } window.HybridWebView.__TriggerAsyncCallback(taskId, result); } catch (ex) { console.error(ex); window.HybridWebView.__TriggerAsyncFailedCallback(taskId, ex); } }, "__TriggerAsyncCallback": function __TriggerAsyncCallback(taskId, result) { const json = JSON.stringify(result); window.HybridWebView.__SendMessageInternal('__InvokeJavaScriptCompleted', taskId + '|' + json); }, "__TriggerAsyncFailedCallback": function __TriggerAsyncCallback(taskId, error) { if (!error) { json = { Message: "Unknown error", StackTrace: Error().stack }; } else if (error instanceof Error) { json = { Name: error.name, Message: error.message, StackTrace: error.stack }; } else if (typeof (error) === 'string') { json = { Message: error, StackTrace: Error().stack }; } else { json = { Message: JSON.stringify(error), StackTrace: Error().stack }; } json = JSON.stringify(json); window.HybridWebView.__SendMessageInternal('__InvokeJavaScriptFailed', taskId + '|' + json); } } window.HybridWebView.Init();
Em seguida, adicione qualquer conteúdo da Web adicional ao seu projeto.
Advertência
Em alguns casos, o Visual Studio pode adicionar entradas ao arquivo de .csproj do projeto que estão incorretas. Ao usar o local padrão para ativos brutos, não deve haver entradas para esses arquivos ou pastas no arquivo .csproj.
Adicione o controle HybridWebView ao seu aplicativo:
<Grid RowDefinitions="Auto,*" ColumnDefinitions="*"> <Button Text="Send message to JavaScript" Clicked="OnSendMessageButtonClicked" /> <HybridWebView x:Name="hybridWebView" RawMessageReceived="OnHybridWebViewRawMessageReceived" Grid.Row="1" /> </Grid>
Modifique o método
CreateMauiApp
de sua classeMauiProgram
para habilitar ferramentas de desenvolvedor nos controles WebView subjacentes quando seu aplicativo estiver sendo executado na configuração de depuração. Para fazer isso, chame o método AddHybridWebViewDeveloperTools no objeto IServiceCollection:using Microsoft.Extensions.Logging; public static class MauiProgram { public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder .UseMauiApp<App>() .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(); } }
Use as APIs HybridWebView para enviar mensagens entre o código JavaScript e o código C#:
private void OnSendMessageButtonClicked(object sender, EventArgs e) { hybridWebView.SendRawMessage($"Hello from C#!"); } private async void OnHybridWebViewRawMessageReceived(object sender, HybridWebViewRawMessageReceivedEventArgs e) { await DisplayAlert("Raw Message Received", e.Message, "OK"); }
As mensagens acima são classificadas como brutas porque nenhum processamento adicional é executado. Você também pode codificar dados dentro da mensagem para executar mensagens mais avançadas.
Invoque JavaScript a partir de C#
O código C# do seu aplicativo pode invocar métodos JavaScript de forma síncrona e assíncrona dentro do HybridWebView, com parâmetros opcionais e um valor de retorno opcional. Isto pode ser conseguido com os métodos InvokeJavaScriptAsync e EvaluateJavaScriptAsync:
- O método EvaluateJavaScriptAsync executa o código JavaScript fornecido por meio de um parâmetro e retorna o resultado como uma cadeia de caracteres.
- O método InvokeJavaScriptAsync invoca um método JavaScript especificado, opcionalmente passando valores de parâmetro, e especifica um argumento genérico que indica o tipo do valor de retorno. Ele retorna um objeto do tipo de argumento genérico que contém o valor de retorno do método JavaScript chamado. Internamente, os parâmetros e valores de retorno são codificados em JSON.
Observação
O .NET 10 inclui uma sobrecarga de InvokeJavaScriptAsync que invoca um método JavaScript especificado sem especificar nenhuma informação sobre o tipo de retorno. Para obter mais informações, consulte Invocar métodos JavaScript que não retornam um valor.
Invocar JavaScript síncrono
Os métodos JavaScript síncronos podem ser invocados com os métodos EvaluateJavaScriptAsync e InvokeJavaScriptAsync. No exemplo a seguir, o método InvokeJavaScriptAsync é usado para demonstrar a invocação de JavaScript incorporado no conteúdo da Web de um aplicativo. Por exemplo, um método Javascript simples para adicionar dois números pode ser definido no seu conteúdo da web:
function AddNumbers(a, b) {
return a + b;
}
O método JavaScript AddNumbers
pode ser invocado a partir do C# com o método InvokeJavaScriptAsync:
double x = 123d;
double y = 321d;
double result = await hybridWebView.InvokeJavaScriptAsync<double>(
"AddNumbers", // JavaScript method name
HybridSampleJSContext.Default.Double, // JSON serialization info for return type
[x, y], // Parameter values
[HybridSampleJSContext.Default.Double, HybridSampleJSContext.Default.Double]); // JSON serialization info for each parameter
A invocação do método requer a especificação de JsonTypeInfo
objetos que incluem informações de serialização para os tipos utilizados na operação. Esses objetos são criados automaticamente incluindo a seguinte classe partial
em seu projeto:
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(double))]
internal partial class HybridSampleJsContext : JsonSerializerContext
{
// This type's attributes specify JSON serialization info to preserve type structure
// for trimmed builds.
}
Importante
A classe HybridSampleJsContext
deve ser partial
para que a geração de código possa fornecer a implementação quando o projeto é compilado. Se o tipo estiver aninhado em outro tipo, aquele tipo também deverá ser partial
.
Invoque JavaScript assíncrono
Os métodos JavaScript assíncronos podem ser invocados com os métodos EvaluateJavaScriptAsync e InvokeJavaScriptAsync. No exemplo a seguir, o método InvokeJavaScriptAsync é usado para demonstrar a invocação de JavaScript incorporado no conteúdo da Web de um aplicativo. Por exemplo, um método Javascript que recupera dados de forma assíncrona pode ser definido em seu conteúdo da Web:
async function EvaluateMeWithParamsAndAsyncReturn(s1, s2) {
const response = await fetch("/asyncdata.txt");
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
var jsonData = await response.json();
jsonData[s1] = s2;
return jsonData;
}
O método JavaScript EvaluateMeWithParamsAndAsyncReturn
pode ser invocado a partir do C# com o método InvokeJavaScriptAsync:
Dictionary<string, string> asyncResult = await hybridWebView.InvokeJavaScriptAsync<Dictionary<string, string>>(
"EvaluateMeWithParamsAndAsyncReturn", // JavaScript method name
HybridSampleJSContext.Default.DictionaryStringString, // JSON serialization info for return type
["new_key", "new_value"], // Parameter values
[HybridSampleJSContext.Default.String, HybridSampleJSContext.Default.String]); // JSON serialization info for each parameter
Neste exemplo, asyncResult
é um Dictionary<string, string>
que contém os dados JSON da solicitação da Web.
A invocação do método requer a especificação de JsonTypeInfo
objetos que incluem informações de serialização para os tipos usados na operação. Esses objetos são criados automaticamente incluindo a seguinte classe partial
em seu projeto:
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Dictionary<string, string>))]
[JsonSerializable(typeof(string))]
internal partial class HybridSampleJSContext : JsonSerializerContext
{
// This type's attributes specify JSON serialization info to preserve type structure
// for trimmed builds.
}
Importante
A classe HybridSampleJsContext
deve ser partial
para que a geração de código possa fornecer a implementação quando o projeto é compilado. Se o tipo estiver aninhado dentro de outro tipo, então esse deve também ser partial
.
Invoque métodos JavaScript que não retornam um valor
O método InvokeJavaScriptAsync também pode ser usado para invocar métodos JavaScript que não retornam um valor. Existem abordagens alternativas para o fazer:
Invoque o InvokeJavaScriptAsync, especificando o nome do método JavaScript e quaisquer parâmetros opcionais:
await hybridWebView.InvokeJavaScriptAsync("javaScriptWithVoidReturn"); // JavaScript method name
Neste exemplo, apenas o nome do método JavaScript é especificado.
Invoque o método InvokeJavaScriptAsync sem especificar o argumento genérico:
await hybridWebView.InvokeJavaScriptAsync( "javaScriptWithParamsAndVoidReturn", // JavaScript method name HybridSampleJSContext.Default.Double, // JSON serialization info for return type [x, y], // Parameter values [HybridSampleJSContext.Default.Double, HybridSampleJSContext.Default.Double]); // JSON serialization info for each parameter
Neste exemplo, embora o argumento genérico não seja necessário, ainda é necessário fornecer informações de serialização JSON para o tipo de retorno, mesmo que ele não seja usado.
Invoque o método InvokeJavaScriptAsync ao especificar o argumento genérico:
await hybridWebView.InvokeJavaScriptAsync<double>( "javaScriptWithParamsAndVoidReturn", // JavaScript method name null, // JSON serialization info for return type [x, y], // Parameter values [HybridSampleJSContext.Default.Double, HybridSampleJSContext.Default.Double]); // JSON serialization info for each parameter
Neste exemplo, o argumento genérico é necessário e
null
pode ser passado como o valor das informações de serialização JSON para o tipo de retorno.
Enviar exceções JavaScript para .NET
Por padrão, a invocação de métodos JavaScript em um HybridWebView pode ocultar exceções geradas pelo seu código JavaScript. Para aceitar que as exceções JavaScript sejam enviadas para o .NET, onde são relançadas como exceções do .NET, adicione o seguinte código à sua classe MauiProgram
:
static MauiProgram()
{
AppContext.SetSwitch("HybridWebView.InvokeJavaScriptThrowsExceptions", true);
}
Isso permite cenários em que, se o código C# chamar código JavaScript e o código JavaScript falhar, a falha JavaScript será enviada para o .NET, onde será relançada como uma exceção .NET que pode ser capturada e tratada.
Invoque C# a partir de JavaScript
O código JavaScript do seu aplicativo dentro do HybridWebView pode invocar métodos C# de forma síncrona e assíncrona, com parâmetros opcionais e um valor de retorno opcional. Este objetivo pode ser atingido através das seguintes medidas:
- Definição de métodos C# públicos que serão invocados a partir do JavaScript.
- Chamando o método SetInvokeJavaScriptTarget para definir o objeto que será o destino das chamadas JavaScript do HybridWebView.
- Chamando os métodos C# a partir do JavaScript.
O exemplo a seguir define métodos públicos síncronos e assíncronos para invocar a partir de JavaScript:
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 async Task DoAsyncWork()
{
Debug.WriteLine("DoAsyncWork");
await Task.Delay(1000);
}
public async Task DoAsyncWorkParams(int i, string s)
{
Debug.WriteLine($"DoAsyncWorkParams: {i}, {s}");
await Task.Delay(1000);
}
public async Task<String> DoAsyncWorkReturn()
{
Debug.WriteLine("DoAsyncWorkReturn");
await Task.Delay(1000);
return "Hello from C#!";
}
public async Task<SyncReturn> DoAsyncWorkParamsReturn(int i, string s)
{
Debug.WriteLine($"DoAsyncWorkParamsReturn: {i}, {s}");
await Task.Delay(1000);
return new SyncReturn
{
Message = "Hello from C#!" + s,
Value = i
};
}
public class SyncReturn
{
public string? Message { get; set; }
public int Value { get; set; }
}
}
Em seguida, você deve chamar o método SetInvokeJavaScriptTarget para definir o objeto que será o destino das chamadas JavaScript do HybridWebView:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
hybridWebView.SetInvokeJavaScriptTarget(this);
}
...
}
Os métodos públicos no objeto definido por meio do método SetInvokeJavaScriptTarget podem ser invocados do JavaScript com a função window.HybridWebView.InvokeDotNet
:
// Synchronous methods
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']);
// Asynchronous methods
await window.HybridWebView.InvokeDotNet('DoAsyncWork');
await window.HybridWebView.InvokeDotNet('DoAsyncWorkParams', [123, 'hello']);
const retValue = await window.HybridWebView.InvokeDotNet('DoAsyncWorkReturn');
const retValue = await window.HybridWebView.InvokeDotNet('DoAsyncWorkParamsReturn', [123, 'hello']);
A função JavaScript window.HybridWebView.InvokeDotNet
invoca um método C# especificado, com parâmetros opcionais e um valor de retorno opcional.
Observação
Invocar a função JavaScript window.HybridWebView.InvokeDotNet
requer que seu aplicativo inclua a biblioteca JavaScript HybridWebView.js listada anteriormente neste artigo.