Search code examples
c#winformshandlercefsharpmousedown

CefSharp browser isn't firing any Mouse-Events in C# WinForms app


So my end goal is to detect when the user has pressed the left mouse button on a webpage (any element like a button, image and so on). With this I'd run some javascript to get the proper html data I need based on where the user has clicked.

Without taking it any further, I'm already stuck at the first part where my browser control doesn't seem to fire any events related to the mouse. However it does seem to provide handlers like MouseDown, MouseEnter, MouseClick and so on but doesn't seem to fire any of them.

I've tried setting up a simple WinForms test project with a textbox control and a button. The browser is added manually like so:

using CefSharp;
using CefSharp.WinForms;
using System;
using System.Drawing;
using System.Windows.Forms;

namespace myProject
{
    public partial class Form1 : Form
    {
        public ChromiumWebBrowser browser;
        private string myUrl = "https://www.google.com/";
        
        public Form1()
        {
            InitializeComponent();
            InitializeChromium();

            browser.MouseDown += ChromeBrowser_MouseDown;
        }

        public void InitializeChromium()
        {
            browser = new ChromiumWebBrowser(myUrl);

            browser.Location = new Point(26, 59);
            browser.Size = new Size(988,566);
            browser.Dock = DockStyle.None;
            this.Controls.Add(browser);

        }

        private void ChromeBrowser_MouseDown(object sender, MouseEventArgs e)
        {
            //This handler never gets fired!!!

            if (e.Button == MouseButtons.Left)
            {
                //get value from html element
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            browser.Load(addressBar.Text);
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            Cef.Shutdown();
        }
    }
}

I've tried looking through a bunch of websites including the official Documentation on Handlers, but can't seem to figure out if I have to do more complex stuff to listen and act on mouse events or that I should be able to implement it just like any other event.

Can anybody help me fix my MouseDown event handler?


Solution

  • It took a while, but finally got around to posting my own answer! Hopefully others will find this useful.

    Thanks to @amaitland for pointing out in the comments that CefSharp browser will not fire any keyboard or Mouse events and also for posting this link which ultimately led me to my answer.

    The workaround is to inject some Javascript (when the browser is done loading a frame) which will then listen to & detect any mouse/keyboard events. Upon fire, your Javascript then calls CefSharp.PostMessage(data); which notifies the CefSharp browser on its own thread and returns any useful data (depending on your script).

    Initializing Event Handlers

    Adding browser.JavascriptMessageReceived += Browser_JavascriptMessageReceived; & browser.FrameLoadEnd += Browser_FrameLoadEnd; to your initialization will activate the necessary hooks, like so (following my original example):

    public void InitializeChromium()
            {
                browser = new ChromiumWebBrowser(myUrl);
    
                browser.Location = new Point(26, 59);
                browser.Size = new Size(988,566);
                browser.Dock = DockStyle.None;
                this.Controls.Add(browser);
    
                browser.JavascriptMessageReceived += Browser_JavascriptMessageReceived;
                browser.FrameLoadEnd += Browser_FrameLoadEnd;
            }
    

    Implementing Event Handlers

    Now all that's left to do is to implement these event handlers by adding the following:

            private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e)
            {
                if (e.Frame.IsMain)
                {
                    browser.ExecuteScriptAsync(@"
                        document.addEventListener('click', function(e) {
                            var parent = e.target.parentElement;
    
                            // run some validation with if(){..}
                            // some more javascript
    
                            CefSharp.PostMessage(parent.outerHTML);
                        }, false);
                    ");
                }
            }
    
            private void Browser_JavascriptMessageReceived(object sender, JavascriptMessageReceivedEventArgs e)
            {
                if (e.Message != null)
                {
                    // Extract data from e.Message.toString() and use delegates/callbacks/Invokes 
                    // to reference the main UI thread for updating the necessary controls.
                }
            }
    
    

    Once the webpage/frame is loaded, the Browser_FrameLoadEnd handler will run which will then inject your custom Javascript. The Browser_JavascriptMessageReceived handler will only be fired when CefSharp.PostMessage(data); is called from your Javascript.

    Also, instead of using document.body.onmouseup in the javascript as suggested by this link, I opted to go for document.addEventListener('click', function(e) in order to get the targeted/clicked element along with it. In my case this was crucial in order to get the necessary data which belonged to the parentElement of this clicked element. (keep in mind that my actual case is meant for a different website than google as suggested by my example)

    Full Example Code

    The full extended example code would look something like this:

    using CefSharp;
    using CefSharp.WinForms;
    using System;
    using System.Drawing;
    using System.Windows.Forms;
    
    namespace myProject
    {
        public partial class Form1 : Form
        {
            public ChromiumWebBrowser browser;
            private string myUrl = "https://www.google.com/";
            
            public Form1()
            {
                InitializeComponent();
                InitializeChromium();
            }
    
            public void InitializeChromium()
            {
                browser = new ChromiumWebBrowser(myUrl);
    
                browser.Location = new Point(26, 59);
                browser.Size = new Size(988,566);
                browser.Dock = DockStyle.None;
                this.Controls.Add(browser);
    
                browser.JavascriptMessageReceived += Browser_JavascriptMessageReceived;
                browser.FrameLoadEnd += Browser_FrameLoadEnd;
            }
    
            private void Browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e)
            {
                if (e.Frame.IsMain)
                {
                    browser.ExecuteScriptAsync(@"
                        document.addEventListener('click', function(e) {
                            var parent = e.target.parentElement;
    
                            // run some validation with if(){..}
                            // some more javascript
    
                            CefSharp.PostMessage(parent.outerHTML);
                        }, false);
                    ");
                }
            }
    
            private void Browser_JavascriptMessageReceived(object sender, JavascriptMessageReceivedEventArgs e)
            {
                if (e.Message != null)
                {
                    // Extract data from e.Message.toString() and use delegates/callbacks/Invokes 
                    // to reference the main UI thread for updating the necessary controls.
                }
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                browser.Load(addressBar.Text);
            }
    
            private void Form1_FormClosing(object sender, FormClosingEventArgs e)
            {
                Cef.Shutdown();
            }
        }
    }