0

CEFSharp was contacted directly and told me to post this here. So here goes:

I was curious as I made a clone of the solution CEF gives here: https://github.com/cefsharp/CefSharp. I am seeing all invocation of the context menu is done in code behind with implementing the 'IContextMenuHandler'. Is there any way to invoke the Context Menu directly in the XAML like:

<cefSharp:ChromiumWebBrowser x:Name="browser" Address="http://www.google.com">
 <FrameworkElement.ContextMenu>
  <ContextMenu >
    <MenuItem Header="Run It" Command="{Binding CommandRun}" / >
  </ContextMenu >
 </FrameworkElement.ContextMenu>
</cefSharp:ChromiumWebBrowser>

?

I was curious as in the code example it is all commented out in this class of xaml:

CefSharp.Wpf.Example/Views/BrowserTabView.xaml

lines 437-467 show this
<!--<FrameworkElement.ContextMenu>
                    <ContextMenu>
                        <MenuItem Header="Back"
                                  Command="{Binding WebBrowser.BackCommand}" />
                        <MenuItem Header="Forward"
                                  Command="{Binding WebBrowser.ForwardCommand}" />
                        <Separator />
                        <MenuItem Header="Print ..."
                                  Command="{Binding WebBrowser.PrintCommand}" />
                        <Separator />
                        <MenuItem Header="Zoom In"
                                  Command="{Binding WebBrowser.ZoomInCommand}" />
                        <MenuItem Header="Zoom Out"
                                  Command="{Binding WebBrowser.ZoomOutCommand}" />
                        <MenuItem Header="Zoom Reset"
                                  Command="{Binding WebBrowser.ZoomResetCommand}" />
                        <Separator />
                        <MenuItem Header="Show DevTools"
                                  Command="{Binding ShowDevToolsCommand}" />
                        <MenuItem Header="Close DevTools"
                                  Command="{Binding CloseDevToolsCommand}" />
                        <Separator />
                        <MenuItem Header="View Source"
                                  Command="{Binding WebBrowser.ViewSourceCommand}" />
                        <MenuItem Header="Cut" Command="{Binding WebBrowser.CutCommand}"/>
                        <MenuItem Header="Copy" Command="{Binding WebBrowser.CopyCommand}"/>
                        <MenuItem Header="Paste" Command="{Binding WebBrowser.PasteCommand}"/>
                    </ContextMenu>
                </FrameworkElement.ContextMenu>-->

I may have missed a section that shows how to implement it. Please let me know if any knows how you can implement your ChromiumWebBrowser in WPF with all the code directly in the XAML following traditional MVVM Bindings to ICommand arguments to be executed in the ViewModel that is bound to the View that holds the ChromiumWebBrowser.

What I have done thus far is create a class that implements IContextMenuHandler like so:

public class BrowserContextMenuHandler : IContextMenuHandler
{
    private Action<IMenuModel> _menu;
    private Func<CefMenuCommand, bool> _menuCommands;

    public BrowserContextMenuHandler(Action<IMenuModel> menu, Func<CefMenuCommand, bool> menuCommands)
    {
        _menu = menu;
        _menuCommands = menuCommands;
    }

    public void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model) =>
        _menu(model);

    public bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags) => _menuCommands(commandId);

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

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

    public bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback) => false;
}

Then to get it to work I would do something like:

XAML:

 <cefSharp:ChromiumWebBrowser Grid.Row="1" x:Name="browser" Address="http://www.google.com">

Code Behind constructor:

 private MainWindowViewModel _vm;

 public MainWindow()
    {
        InitializeComponent();
        _vm = new MainWindowViewModel();
        DataContext = _vm;
        browser.MenuHandler = new BrowserContextMenuHandler(menu =>
            {
                menu.Clear();
                menu.AddItem((CefMenuCommand)26501, "Run Test for VM");
                menu.AddSeparator();
                menu.AddItem((CefMenuCommand)26502, "Hello");
            },
            commandId =>
            {
                if (commandId == (CefMenuCommand)26501)
                    _vm.CommandRun.Execute(null);

                if (commandId == (CefMenuCommand)26502)
                    MessageBox.Show("Hello there");

                return true;
            });
    }

And then I can invoke VM commands with a delayed action till menu items are clicked. This does all work and feel free to play with my Repo: https://github.com/djangojazz/CefSharpChromiumWebBrowser. However this is kind of messy and I was hoping I could just do this in xaml directly. Does anyone know? I was thinking I could at least Add a 'CefSharp.WebBrowser' property to my VM and potentially invoke all of the Context Menu directly there(have not tried that yet as of writing this). That could put more of the burden on the VM.

Thanks

amaitland
  • 4,073
  • 3
  • 25
  • 63
djangojazz
  • 14,131
  • 10
  • 56
  • 94
  • You can bind the browser to your viewmodel, look closer at the example https://github.com/cefsharp/CefSharp/blob/master/CefSharp.Wpf.Example/ViewModels/BrowserTabViewModel.cs#L54 – amaitland Jul 06 '18 at 21:45
  • The dynamic nature of the context menu does not lend itself to binding in xaml in my opinion. You are welcome to analyze the source and submit a PR to make the control more mvvm friendly. For reference the upstream API doc is http://magpcss.org/ceforum/apidocs3/index-all.html – amaitland Jul 06 '18 at 21:47
  • Binding the browser with != ... items bound directly... . At a certain point if you don't offer the xaml and just build up an object dynamically in the ViewModel replacing the XAML, you essentially are making the VM operate like the View inside of the VM. – djangojazz Jul 06 '18 at 22:10
  • Not everything fits nicely in a neat little mvvm box, at it's core CefSharp is a wrapper around the Chromium embedded framework. You are welcome to get involved and make the control more mvvm friendly – amaitland Jul 06 '18 at 23:10
  • You can implement a pure xaml context menu, you just won't get any context specific menu items. – amaitland Jul 06 '18 at 23:11

0 Answers0