Skip to content

Commit

Permalink
WPF Example - update MenuHandler to implement custom menu, execute co…
Browse files Browse the repository at this point in the history
…mmand based on CefMenuCommand enum rather than trying to use IRunContextMenuCallback which isn't working (upstream issue)

This is a workaround for #1767
You can use the default CEF menu it does however require you to integrate CEF into your main message loop see #1795
  • Loading branch information
amaitland committed Jan 22, 2018
1 parent e66a99d commit d74215a
Showing 1 changed file with 153 additions and 51 deletions.
204 changes: 153 additions & 51 deletions CefSharp.Wpf.Example/Handlers/MenuHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

using System;
using System.Linq;
using System.Collections.Generic;
using System.Windows.Controls;
using System.Windows;
Expand Down Expand Up @@ -48,71 +49,172 @@ bool IContextMenuHandler.OnContextMenuCommand(IWebBrowser browserControl, IBrows

void IContextMenuHandler.OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame)
{
//var chromiumWebBrowser = (ChromiumWebBrowser)browserControl;
var chromiumWebBrowser = (ChromiumWebBrowser)browserControl;

//chromiumWebBrowser.Dispatcher.Invoke(() =>
//{
// chromiumWebBrowser.ContextMenu = null;
//});
chromiumWebBrowser.Dispatcher.Invoke(() =>
{
chromiumWebBrowser.ContextMenu = null;
});
}

bool IContextMenuHandler.RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback)
{
return false;
//NOTE: Return false to use the build in Context menu - in WPF this requires you integrate into your existing message loop, read the General Usage Guide for more details
//return false;

var chromiumWebBrowser = (ChromiumWebBrowser)browserControl;

//var chromiumWebBrowser = (ChromiumWebBrowser)browserControl;

////IMenuModel is only valid in the context of this method, so need to read the values before invoking on the UI thread
//var menuItems = GetMenuItems(model);

//chromiumWebBrowser.Dispatcher.Invoke(() =>
//{
// var menu = new ContextMenu
// {
// IsOpen = true
// };

// RoutedEventHandler handler = null;

// handler = (s, e) =>
// {
// menu.Closed -= handler;

// //If the callback has been disposed then it's already been executed
// //so don't call Cancel
// if(!callback.IsDisposed)
// {
// callback.Cancel();
// }
// };

// menu.Closed += handler;

// foreach (var item in menuItems)
// {
// menu.Items.Add(new MenuItem
// {
// Header = item.Item1,
// Command = new RelayCommand(() => { callback.Continue(item.Item2, CefEventFlags.None); })
// });
// }
// chromiumWebBrowser.ContextMenu = menu;
//});

//return true;
//IMenuModel is only valid in the context of this method, so need to read the values before invoking on the UI thread
var menuItems = GetMenuItems(model).ToList();

chromiumWebBrowser.Dispatcher.Invoke(() =>
{
var menu = new ContextMenu
{
IsOpen = true
};

RoutedEventHandler handler = null;

handler = (s, e) =>
{
menu.Closed -= handler;

//If the callback has been disposed then it's already been executed
//so don't call Cancel
if (!callback.IsDisposed)
{
callback.Cancel();
}
};

menu.Closed += handler;

foreach (var item in menuItems)
{
if(item.Item2 == CefMenuCommand.NotFound && string.IsNullOrWhiteSpace(item.Item1))
{
menu.Items.Add(new Separator());
continue;
}

menu.Items.Add(new MenuItem
{
Header = item.Item1.Replace("&", "_"),
IsEnabled = item.Item3,
Command = new RelayCommand(() =>
{
//BUG: CEF currently not executing callbacks correctly so we manually map the commands below
//the following line worked in previous versions, it doesn't now, so custom EXAMPLE below
//callback.Continue(item.Item2, CefEventFlags.None);

//NOTE: Note all menu item options below have been tested, you can work out the rest
switch (item.Item2)
{
case CefMenuCommand.Back:
{
browser.GoBack();
break;
}
case CefMenuCommand.Forward:
{
browser.GoForward();
break;
}
case CefMenuCommand.Cut:
{
browser.FocusedFrame.Cut();
break;
}
case CefMenuCommand.Copy:
{
browser.FocusedFrame.Copy();
break;
}
case CefMenuCommand.Paste:
{
browser.FocusedFrame.Paste();
break;
}
case CefMenuCommand.Print:
{
browser.GetHost().Print();
break;
}
case CefMenuCommand.ViewSource:
{
browser.FocusedFrame.ViewSource();
break;
}
case CefMenuCommand.Undo:
{
browser.FocusedFrame.Undo();
break;
}
case CefMenuCommand.StopLoad:
{
browser.StopLoad();
break;
}
case CefMenuCommand.SelectAll:
{
browser.FocusedFrame.SelectAll();
break;
}
case CefMenuCommand.Redo:
{
browser.FocusedFrame.Redo();
break;
}
case CefMenuCommand.Find:
{
browser.GetHost().Find(0, parameters.SelectionText, true, false, false);
break;
}
case CefMenuCommand.AddToDictionary:
{
browser.GetHost().AddWordToDictionary(parameters.MisspelledWord);
break;
}
case CefMenuCommand.Reload:
{
browser.Reload();
break;
}
case CefMenuCommand.ReloadNoCache:
{
browser.Reload(ignoreCache: true);
break;
}
case (CefMenuCommand)26501:
{
browser.GetHost().ShowDevTools();
break;
}
case (CefMenuCommand)26502:
{
browser.GetHost().CloseDevTools();
break;
}
}
})
});
}
chromiumWebBrowser.ContextMenu = menu;
});

return true;
}

private static IEnumerable<Tuple<string, CefMenuCommand>> GetMenuItems(IMenuModel model)
private static IEnumerable<Tuple<string, CefMenuCommand, bool>> GetMenuItems(IMenuModel model)
{
var list = new List<Tuple<string, CefMenuCommand>>();
for(var i = 0; i < model.Count; i++)
{
var header = model.GetLabelAt(i);
var commandId = model.GetCommandIdAt(i);
list.Add(new Tuple<string, CefMenuCommand>(header, commandId));
var isEnabled = model.IsEnabledAt(i);
yield return new Tuple<string, CefMenuCommand, bool>(header, commandId, isEnabled);
}

return list;
}
}
}

0 comments on commit d74215a

Please sign in to comment.