【WinForm】用ChromiumWeb内核开发一个自用的浏览器

需要一个自用的浏览器,最好是用ChromiumWeb内核快速开发,下面使用Visual Studio 开发工具新建一个桌面程序WinForm项目,实现一个基本上网浏览功能使用即可

  1. 选择项目,鼠标右键选择打开Nuget程序包管理器,插件上找CefSharp 安装好,如下图所示,由于此插件只支持x64或x86的处理器,若编译运行不了就设置编译为x64或x86即可
    在这里插入图片描述

  2. 把窗口布局弄成大致如下图所示
    在这里插入图片描述

窗口有包括返回、刷新、关闭、(搜索)访问、打印、开发者工具按钮等,还有输入框
用的两大组件: TabControl,ToolStrip,里面在包含一些子组件Label,TextBox,Button…

  1. 开始写代码,处理初始化,在组件TabControl中添加浏览器组件,代码如下
public partial class Form2 : Form
{
    ChromiumWebBrowser mChromium;

    public Form2()
    {
        InitializeComponent();
        InitializeChromium();//调用初始化浏览器方法
    }

    private void InitializeChromium()
    {
        var tempPath = Path.Combine(System.Environment.GetEnvironmentVariable("TMP"), "mybrowser");//用户临时目录
        if (Directory.Exists(tempPath)!=true)
        {
            Directory.CreateDirectory(tempPath);
        }
        var settings = new CefSettings();
        settings.UserDataPath = Path.Combine(tempPath, "userData");
        settings.CachePath = Path.Combine(tempPath, "cache");//缓存路径
        settings.LogFile = Path.Combine(tempPath, "logFile");//日志文件
        //settings.ResourcesDirPath = Path.Combine(tempPath, "resDir");
        //settings.No = true;
        //settings.Locale = "zh-CN";
        //settings.LocalesDirPath = Path.Combine(tempPath, "localesDir");
        settings.AcceptLanguageList = "zh-CN,zh;q=0.8";//引擎语言
        settings.PersistSessionCookies = true;
        settings.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36";
        CefSharp.Cef.Initialize(settings, true);

        if (CefSharpSettings.ShutdownOnExit)
        {
            Application.ApplicationExit += OnApplicationExit;//设置退出时调用的方法事件
        }

        tabControl1.TabPages[0].Text = "主页";
        tabControl1.TabPages[1].Text = "loading...";

        var url = toolStripTextBox1.Text;//使用默认地址
        OpenNewBrowser(tabControl1.SelectedIndex + 1, url);//打开浏览器方法

    }
    
    private void OnApplicationExit(object sender, EventArgs e)
    {
        Cef.Shutdown();//释放资源
    }
}

4.打开浏览器方法,实现细节很多,代码如下

public partial class Form2 : Form
{
    
    private void OpenNewBrowser(int tabIndex, string url)
    {
        var browser = new ChromiumWebBrowser(url);
        //var browser = new ChromiumWebBrowser();
        //标题改变事件
        browser.TitleChanged += new EventHandler<TitleChangedEventArgs>(delegate (object sender, TitleChangedEventArgs args) {
            this.Invoke(new Action(delegate ()
            {
                if (tabIndex < tabControl1.TabPages.Count)
                {
                    tabControl1.TabPages[tabIndex].Text = args.Title;
                }
            }));
        });
		//加载错误处理事件
        browser.LoadError += new EventHandler<LoadErrorEventArgs>(delegate (object sender, LoadErrorEventArgs args) {
            var errorTemplate = string.Format(HtmlTemplate.ErrorPage3, args.ErrorText, args.ErrorCode.ToString(), args.FailedUrl);
            browser.LoadHtml(errorTemplate);
        });
		//状态改变事件 例如 用户鼠标指向的链接
        browser.StatusMessage += new EventHandler<StatusMessageEventArgs>((object sender, StatusMessageEventArgs args) => {
            this.Invoke(new Action(() => {
                if (string.IsNullOrEmpty(args.Value))
                {
                    label1.Text = "";
                    return;
                }
                label1.Text = string.Format("预点击链接:{0}", args.Value);
            }));
        });
		//浏览器地址跳转 改变事件
        browser.AddressChanged += new EventHandler<AddressChangedEventArgs>((object sender, AddressChangedEventArgs args) => {
            this.Invoke(new Action(() => {
                if (toolStripTextBox1.Focused != false) return;
                toolStripTextBox1.Text = args.Address;
            }));
        });
		//对打开新页面时做处理
        browser.LifeSpanHandler = new CefLifeSpanHandler((IWindowInfo windowInfo, String targetUrl) => {
            this.Invoke(new Action(() => {
                tabControl1.TabPages.Add(targetUrl);
                OpenNewBrowser(tabControl1.TabPages.Count - 1, targetUrl);
            }));
        });
		//下载事件
        browser.DownloadHandler = new CefDownloadHandler("_007",(String targetUrl, String id) => {
            var res = MessageBox.Show(String.Format("下载完成,是否打开文件位置?\n{0}", targetUrl), "系统提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Information);
            if (res == DialogResult.OK)
            {
                openExplorer(targetUrl);//打开文件位置
            }
            return true;
        });
		//鼠标右键菜单
        browser.MenuHandler = new CefMenuHandler();

        browser.Dock = System.Windows.Forms.DockStyle.Fill;
		//添加浏览器组件
        tabControl1.TabPages[tabIndex].Controls.Add(browser);

        if (tabControl1.SelectedIndex != tabIndex)
        {
            tabControl1.SelectedIndex = tabIndex;
        }

    }

}
  1. 浏览器的一些处理事件有调用了自定义的类,分别是CefLifeSpanHandlerCefDownloadHandlerCefMenuHandler,贴上代码如下
  • 处理打开新页面(_blank)
public class CefLifeSpanHandler : CefSharp.ILifeSpanHandler
{
    Action<IWindowInfo, String> onNewWindow;

