Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

HybridWebView control for integrating JS/HTML/CSS easily into a .NET MAUI app #22880

Merged
merged 9 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion eng/devices/windows.cake
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ Task("Test")
.IsDependentOn("SetupTestPaths")
.Does(() =>
{
var waitForResultTimeoutInSeconds = 120;
var waitForResultTimeoutInSeconds = 240;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On CI the tests were failing due to timeout, so I doubled it.

CleanDirectories(TEST_RESULTS);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<views:BasePage
xmlns="https://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="https://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.Pages.HybridWebViewPage"
xmlns:views="clr-namespace:Maui.Controls.Sample.Pages.Base"
Title="HybridWebView">
<views:BasePage.Content>

<Grid ColumnDefinitions="2*,1*" RowDefinitions="Auto,1*">

<Label
Grid.Row="0"
Grid.Column="0"
Text="HybridWebView here"
x:Name="statusLabel" />

<Button
Grid.Row="0"
Grid.Column="1"
Text="Send message to JS"
Clicked="SendMessageButton_Clicked" />

<HybridWebView
x:Name="hwv"
Grid.Row="1"
Grid.ColumnSpan="2"
HybridRoot="HybridSamplePage"
RawMessageReceived="hwv_RawMessageReceived"/>

</Grid>
</views:BasePage.Content>
</views:BasePage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using Microsoft.Maui.Controls;

namespace Maui.Controls.Sample.Pages
{
public partial class HybridWebViewPage
{
public HybridWebViewPage()
{
InitializeComponent();
}

private void SendMessageButton_Clicked(object sender, EventArgs e)
{
hwv.SendRawMessage("Hello from C#!");
}

private void hwv_RawMessageReceived(object sender, HybridWebViewRawMessageReceivedEventArgs e)
{
Dispatcher.Dispatch(() => statusLabel.Text += e.Message);
}
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!DOCTYPE html>

<html lang="en" xmlns="https://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<link rel="stylesheet" href="styles/app.css">
<script src="scripts/HybridWebView.js"></script>
<script>
window.addEventListener(
"HybridWebViewMessageReceived",
function (e) {
var messageFromCSharp = document.getElementById("messageFromCSharp");
messageFromCSharp.value += '\r\n' + e.detail.message;
});
</script>
</head>
<body>
<div>
Hybrid sample!
</div>
<div>
<button onclick="window.HybridWebView.SendRawMessage('Message from JS!')">Send message to C#</button>
</div>
<div>
Message from C#: <textarea readonly id="messageFromCSharp" style="width: 80%; height: 10em;"></textarea>
</div>
<div>
Consider checking out this PDF: <a href="docs/sample.pdf">sample.pdf</a>
</div>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
function HybridWebViewInit() {

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);
});
}
}

window.HybridWebView = {
"SendRawMessage": function (message) {

if (window.chrome && window.chrome.webview) {
// Windows WebView2
window.chrome.webview.postMessage(message);
}
else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop) {
// iOS and MacCatalyst WKWebView
window.webkit.messageHandlers.webwindowinterop.postMessage(message);
}
else {
// Android WebView
hybridWebViewHost.sendRawMessage(message);
}
}
}

