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

Context menu support for Windows and MacCatalyst #9174

Merged
merged 31 commits into from
Aug 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2403d5a
Context menus for all Views
Eilon Jul 13, 2022
2808ece
Add Public API info
Eilon Aug 9, 2022
63b8deb
Update PublicAPI.Unshipped.txt
Eilon Aug 9, 2022
a880e77
- add tests and fix BC propagation
PureWeen Aug 10, 2022
317f901
- cleanup winui handler
PureWeen Aug 10, 2022
152eafc
- add comment and issue reference
PureWeen Aug 10, 2022
ec70d8e
- add some additional samples for testing
PureWeen Aug 11, 2022
1c93799
- add link to github issue
PureWeen Aug 11, 2022
90f7f6d
- simplify ios mapcontextflyout
PureWeen Aug 11, 2022
bb3b3ca
- cleanup
PureWeen Aug 11, 2022
277c9ab
- Convert to MenuFlyout
PureWeen Aug 15, 2022
3e93cc4
- fix handler names
PureWeen Aug 15, 2022
2b9aeea
- fix test names
PureWeen Aug 15, 2022
0a4f6e8
- inline creating of args
PureWeen Aug 15, 2022
bb85b32
- add FlyoutBase API
PureWeen Aug 15, 2022
3a74863
- fix Tizen API
PureWeen Aug 15, 2022
6e0cd68
- fix device tests
PureWeen Aug 16, 2022
04e3123
- switch to attached property
PureWeen Aug 17, 2022
a9ff5e2
- IContextFlyoutElement
PureWeen Aug 17, 2022
71491de
- fixed BP
PureWeen Aug 17, 2022
b872532
- fix propagated bindingcontext
PureWeen Aug 17, 2022
c524e76
- rewire context menu code to be more on demand ios
PureWeen Aug 19, 2022
f0b0e75
Merge branch 'main' into eilon/context-menus-everywhere
PureWeen Aug 19, 2022
c0b360f
Update ContextFlyoutTests.Windows.cs
PureWeen Aug 20, 2022
d0d955c
Merge branch 'main' into eilon/context-menus-everywhere
PureWeen Aug 20, 2022
25c3dd9
Merge branch 'main' into eilon/context-menus-everywhere
PureWeen Aug 22, 2022
fd927de
Update Element.Impl.cs
PureWeen Aug 22, 2022
035a006
- fix extra ZIndex APIs
PureWeen Aug 22, 2022
5cdf1e4
Merge branch 'main' into eilon/context-menus-everywhere
PureWeen Aug 22, 2022
d6eae61
Update PublicAPI.Unshipped.txt
PureWeen Aug 22, 2022
9c5c715
Update PublicAPI.Unshipped.txt
PureWeen Aug 22, 2022
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
18 changes: 10 additions & 8 deletions src/Compatibility/Core/src/Windows/CellControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls.Handlers.Compatibility;
using Microsoft.Maui.Controls.Platform;
using WMenuFlyout = Microsoft.UI.Xaml.Controls.MenuFlyout;
using WFlyoutBase = Microsoft.UI.Xaml.Controls.Primitives.FlyoutBase;

namespace Microsoft.Maui.Controls.Compatibility.Platform.UWP
{
Expand Down Expand Up @@ -284,9 +286,9 @@ void OnLongTap(object sender, HoldingRoutedEventArgs e)
/// <summary>
/// To check the context, not just the text.
/// </summary>
MenuFlyout GetAttachedFlyout()
WMenuFlyout GetAttachedFlyout()
{
if (FlyoutBase.GetAttachedFlyout(CellContent) is MenuFlyout flyout)
if (WFlyoutBase.GetAttachedFlyout(CellContent) is WMenuFlyout flyout)
{
var actions = Cell.ContextActions;
if (flyout.Items.Count != actions.Count)
Expand All @@ -306,16 +308,16 @@ void OpenContextMenu()
{
if (GetAttachedFlyout() == null)
{
var flyout = new MenuFlyout();
var flyout = new WMenuFlyout();
SetupMenuItems(flyout);

((INotifyCollectionChanged)Cell.ContextActions).CollectionChanged += OnContextActionsChanged;

_contextActions = Cell.ContextActions;
FlyoutBase.SetAttachedFlyout(CellContent, flyout);
WFlyoutBase.SetAttachedFlyout(CellContent, flyout);
}

FlyoutBase.ShowAttachedFlyout(CellContent);
WFlyoutBase.ShowAttachedFlyout(CellContent);
}

void SetCell(object newContext)
Expand Down Expand Up @@ -441,17 +443,17 @@ void SetupContextMenu()
_contextActions = null;
}

FlyoutBase.SetAttachedFlyout(CellContent, null);
WFlyoutBase.SetAttachedFlyout(CellContent, null);
return;
}

CellContent.PointerReleased += OnClick;
CellContent.Holding += OnLongTap;
}