    public CefLifeSpanHandler(Action<IWindowInfo, String> OnNewWindow)
    {
        this.onNewWindow = OnNewWindow;
    }

    public bool DoClose(IWebBrowser chromiumWebBrowser, IBrowser browser)
    {
        if (browser.IsDisposed || browser.IsPopup)
        {
            return false;
        }

        return true;
    }

    public void OnAfterCreated(IWebBrowser chromiumWebBrowser, IBrowser browser)
    {
        //throw new NotImplementedException();
    }

    public void OnBeforeClose(IWebBrowser chromiumWebBrowser, IBrowser browser)
    {
        //throw new NotImplementedException();
    }

    public bool OnBeforePopup(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser)
    {
        if (onNewWindow != null)
        {
            onNewWindow(windowInfo, targetUrl);//调用传入的方法
        }

        newBrowser = null;
        return true;
    }
}
  • 处理下载文件的
public class CefDownloadHandler : CefSharp.IDownloadHandler
{
    Func<string, string, bool> calDownload;
    String id;

    public CefDownloadHandler(String id, Func<string, string, bool> calDownload)
    {
        this.calDownload = calDownload;
        this.id = id;
    }

    public bool CanDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, string url, string requestMethod)
    {
        return true;
    }

    public void OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback)
    {
        callback.Continue(downloadItem.Url, true);
    }

    public void OnDownloadUpdated(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback)
    {
        if (downloadItem.IsComplete != true) return;
        //下载成功时调用
        calDownload(downloadItem.FullPath, id);
    }
}
  • 处理弹出右键菜单的
//封装命令集的类 如枚举
class MenuCommand
{
    public static CefMenuCommand CopySelectText = (CefMenuCommand) 1000011;
    public static CefMenuCommand DownloadSave = (CefMenuCommand) 1000012;
    public static CefMenuCommand CopyLink = (CefMenuCommand) 1000013;
}

public class CefMenuHandler : CefSharp.IContextMenuHandler
{
    public void OnBeforeContextMenu(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model)
    {
        if (model.Count>0)
        {
            model.AddSeparator();
        }
        if (!String.IsNullOrEmpty(parameters.SourceUrl))
        {
            if (parameters.MediaType != ContextMenuMediaType.None)
            {
                model.AddItem(MenuCommand.DownloadSave, "save file");//保存文件
            }
        }
        else if (!String.IsNullOrEmpty(parameters.LinkUrl))
        {
            var reg = new Regex("\\.\\w+$");
            if (reg.IsMatch(parameters.LinkUrl))
            {
                model.AddItem(MenuCommand.DownloadSave, "save file");//保存文件
            } else
            {
                model.AddItem(MenuCommand.CopyLink, "copy link");//复制链接
            }
            return;
        }
        
    }

