Skip to content

Commit d74215a

Browse files
committed
WPF Example - update MenuHandler to implement custom menu, execute command 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
1 parent e66a99d commit d74215a

File tree

1 file changed

+153
-51
lines changed

1 file changed

+153
-51
lines changed

CefSharp.Wpf.Example/Handlers/MenuHandler.cs

Lines changed: 153 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
44

55
using System;
6+
using System.Linq;
67
using System.Collections.Generic;
78
using System.Windows.Controls;
89
using System.Windows;
@@ -48,71 +49,172 @@ bool IContextMenuHandler.OnContextMenuCommand(IWebBrowser browserControl, IBrows
4849

4950
void IContextMenuHandler.OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame)
5051
{
51-
//var chromiumWebBrowser = (ChromiumWebBrowser)browserControl;
52+
var chromiumWebBrowser = (ChromiumWebBrowser)browserControl;
5253

53-
//chromiumWebBrowser.Dispatcher.Invoke(() =>
54-
//{
55-
// chromiumWebBrowser.ContextMenu = null;
56-
//});
54+
chromiumWebBrowser.Dispatcher.Invoke(() =>
55+
{
56+
chromiumWebBrowser.ContextMenu = null;
57+
});
5758
}
5859

5960
bool IContextMenuHandler.RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback)
6061
{
61-
return false;
62+
//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
63+
//return false;
64+
65+
var chromiumWebBrowser = (ChromiumWebBrowser)browserControl;
6266

63-
//var chromiumWebBrowser = (ChromiumWebBrowser)browserControl;
64-
65-
////IMenuModel is only valid in the context of this method, so need to read the values before invoking on the UI thread
66-
//var menuItems = GetMenuItems(model);
67-
68-
//chromiumWebBrowser.Dispatcher.Invoke(() =>
69-
//{
70-
// var menu = new ContextMenu
71-
// {
72-
// IsOpen = true
73-
// };
74-
75-
// RoutedEventHandler handler = null;
76-
77-
// handler = (s, e) =>
78-
// {
79-
// menu.Closed -= handler;
80-
81-
// //If the callback has been disposed then it's already been executed
82-
// //so don't call Cancel
83-
// if(!callback.IsDisposed)
84-
// {
85-
// callback.Cancel();
86-
// }
87-
// };
88-
89-
// menu.Closed += handler;
90-
91-
// foreach (var item in menuItems)
92-
// {
93-
// menu.Items.Add(new MenuItem
94-
// {
95-
// Header = item.Item1,
96-
// Command = new RelayCommand(() => { callback.Continue(item.Item2, CefEventFlags.None); })
97-
// });
98-
// }
99-
// chromiumWebBrowser.ContextMenu = menu;
100-
//});
101-
102-
//return true;
67+
//IMenuModel is only valid in the context of this method, so need to read the values before invoking on the UI thread
68+
var menuItems = GetMenuItems(model).ToList();
69+
70+
chromiumWebBrowser.Dispatcher.Invoke(() =>
71+
{
72+
var menu = new ContextMenu
73+
{
74+
IsOpen = true
75+
};
76+
77+
RoutedEventHandler handler = null;
78+
79+
handler = (s, e) =>
80+
{
81+
menu.Closed -= handler;
82+
83+
//If the callback has been disposed then it's already been executed
84+
//so don't call Cancel
85+
if (!callback.IsDisposed)
86+
{
87+
callback.Cancel();
88+
}
89+
};
90+
91+
menu.Closed += handler;
92+
93+
foreach (var item in menuItems)
94+
{
95+
if(item.Item2 == CefMenuCommand.NotFound && string.IsNullOrWhiteSpace(item.Item1))
96+
{
97+
menu.Items.Add(new Separator());
98+
continue;
99+
}
100+
101+
menu.Items.Add(new MenuItem
102+
{
103+
Header = item.Item1.Replace("&", "_"),
104+
IsEnabled = item.Item3,
105+
Command = new RelayCommand(() =>
106+
{
107+
//BUG: CEF currently not executing callbacks correctly so we manually map the commands below
108+
//the following line worked in previous versions, it doesn't now, so custom EXAMPLE below
109+
//callback.Continue(item.Item2, CefEventFlags.None);
110+
111+
//NOTE: Note all menu item options below have been tested, you can work out the rest
112+
switch (item.Item2)
113+
{
114+
case CefMenuCommand.Back:
115+
{
116+
browser.GoBack();
117+
break;
118+
}
119+
case CefMenuCommand.Forward:
120+
{
121+
browser.GoForward();
122+
break;
123+
}
124+
case CefMenuCommand.Cut:
125+
{
126+
browser.FocusedFrame.Cut();
127+
break;
128+
}
129+
case CefMenuCommand.Copy:
130+
{
131+
browser.FocusedFrame.Copy();
132+
break;
133+
}
134+
case CefMenuCommand.Paste:
135+
{
136+
browser.FocusedFrame.Paste();
137+
break;
138+
}
139+
case CefMenuCommand.Print:
140+
{
141+
browser.GetHost().Print();
142+
break;
143+
}
144+
case CefMenuCommand.ViewSource:
145+
{
146+
browser.FocusedFrame.ViewSource();
147+
break;
148+
}
149+
case CefMenuCommand.Undo:
150+
{
151+
browser.FocusedFrame.Undo();
152+
break;
153+
}
154+
case CefMenuCommand.StopLoad:
155+
{
156+
browser.StopLoad();
157+
break;
158+
}
159+
case CefMenuCommand.SelectAll:
160+
{
161+
browser.FocusedFrame.SelectAll();
162+
break;
163+
}
164+
case CefMenuCommand.Redo:
165+
{
166+
browser.FocusedFrame.Redo();
167+
break;
168+
}
169+
case CefMenuCommand.Find:
170+
{
171+
browser.GetHost().Find(0, parameters.SelectionText, true, false, false);
172+
break;
173+
}
174+
case CefMenuCommand.AddToDictionary:
175+
{
176+
browser.GetHost().AddWordToDictionary(parameters.MisspelledWord);
177+
break;
178+
}
179+
case CefMenuCommand.Reload:
180+
{
181+
browser.Reload();
182+
break;
183+
}
184+
case CefMenuCommand.ReloadNoCache:
185+
{
186+
browser.Reload(ignoreCache: true);
187+
break;
188+
}
189+
case (CefMenuCommand)26501:
190+
{
191+
browser.GetHost().ShowDevTools();
192+
break;
193+
}
194+
case (CefMenuCommand)26502:
195+
{
196+
browser.GetHost().CloseDevTools();
197+
break;
198+
}
199+
}
200+
})
201+
});
202+
}
203+
chromiumWebBrowser.ContextMenu = menu;
204+
});
205+
206+
return true;
103207
}
104208

105-
private static IEnumerable<Tuple<string, CefMenuCommand>> GetMenuItems(IMenuModel model)
209+
private static IEnumerable<Tuple<string, CefMenuCommand, bool>> GetMenuItems(IMenuModel model)
106210
{
107-
var list = new List<Tuple<string, CefMenuCommand>>();
108211
for(var i = 0; i < model.Count; i++)
109212
{
110213
var header = model.GetLabelAt(i);
111214
var commandId = model.GetCommandIdAt(i);
112-
list.Add(new Tuple<string, CefMenuCommand>(header, commandId));
215+
var isEnabled = model.IsEnabledAt(i);
216+
yield return new Tuple<string, CefMenuCommand, bool>(header, commandId, isEnabled);
113217
}
114-
115-
return list;
116218
}
117219
}
118220
}

0 commit comments

Comments
 (0)