HybridWebViewInit();
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
button
{
background-color: dodgerblue;
border-radius: 5px;
border-width: 1px;
color: white;
cursor: pointer;
margin: 6px;
text-align: center;
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ protected override IEnumerable<SectionModel> CreateItems() => new[]
new SectionModel(typeof(EntryPage), "Entry",
"The Entry control is used for single-line text input."),

new SectionModel(typeof(HybridWebViewPage), "HybridWebView",
"The HybridWebView control embeds web content locally and natively in an app."),

new SectionModel(typeof(ImagePage), "Image",
"Displays an image."),

Expand Down
54 changes: 54 additions & 0 deletions src/Controls/src/Core/HybridWebView/HybridWebView.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;

namespace Microsoft.Maui.Controls
{
/// <summary>
/// A <see cref="View"/> that presents local HTML content in a web view and allows JavaScript and C# code to interop using messages.
/// </summary>
public class HybridWebView : View, IHybridWebView
{
/// <summary>Bindable property for <see cref="DefaultFile"/>.</summary>
public static readonly BindableProperty DefaultFileProperty =
BindableProperty.Create(nameof(DefaultFile), typeof(string), typeof(HybridWebView), defaultValue: "index.html");
mattleibow marked this conversation as resolved.
Show resolved Hide resolved
/// <summary>Bindable property for <see cref="HybridRoot"/>.</summary>
public static readonly BindableProperty HybridRootProperty =
BindableProperty.Create(nameof(HybridRoot), typeof(string), typeof(HybridWebView), defaultValue: "wwwroot");


/// <summary>
/// Specifies the file within the <see cref="HybridRoot"/> that should be served as the default file. The
/// default value is <c>index.html</c>.
/// </summary>
public string? DefaultFile
{
get { return (string)GetValue(DefaultFileProperty); }
set { SetValue(DefaultFileProperty, value); }
}

/// <summary>
/// The path within the app's "Raw" asset resources that contain the web app's contents. For example, if the
/// files are located in <c>[ProjectFolder]/Resources/Raw/hybrid_root</c>, then set this property to "hybrid_root".
/// The default value is <c>wwwroot</c>, which maps to <c>[ProjectFolder]/Resources/Raw/wwwroot</c>.
/// </summary>
public string? HybridRoot
{
get { return (string)GetValue(HybridRootProperty); }
set { SetValue(HybridRootProperty, value); }
}

void IHybridWebView.RawMessageReceived(string rawMessage)
{
RawMessageReceived?.Invoke(this, new HybridWebViewRawMessageReceivedEventArgs(rawMessage));
}

/// <summary>
/// Raised when a raw message is received from the web view. Raw messages are strings that have no additional processing.
/// </summary>
public event EventHandler<HybridWebViewRawMessageReceivedEventArgs>? RawMessageReceived;

public void SendRawMessage(string rawMessage)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any way that platforms support a "blocking" request where we could have a SendRawMessageAsync so that people can await the call and potentially have code that wait for the JS to finish executing? Or is this all a fire-and-forget messaging system?

My main thought is that if you want to invoke multiple messages, but they need to run in series and not just span and maybe a small delay somewhere will mix the order.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are several variations of these methods that I'll be adding in subsequent PRs, including async versions, plus method invocation. Some of this is tracked in #22303 and #22304.

You can technically implement this right now manually by sending messages back and forth like a state machine, but it's of course not ideal. I described this PR to another person as M-MVP (Minimum Minimal Viable Product 😁 ).

{
Handler?.Invoke(nameof(IHybridWebView.SendRawMessage), rawMessage);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For future proofing, this should not pass the string directly but instead pass a new record type:

// in src/Core/src/Primitives/SendRawMessageRequest.cs

namespace Microsoft.Maui
{
	public record SendRawMessageRequest(string rawMessage);
}

If there is any need to return a value - such as a result of the request or some response, then inherit from RetrievePlatformValueRequest<T>:

// in src/Core/src/Primitives/SendRawMessageRequest.cs

namespace Microsoft.Maui
{
	public class SendRawMessageRequest(string rawMessage) : RetrievePlatformValueRequest<string>
    {
        // base members:
        //   public string Result { get; }
        //   public void SetResult(string result);
        //   public bool TrySetResult(string result);
    }
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For this PR this is really all we plan to support. But as part of #22303 and #22304 the message pattern will be updated to be more generic and future-proofed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the original prototype the pattern was that the messages were encoded as JSON and there was a message type ID that said whether it was raw, method invoke, etc. Not sure what the new design will be but it will be capable of supporting multiple message types.

Copy link
Member

@mattleibow mattleibow Jul 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean that for the maui codebase, we want to be consistent and use a wrapper for commands so people can cast instead of each command being its parameter data type.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed in the mtg; something like this will definitely happen but I'd rather do it once I add another message type (such as to invoke methods). That way I'll have a clearer sense of the design.

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;

namespace Microsoft.Maui.Controls
{
public class HybridWebViewRawMessageReceivedEventArgs : EventArgs
{
public HybridWebViewRawMessageReceivedEventArgs(string? message)
{
Message = message;
}

public string? Message { get; }
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
#nullable enable
Microsoft.Maui.Controls.HybridWebView
Microsoft.Maui.Controls.HybridWebView.DefaultFile.get -> string?
Microsoft.Maui.Controls.HybridWebView.DefaultFile.set -> void
Microsoft.Maui.Controls.HybridWebView.HybridRoot.get -> string?
Microsoft.Maui.Controls.HybridWebView.HybridRoot.set -> void
Microsoft.Maui.Controls.HybridWebView.HybridWebView() -> void
Microsoft.Maui.Controls.HybridWebView.RawMessageReceived -> System.EventHandler<Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs!>?
Microsoft.Maui.Controls.HybridWebView.SendRawMessage(string! rawMessage) -> void
Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs
Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs.HybridWebViewRawMessageReceivedEventArgs(string? message) -> void
Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs.Message.get -> string?
Microsoft.Maui.Controls.PlatformDragStartingEventArgs
Microsoft.Maui.Controls.PlatformDragStartingEventArgs.Sender.get -> Android.Views.View!
Microsoft.Maui.Controls.PlatformDragStartingEventArgs.MotionEvent.get -> Android.Views.MotionEvent!
Expand Down Expand Up @@ -73,6 +84,8 @@ Microsoft.Maui.Controls.TimeChangedEventArgs.NewTime.get -> System.TimeSpan
Microsoft.Maui.Controls.TimeChangedEventArgs.OldTime.get -> System.TimeSpan
Microsoft.Maui.Controls.TimeChangedEventArgs.TimeChangedEventArgs(System.TimeSpan oldTime, System.TimeSpan newTime) -> void
Microsoft.Maui.Controls.TimePicker.TimeSelected -> System.EventHandler<Microsoft.Maui.Controls.TimeChangedEventArgs>
static readonly Microsoft.Maui.Controls.HybridWebView.DefaultFileProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.HybridWebView.HybridRootProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.KeyboardAccelerator.KeyProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.KeyboardAccelerator.ModifiersProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.DragGestureRecognizer.CanDragProperty -> Microsoft.Maui.Controls.BindableProperty!
Expand Down
13 changes: 13 additions & 0 deletions src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
#nullable enable
Microsoft.Maui.Controls.HybridWebView
Microsoft.Maui.Controls.HybridWebView.DefaultFile.get -> string?
Microsoft.Maui.Controls.HybridWebView.DefaultFile.set -> void
Microsoft.Maui.Controls.HybridWebView.HybridRoot.get -> string?
Microsoft.Maui.Controls.HybridWebView.HybridRoot.set -> void
Microsoft.Maui.Controls.HybridWebView.HybridWebView() -> void
Microsoft.Maui.Controls.HybridWebView.RawMessageReceived -> System.EventHandler<Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs!>?
Microsoft.Maui.Controls.HybridWebView.SendRawMessage(string! rawMessage) -> void
Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs
Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs.HybridWebViewRawMessageReceivedEventArgs(string? message) -> void
Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs.Message.get -> string?
override Microsoft.Maui.Controls.Handlers.Compatibility.CellRenderer.Invoke(string! command, object? args) -> void
override Microsoft.Maui.Controls.Handlers.Compatibility.CellRenderer.UpdateValue(string! property) -> void
override Microsoft.Maui.Controls.Handlers.Compatibility.ShellRenderer.PreferredStatusBarUpdateAnimation.get -> UIKit.UIStatusBarAnimation
Expand Down Expand Up @@ -89,6 +100,8 @@ override Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutRenderer.Pref
override Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutRenderer.PrefersHomeIndicatorAutoHidden.get -> bool
override Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutRenderer.PrefersStatusBarHidden() -> bool
override Microsoft.Maui.Controls.Platform.Compatibility.ShellSectionRenderer.ViewDidAppear(bool animated) -> void
static readonly Microsoft.Maui.Controls.HybridWebView.DefaultFileProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.HybridWebView.HybridRootProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.KeyboardAccelerator.KeyProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.KeyboardAccelerator.ModifiersProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.DragGestureRecognizer.CanDragProperty -> Microsoft.Maui.Controls.BindableProperty!
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
#nullable enable
Microsoft.Maui.Controls.HybridWebView
Microsoft.Maui.Controls.HybridWebView.DefaultFile.get -> string?
Microsoft.Maui.Controls.HybridWebView.DefaultFile.set -> void
Microsoft.Maui.Controls.HybridWebView.HybridRoot.get -> string?
Microsoft.Maui.Controls.HybridWebView.HybridRoot.set -> void
Microsoft.Maui.Controls.HybridWebView.HybridWebView() -> void
Microsoft.Maui.Controls.HybridWebView.RawMessageReceived -> System.EventHandler<Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs!>?
Microsoft.Maui.Controls.HybridWebView.SendRawMessage(string! rawMessage) -> void
Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs
Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs.HybridWebViewRawMessageReceivedEventArgs(string? message) -> void
Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs.Message.get -> string?
override Microsoft.Maui.Controls.Handlers.Compatibility.CellRenderer.Invoke(string! command, object? args) -> void
override Microsoft.Maui.Controls.Handlers.Compatibility.CellRenderer.UpdateValue(string! property) -> void
override Microsoft.Maui.Controls.GradientBrush.IsEmpty.get -> bool
Expand Down Expand Up @@ -83,6 +94,8 @@ Microsoft.Maui.Controls.TimeChangedEventArgs.OldTime.get -> System.TimeSpan
Microsoft.Maui.Controls.TimeChangedEventArgs.TimeChangedEventArgs(System.TimeSpan oldTime, System.TimeSpan newTime) -> void
Microsoft.Maui.Controls.TimePicker.TimeSelected -> System.EventHandler<Microsoft.Maui.Controls.TimeChangedEventArgs>
override Microsoft.Maui.Controls.Handlers.Items.ItemsViewController<TItemsView>.LoadView() -> void
static readonly Microsoft.Maui.Controls.HybridWebView.DefaultFileProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.HybridWebView.HybridRootProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.KeyboardAccelerator.KeyProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.KeyboardAccelerator.ModifiersProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.DragGestureRecognizer.CanDragProperty -> Microsoft.Maui.Controls.BindableProperty!
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
#nullable enable
Microsoft.Maui.Controls.HybridWebView
Microsoft.Maui.Controls.HybridWebView.DefaultFile.get -> string?
Microsoft.Maui.Controls.HybridWebView.DefaultFile.set -> void
Microsoft.Maui.Controls.HybridWebView.HybridRoot.get -> string?
Microsoft.Maui.Controls.HybridWebView.HybridRoot.set -> void
Microsoft.Maui.Controls.HybridWebView.HybridWebView() -> void
Microsoft.Maui.Controls.HybridWebView.RawMessageReceived -> System.EventHandler<Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs!>?
Microsoft.Maui.Controls.HybridWebView.SendRawMessage(string! rawMessage) -> void
Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs
Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs.HybridWebViewRawMessageReceivedEventArgs(string? message) -> void
Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs.Message.get -> string?
Microsoft.Maui.Controls.PlatformDragStartingEventArgs
Microsoft.Maui.Controls.PlatformDropCompletedEventArgs
Microsoft.Maui.Controls.PlatformDragEventArgs
Expand Down Expand Up @@ -55,6 +66,8 @@ Microsoft.Maui.Controls.TimeChangedEventArgs.OldTime.get -> System.TimeSpan
Microsoft.Maui.Controls.TimeChangedEventArgs.TimeChangedEventArgs(System.TimeSpan oldTime, System.TimeSpan newTime) -> void
Microsoft.Maui.Controls.TimePicker.TimeSelected -> System.EventHandler<Microsoft.Maui.Controls.TimeChangedEventArgs>
override Microsoft.Maui.Controls.GradientBrush.IsEmpty.get -> bool
static readonly Microsoft.Maui.Controls.HybridWebView.DefaultFileProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.HybridWebView.HybridRootProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.KeyboardAccelerator.KeyProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.KeyboardAccelerator.ModifiersProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.DragGestureRecognizer.CanDragProperty -> Microsoft.Maui.Controls.BindableProperty!
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
#nullable enable
Microsoft.Maui.Controls.Handlers.Items.CarouselViewHandler.~CarouselViewHandler() -> void
Microsoft.Maui.Controls.HybridWebView
Microsoft.Maui.Controls.HybridWebView.DefaultFile.get -> string?
Microsoft.Maui.Controls.HybridWebView.DefaultFile.set -> void
Microsoft.Maui.Controls.HybridWebView.HybridRoot.get -> string?
Microsoft.Maui.Controls.HybridWebView.HybridRoot.set -> void
Microsoft.Maui.Controls.HybridWebView.HybridWebView() -> void
Microsoft.Maui.Controls.HybridWebView.RawMessageReceived -> System.EventHandler<Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs!>?
Microsoft.Maui.Controls.HybridWebView.SendRawMessage(string! rawMessage) -> void
Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs
Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs.HybridWebViewRawMessageReceivedEventArgs(string? message) -> void
Microsoft.Maui.Controls.HybridWebViewRawMessageReceivedEventArgs.Message.get -> string?
Microsoft.Maui.Controls.PlatformDragStartingEventArgs
Microsoft.Maui.Controls.PlatformDragStartingEventArgs.Sender.get -> Microsoft.UI.Xaml.UIElement!
Microsoft.Maui.Controls.PlatformDragStartingEventArgs.DragStartingEventArgs.get -> Microsoft.UI.Xaml.DragStartingEventArgs!
Expand Down Expand Up @@ -80,6 +91,8 @@ Microsoft.Maui.Controls.TimeChangedEventArgs.NewTime.get -> System.TimeSpan
Microsoft.Maui.Controls.TimeChangedEventArgs.OldTime.get -> System.TimeSpan
Microsoft.Maui.Controls.TimeChangedEventArgs.TimeChangedEventArgs(System.TimeSpan oldTime, System.TimeSpan newTime) -> void
Microsoft.Maui.Controls.TimePicker.TimeSelected -> System.EventHandler<Microsoft.Maui.Controls.TimeChangedEventArgs>
static readonly Microsoft.Maui.Controls.HybridWebView.DefaultFileProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.HybridWebView.HybridRootProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.KeyboardAccelerator.KeyProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.KeyboardAccelerator.ModifiersProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.DragGestureRecognizer.CanDragProperty -> Microsoft.Maui.Controls.BindableProperty!
Expand Down
Loading
Loading