    public bool OnContextMenuCommand(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags)
    {
        if (commandId == MenuCommand.CopyLink)
        {
            Clipboard.SetText(parameters.SelectionText);
        }
        else if (commandId == MenuCommand.DownloadSave)
        {
            var cbrowser = chromiumWebBrowser as ChromiumWebBrowser;
            if (!string.IsNullOrEmpty(parameters.SourceUrl))
            {
                cbrowser.StartDownload(parameters.SourceUrl);
                return true;
            }
            else if (!string.IsNullOrEmpty(parameters.LinkUrl))
            {
                cbrowser.StartDownload(parameters.LinkUrl);
                return true;
            }
        }
        return false;
    }

    public void OnContextMenuDismissed(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame)
    {
        var cbrowser = chromiumWebBrowser as ChromiumWebBrowser;
        cbrowser.Invoke(new Action(() =>
        {
            cbrowser.ContextMenu = null;
        }));
    }

    public bool RunContextMenu(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback)
    {
        return false; //可弹出
    }
}
  1. 最后,补充完善一下用户的交互逻辑,如点击按钮有返回、刷新、关闭、访问等,代码如下
public partial class Form2 : Form
{
    ChromiumWebBrowser mChromium;

    public Form2()
    {
        InitializeComponent();
        InitializeChromium();
    }

    private void InitializeChromium()
    {
		//...
    }

    private void OpenNewBrowser(int tabIndex, string url)
    {
		//...
    }

    /// <summary>
    /// 打开文件位置
    /// </summary>
    /// <param name="filePath">文件路径</param>
    private void openExplorer(string filePath)
    {
        filePath = filePath.Replace("/", "\\");//此处的路径都要求是右斜杠的’\’才能定位打开
        System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo("Explorer.exe");
        psi.Arguments = "/e,/select," + filePath;
        System.Diagnostics.Process.Start(psi);
    }

    private void Form2_Load(object sender, EventArgs e)
    {

    }

    private void OnApplicationExit(object sender, EventArgs e)
    {
		//...
    }

    private void Form2_FormClosing(object sender, FormClosingEventArgs e)
    {
        try
        {
            for(int i=1; i< tabControl1.Controls.Count; i++)
            {
                var control = tabControl1.Controls[i];
                if (control.Controls.Count <= 0) continue;
                CloseBrowser(control.Controls[0] as ChromiumWebBrowser);
                control.Controls.Clear();
            }
            //KillBrowserSubprocess();//杀死进程
            //if (isShutdown) CefSharp.Cef.Shutdown();//释放资源
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString(), "清理CefSharp.BrowserSubprocess异常");
        }
    }

    private void CloseBrowser(ChromiumWebBrowser cefBrowser)
    {
        cefBrowser.JavascriptObjectRepository.UnRegisterAll();//解绑对象 高版本才有

        cefBrowser.CloseDevTools();//关闭调试
        cefBrowser.GetBrowser().CloseBrowser(true);//关闭浏览器
        cefBrowser.Dispose();
    }
	//刷新按钮点击
    private void toolStripButton2_Click(object sender, EventArgs e)
    {
        var browser = GetCurrorBrowser();
        if (browser == null) return;

        browser.Refresh();
    }
	//获取当前选项卡下的浏览窗口
    private ChromiumWebBrowser GetCurrorBrowser()
    {
        if (tabControl1.SelectedIndex < 1) return null;
        return tabControl1.SelectedTab.Controls[0] as ChromiumWebBrowser;
    }
	//返回按钮点击
    private void toolStripButton1_Click(object sender, EventArgs e)
    {
        var browser = GetCurrorBrowser();
        if (browser == null) return;

        browser.Back();
    }
	//关闭当前选项卡
    private void toolStripButton3_Click(object sender, EventArgs e)
    {
        var browser = GetCurrorBrowser();
        if (browser == null) return;
        //TODO: 有Bug,当它删掉两个就会挂掉 错误未知  需要优化
        CloseBrowser(browser);

         if (tabControl1.SelectedIndex > 0)
         {
             tabControl1.SelectedIndex--;
             tabControl1.TabPages.RemoveAt(tabControl1.SelectedIndex + 1);
         }
    }
	//地址输入框 的 访问按钮点击
    private void toolStripButton4_Click(object sender, EventArgs e)
    {
        var browser = GetCurrorBrowser();
        if (browser == null) return;

        var text = toolStripTextBox1.Text.Trim();
        if (string.IsNullOrEmpty(text)) return;

        if (!text.StartsWith("http"))
        {
            if (!text.StartsWith("//"))
            {
                text = "//" + text;
            }
            if (!text.StartsWith(":"))
            {
                text = ":" + text;
            }
        }

        if (toolStripTextBox1.Text.Contains(text))
        {
            toolStripTextBox1.Text = text;
        }

        browser.LoadUrlAsync(text);
    }

	//主页 百度一下 按钮点击
    private void button1_Click(object sender, EventArgs e)
    {
        var text = textBox1.Text.Trim();
        if (string.IsNullOrEmpty(text)) return;

        var url = "https://www.baidu.com/s?ie=utf-8&wd=" + HttpUtility.UrlEncode(text);
        tabControl1.TabPages.Add(url);
        OpenNewBrowser(tabControl1.TabPages.Count-1, url);
    }
	//打印按钮点击
    private void toolStripButton5_Click(object sender, EventArgs e)
    {
        var browser = GetCurrorBrowser();
        if (browser == null) return;

        browser.Print();
    }
	//打开开发者工具 控制台 调试
    private void toolStripButton6_Click(object sender, EventArgs e)
    {
        var browser = GetCurrorBrowser();
        if (browser == null) return;

        browser.ShowDevTools();
    }
}
  1. 忘了还有HtmlTemplate类,是一个基本网页模板代码,还是贴出来参考,代码如下