void SetupMenuItems(MenuFlyout flyout)
void SetupMenuItems(WMenuFlyout flyout)
{
foreach (MenuItem item in Cell.ContextActions)
foreach (var item in Cell.ContextActions)
{
var flyoutItem = new Microsoft.UI.Xaml.Controls.MenuFlyoutItem();
flyoutItem.SetBinding(UI.Xaml.Controls.MenuFlyoutItem.TextProperty, "Text");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
</PropertyGroup>

<PropertyGroup>
<ApplicationTitle>.NET MAUI Controls</ApplicationTitle>
<ApplicationId>com.microsoft.maui.sample</ApplicationId>
<ApplicationTitle>.NET MAUI Controls Sandbox</ApplicationTitle>
<ApplicationId>com.microsoft.maui.sandbox</ApplicationId>
<ApplicationIdGuid>5ee3361c-1cf9-443e-87f1-a7667fba9caa</ApplicationIdGuid>
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<ApplicationVersion>1</ApplicationVersion>
<_FastDeploymentDiagnosticLogging>True</_FastDeploymentDiagnosticLogging>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap rescap">

<Identity
Name="f9e4fa3e-3505-4742-9b2b-d1acdaff4ec8"
Publisher="CN=.NET Foundation"
Version="1.0.0.0" />
<Identity Publisher="CN=.NET Foundation" />

<Properties>
<DisplayName>$placeholder$</DisplayName>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.Pages.ContextFlyoutPage">

<ScrollView>
<VerticalStackLayout>
<HorizontalStackLayout Spacing="10">
<Button Text="Increment by 1 (or right-click me)" Clicked="OnIncrementByOneClicked" FontSize="30" BackgroundColor="DarkGray">
<FlyoutBase.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem Text="Increment by 10" Clicked="OnIncrementMenuItemClicked" CommandParameter="10"></MenuFlyoutItem>
PureWeen marked this conversation as resolved.
Show resolved Hide resolved
PureWeen marked this conversation as resolved.
Show resolved Hide resolved
<MenuFlyoutItem Text="Increment by 20" Clicked="OnIncrementMenuItemClicked" CommandParameter="20"></MenuFlyoutItem>
<MenuFlyoutSubItem Text="More options">
<MenuFlyoutItem Text="Increment by 1,000!" Clicked="OnIncrementMenuItemClicked" CommandParameter="1000"></MenuFlyoutItem>
<MenuFlyoutItem Text="Increment by 1,000,000!" Clicked="OnIncrementMenuItemClicked" CommandParameter="1000000"></MenuFlyoutItem>
</MenuFlyoutSubItem>
</MenuFlyout>
</FlyoutBase.ContextFlyout>
</Button>
<Label Text="{Binding CounterValue}" FontSize="30" VerticalOptions="Center"></Label>
</HorizontalStackLayout>

<Label Text="Right-click and add additional menus (currently quirky on Windows and Catalyst)">
<FlyoutBase.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem Text="Add Menu" Clicked="OnAddMenuClicked"></MenuFlyoutItem>
<MenuFlyoutSubItem Text="Add Sub Menu" Clicked="OnSubMenuClicked"></MenuFlyoutSubItem>
</MenuFlyout>
</FlyoutBase.ContextFlyout>
</Label>

<Label Text="Right-click to see beautiful menus" FontSize="30">
Copy link
Member

Choose a reason for hiding this comment

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

<FlyoutBase.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem Text="Red">
<MenuFlyoutItem.IconImageSource>
<FontImageSource Glyph="X" FontFamily="Arial" Color="Red" />
</MenuFlyoutItem.IconImageSource>
</MenuFlyoutItem>
<MenuFlyoutItem Text="Blue">
<MenuFlyoutItem.IconImageSource>
<FontImageSource Glyph="X" FontFamily="Arial" Color="Blue" />
</MenuFlyoutItem.IconImageSource>
</MenuFlyoutItem>
<MenuFlyoutItem Text="Green">
<MenuFlyoutItem.IconImageSource>
<FontImageSource Glyph="X" FontFamily="Arial" Color="Green" />
</MenuFlyoutItem.IconImageSource>
</MenuFlyoutItem>
<MenuFlyoutSeparator />
<MenuFlyoutItem Text="Orange">
<MenuFlyoutItem.IconImageSource>
<FontImageSource Glyph="X" FontFamily="Arial" Color="Orange" />
</MenuFlyoutItem.IconImageSource>
</MenuFlyoutItem>
<MenuFlyoutItem Text="Pink">
<MenuFlyoutItem.IconImageSource>
<FontImageSource Glyph="X" FontFamily="Arial" Color="Pink" />
</MenuFlyoutItem.IconImageSource>
</MenuFlyoutItem>
<MenuFlyoutItem Text="Brown">
<MenuFlyoutItem.IconImageSource>
<FontImageSource Glyph="X" FontFamily="Arial" Color="Brown" />
</MenuFlyoutItem.IconImageSource>
</MenuFlyoutItem>
<MenuFlyoutSeparator />
<MenuFlyoutSubItem Text="Advanced colors">
<MenuFlyoutItem Text="Chartreuse">
<MenuFlyoutItem.IconImageSource>
<FontImageSource Glyph="X" FontFamily="Arial" Color="Chartreuse" />
</MenuFlyoutItem.IconImageSource>
</MenuFlyoutItem>
<MenuFlyoutItem Text="Misty Rose">
<MenuFlyoutItem.IconImageSource>
<FontImageSource Glyph="X" FontFamily="Arial" Color="MistyRose" />
</MenuFlyoutItem.IconImageSource>
</MenuFlyoutItem>
<MenuFlyoutItem Text="Medium Purple">
<MenuFlyoutItem.IconImageSource>
<FontImageSource Glyph="X" FontFamily="Arial" Color="MediumPurple" />
</MenuFlyoutItem.IconImageSource>
</MenuFlyoutItem>
</MenuFlyoutSubItem>
</MenuFlyout>
</FlyoutBase.ContextFlyout>
</Label>

<Entry Placeholder="Has default context menu (crashes on WinUI #9353)">
</Entry>

<Entry Placeholder="Has a custom context menu" x:Name="EntryWithContextFlyout">
<FlyoutBase.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem Text="Show text in a message" Clicked="OnEntryShowTextClicked"></MenuFlyoutItem>
<MenuFlyoutItem Text="Add some text" Clicked="OnEntryAddTextClicked"></MenuFlyoutItem>
<MenuFlyoutItem Text="Clear all text" Clicked="OnEntryClearTextClicked"></MenuFlyoutItem>
</MenuFlyout>
</FlyoutBase.ContextFlyout>
</Entry>

<Image MaximumHeightRequest="200" MaximumWidthRequest="200">
<FlyoutBase.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem Text="Use Clicked event" Clicked="OnImageContextClicked"></MenuFlyoutItem>
<MenuFlyoutItem Text="Use Command and CommandParameter" Command="{Binding ImageContextCommand}" CommandParameter="some value"></MenuFlyoutItem>
</MenuFlyout>
</FlyoutBase.ContextFlyout>
<Image.Source>
<FontImageSource Glyph="🆒" FontFamily="Arial" Color="MediumPurple" Size="50" />
</Image.Source>
</Image>

<WebView x:Name="ContextMenuWebView" Source="https://bing.com" MinimumHeightRequest="400">
<FlyoutBase.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem Text="Go to MAUI repo" Clicked="OnWebViewGoToSiteClicked"></MenuFlyoutItem>
<MenuFlyoutSeparator />
<MenuFlyoutItem Text="Invoke some JS" Clicked="OnWebViewInvokeJSClicked"></MenuFlyoutItem>
</MenuFlyout>
</FlyoutBase.ContextFlyout>
</WebView>

</VerticalStackLayout>
</ScrollView>
</ContentPage>

Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
using System;
using System.Diagnostics;
using System.Windows.Input;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics;

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

ImageContextCommand = new Command(
execute: async (object arg) =>
{
await DisplayAlert(
title: "Image",
message: $"The image's context menu was clicked via a command with parameter: {arg}",
cancel: "OK");
});

BindingContext = this;

ContextMenuWebView.HandlerChanged += OnWebViewHandlerChanged;
}


void OnWebViewHandlerChanged(object sender, EventArgs e)
{
if (ContextMenuWebView.Handler != null)
{
#if WINDOWS
var webView2 = (Microsoft.UI.Xaml.Controls.WebView2)ContextMenuWebView.Handler.PlatformView;
webView2.CoreWebView2Initialized += OnWebView2CoreWebView2Initialized;
#elif MACCATALYST
var wkWebView = (WebKit.WKWebView)ContextMenuWebView.Handler.PlatformView;
// TODO: Need to figure out how to disable default WKWebView context menu so that
// the custom context flyout is shown instead. (It does sometimes show up for a second
// but then it goes back to the default web context menu.)
#endif
}
}

#if WINDOWS
void OnWebView2CoreWebView2Initialized(Microsoft.UI.Xaml.Controls.WebView2 sender, Microsoft.UI.Xaml.Controls.CoreWebView2InitializedEventArgs args)
{
sender.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false;
}
#endif

public ICommand ImageContextCommand { get; init; }

int count;

void OnIncrementByOneClicked(object sender, EventArgs e)
{
count++;
OnPropertyChanged(nameof(CounterValue));
}

void OnIncrementMenuItemClicked(object sender, EventArgs e)
{
var menuItem = (MenuFlyoutItem)sender;
var incrementAmount = int.Parse((string)menuItem.CommandParameter);
count += incrementAmount;
OnPropertyChanged(nameof(CounterValue));
}

public string CounterValue => count.ToString("N0");

async void OnEntryShowTextClicked(object sender, EventArgs e)
{
await DisplayAlert(
title: "Entry",
message: $"The entry's text is: {EntryWithContextFlyout.Text}",
cancel: "OK");
}

void OnEntryAddTextClicked(object sender, EventArgs e)
{
EntryWithContextFlyout.Text += " more text!";
}

void OnEntryClearTextClicked(object sender, EventArgs e)
{
EntryWithContextFlyout.Text = "";
}

async void OnImageContextClicked(object sender, EventArgs e)
{
await DisplayAlert(
title: "Image",
message: $"The image's context menu was clicked",
cancel: "OK");
}

void OnWebViewGoToSiteClicked(object sender, EventArgs e)
{
ContextMenuWebView.Source = new UrlWebViewSource() { Url = "https://github.com/dotnet/maui", };
}

async void OnWebViewInvokeJSClicked(object sender, EventArgs e)
{
await ContextMenuWebView.EvaluateJavaScriptAsync(@"alert('help, i\'m being invoked!');");
}

void OnAddMenuClicked(object sender, EventArgs e)
{
var contextFlyout = ((MenuFlyoutItem)sender).Parent as MenuFlyout;
contextFlyout.Add(new MenuFlyoutItem() { Text = "Thank you for adding me" });
}

void OnSubMenuClicked(object sender, EventArgs e)
{
var subMenu = ((MenuFlyoutSubItem)sender);
subMenu.Add(new MenuFlyoutItem() { Text = "Thank you for adding me" });
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ protected override IEnumerable<SectionModel> CreateItems() => new[]
new SectionModel(typeof(ClipPage), "Clip",
"Defines the outline of the contents of an element."),

new SectionModel(typeof(ContextFlyoutPage), "ContextFlyout",
"Right-click context menu for controls."),

new SectionModel(typeof(ContentPageGallery), "ContentPage",
"Demonstrates using a Content Page."),

Expand Down
3 changes: 3 additions & 0 deletions src/Controls/src/Core/BindableObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,9 @@ protected virtual void OnBindingContextChanged()

if (Shell.GetTitleView(this) is View titleView)
SetInheritedBindingContext(titleView, BindingContext);

if (FlyoutBase.GetContextFlyout(this) is BindableObject contextFlyout)
SetInheritedBindingContext(contextFlyout, BindingContext);
}

protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
Expand Down
Loading