public class HtmlTemplate
{
    internal static readonly string ErrorPage3 = "<!doctype html>" +
        "<html>" +
        "<head>" +
        "<meta charset=\"utf-8\" />" +
        "<title>Error Page</title>" +
        "</head>" +
        "<body style=\"padding: 20px; background-color: #0000ff; color: #ffffff;\">" +
        "<h1>:(</h1>" +
        "<h3>{0}</h3>" +
        "<hr/>" +
        "<p>错误信息:{1}</p>" +
        "<p>错误地址:<a style=\"opacity: 0.5; color: chartreuse; text-decoration: none;\" href=\"javascript:return false;\">{2}</a></p>" +
        "</body>" +
        "</html>";
}

8.就记录到这了,收工!

该浏览器只实现后,经过测试如下:

  1. 支持HTML5标准
  2. 可下载文件
  3. 可播放视频MP4 在线播放其它视频格式暂时不支持
  4. 可自定义右键菜单
  5. 已禁止播放flash插件
  6. 其它…脚本与浏览器通信…未测

在这里插入图片描述

  • 6
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
实现一个浏览器小工具,可以使用C# WinFormWebBrowser控件。下面是一个简单的实现过程和代码示例。 1. 创建一个新的WinForm应用程序项目,并在窗体上添加一个WebBrowser控件。 2. 在窗体的构造函数中,设置WebBrowser控件的Dock属性为Fill,这样它将填充整个窗体。 ```csharp public Form1() { InitializeComponent(); webBrowser1.Dock = DockStyle.Fill; } ``` 3. 在窗体上添加一个文本框和一个按钮,用于输入和导航到URL。 4. 在按钮的Click事件中,使用WebBrowser控件的Navigate方法导航到指定的URL。 ```csharp private void button1_Click(object sender, EventArgs e) { string url = textBox1.Text; if (!url.StartsWith("http://") && !url.StartsWith("https://")) { url = "http://" + url; } webBrowser1.Navigate(new Uri(url)); } ``` 5. 可以添加其他功能,如前进、后退、刷新等按钮,这些功能可以使用WebBrowser控件的各种方法实现。 完整代码示例: ```csharp public partial class Form1 : Form { public Form1() { InitializeComponent(); webBrowser1.Dock = DockStyle.Fill; } private void button1_Click(object sender, EventArgs e) { string url = textBox1.Text; if (!url.StartsWith("http://") && !url.StartsWith("https://")) { url = "http://" + url; } webBrowser1.Navigate(new Uri(url)); } private void button2_Click(object sender, EventArgs e) { if (webBrowser1.CanGoBack) { webBrowser1.GoBack(); } } private void button3_Click(object sender, EventArgs e) { if (webBrowser1.CanGoForward) { webBrowser1.GoForward(); } } private void button4_Click(object sender, EventArgs e) { webBrowser1.Refresh(); } } ```
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TA远方

谢谢!收到你的爱╮(╯▽╰)╭

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值