Click here to Skip to main content
15,905,914 members
Articles / Web Development / HTML

Using HTML as UI Elements in a WinForms Application with Chrome / Chromium Embedded Framework (CEF)

Rate me:
Please Sign up or sign in to vote.
4.95/5 (116 votes)
23 Feb 2016CPOL14 min read 417K   208   51
Using HTML as UI Elements in a WinForms Application with Chrome / Chromium Embedded Framework (CEF)


It’s no secret that all the cool action is happening on the web! – It seems that every other week, a new JavaScript/HTML5 framework is born. With HTML5, the browser itself includes some great toys in the box. It is amazing what you can achieve using CSS alone these days. Factor in canvas support and WebGL and the browser is where it’s at!

Wouldn’t it be nice to harness and include some of that functionality in our WinForms application?

I have noticed more and more traditional Windows desktop applications where the UI has a HTML feel to them. Take, for example, the screenshot below of the TeamViewer application. Now granted, I'm not suggesting it’s a C#/WinForms application but the user-interface borrows heavily from HTML.

Image 1

Traditionally, of course WinForms has always come bundled with a WebBrowser control but typically this was subpar in that it didn’t supply the latest features and you was at the mercy of the version of Internet Explorer that was installed on the client machine.

The source code for this project can be found on GitHub at:


My goals for this project are to create a simple proof of concept C# WinForms application that can leverage HTML as a User Interface. From this, we should be able to have the ability to:

  • display HTML on a WinForms Application
  • call a JavaScript function from C#
  • call a C# function from JavaScript
  • pass data between C# and JavaScript in either direction
  • debug HTML/JavaScript using Chrome Dev Tools

Chromium and the Chromium Embedded Framework

To meet the goals above, I will be using the open source Chromium web browser and in particular the Chromium Embedded Framework (CEF). From their website, the Chromium Embedded Framework (CEF) is a simple framework for embedding Chromium-based browsers in other applications.

Honourable Mentions

Whilst researching embedding HTML content in a .NET app, I came across other projects which deserve a mention.



Where To Find It

Inbuilt C# Web Browser Control

This control actually can actually make calls through to JavaScript and vice-versa. The problem with this control is that you are tied to the version of Internet Explorer that is installed on the host OS and there is no runtime debugging feature.

HTML Renderer

This is a fantastic little framework. It is lightweight and includes only 2 .DLL's. However, I was unable to see if you can call JavaScript or if JavaScript can call back to C# code ( I don’t believe it includes a JS engine )

Awesomium HTML UI Engine

This looks very good with its own API. However I excluded it as it looked like it is an API on top of Chromium. For this article, I decided to investigate Chromium and the CEF framework.


GeckoFX uses the Firefox engine. This also looks good. Like Chromium, it is open-source and a full modern implementation of a browser.

Should We Do This?

This is a code-journey I wanted to make to see what can be accomplished using the Chromium project. The Jurassic park line: "we were so pre-occupied with whether we could, we didn't stop to think whether we should" applies here. You need to decide for yourself what’s right for you and what’s right for your project. - For me, I think this stuff is cool!!

Advantages of Using Chromium / CEF

The reason why I chose Chromium over the other browser engines is that first and foremost, it is a fully modern browser and when used in conjunction with CEF allows you to implement HTML5 based user interfaces with ease in a WinForms application. Chromium itself supports all the latest goodies we come to expect from a modern HTML5 browser.

  • HTML5
  • CSS3
  • Canvas
  • SVG
  • WebGL
  • Dev Tools

To see how feature complete Chromium is with regards to HTML5 and CSS3 features, we can visit:

But the main reason for choosing Chromium is that interactive between JavaScript and C# code is easy. We can call C# code from JavaScript land and vice-versa with ease.

Finally, the big feature is that Chromium includes the full debugging dev-tools shipped with Chrome. We are going to be writing JavaScript code or including JavaScript libraries with our application and the ability to debug them in-app is essential. This puts Chromium over the top.

Drawbacks of Using Chromium

Okay, so what are the drawbacks of using Chromium? Chromium is by no means lightweight. It depends on a number of DLLs that you will need to bundle with your application. But for me, this is a small price to pay for including such great abilities.

Setting Up Chromium in a WinForms Project

To include chromium in your WinForms project, please refer to the article series "Display HTML in WPF and CefSharp" by Dirkster99 and Alex Maitland. These articles will get you up and running.

You can install Cefsharp into your solution with ease by using Nuget.

Example of using Nuget to install CefSharp in your WinForms project.

Image 2

Once you have got CefSharp installed, you will notice this warning:

Image 3

Again, referring to Dirkster99 and Alex Maitland above articles, we need to specify the platform we are targeting. I will choose x86.

Example of setting up platform builds x86 and x64.

Image 4

Once these have been set, the project will build successfully.

At this point, our project includes all the functionality needed to use Chromium. Let’s now take a look at how we use this.

A Quick Tour Around the Chromium API

Adding the Chromium Web Browser Control

To add the Chromium Web Browser to the Form, simply include the following code into the Forms Load() and FormClosing() methods:

Code Snippet 1: Getting started with Chromium
private void Form1_Load(object sender, EventArgs e)
    ChromiumWebBrowser myBrowser = new ChromiumWebBrowser("" );

private void Form1_FormClosing(object sender, FormClosingEventArgs e)

Note: The function calls Cef.Initialize(); and Cef.Shutdown(); only need to be called once in the application.

This will add the Chromium browser control to the main form. In the above code, we are setting the default URL to This is what our resulting form looks like:

Screenshot of Sample Application.

Image 5

Now, let’s explore what we can do with this:

Displaying the Chromium Dev Tools

Displaying the dev tools for a page is as easy as a simple function call.

Code Snippet 2: Displaying the Chromium Dev Tools
private void buttonShowDevTools_Click(object sender, EventArgs e)

This will bring up the familiar chrome developer tools. You get access to everything and allows you to inspect the DOM and elements, debugging JavaScript code, viewing CSS styles, run commands via the console, etc.

Screenshot of Chrome DevTools

Image 6

Web developers are used to viewing the dev tools to inspect/modify code by pressing F12 key. I have added a menu option to the left system menu (see Fig 3) that allows you to view the chrome dev tools at runtime. To insert the menu option, I have created a utility class that injects the menu option.

[As a side-note, I initially attempted to catch the F12 key on the form, however even with KeyPreview set to true, it seemed Chrome caught all key strokes and the key preview was never caught by the WinForm class.]

Screenshot of Chrome Dev Tools Menu option

Image 7

To enable this menu, you make a call to the following static class in the forms Load method:


Then override the WndProc method listening for the SYSMENU_CHROME_DEV_TOOLS menu selection.

Code Snippet 3: Example of loading custom HTML
protected override void WndProc(ref Message m)
    base.WndProc(ref m);

    // Test if the About item was selected from the system menu
    if ((m.Msg == ChromeDevToolsSystemMenu.WM_SYSCOMMAND) && 
       ((int )m.WParam == ChromeDevToolsSystemMenu.SYSMENU_CHROME_DEV_TOOLS))

The code for this ChromeDevToolsSystemMenu class is included in the sample project. It is not described here as it detours from the main premise of the article.

Finding Out Which Version of Chromium You Are Using

To find out what version of chrome you are using within your application, simply navigate to this URL:


This will tell you which version of chromium you are running in addition to the version of CEF.

Screenshot of chrome version

Image 8

In my instance, I am running version 39 which was released around August 2014.

Displaying "on the fly" HTML in Chromium

Our main purpose for including Chromium in a WinForms app is to display custom HTML. To do this, you need to call LoadHtml(). This function expects:

Code Snippet 4: Example of loading custom HTML
private void buttonCustomHTML_Click(object sender, EventArgs e)
    m_chromeBrowser.LoadHtml( "Hello world" , "http://customrendering/" );

Including HTML Assets in a Visual Studio Project

I am going to organise the HTML resources/assets in the following directory structure. You can create a directory structure that suits you.

Screenshot of directory structure

Image 9

You can either create these directories directly In Visual Studio's Solution Explorer, however I find it easier to create your directory structure directly in Windows Explorer.

Then, in Visual Studio, click the "Show All Files" icon in Solution Explorer (shown below). After that, the added directories will show up in Solution Explorer. You will then need to select this directory, right click, and choose "Include in Project."

Screenshot of “Show All Files” button in Solution Explorer.

Image 10

Screenshot of “Include In Project” right-click context menu.

Image 11

Finally, in Visual Studio, make sure the "Build Action" property is set to "Content" and the "Copy to Output Directory" property is set to "Copy always".

Screenshot of setting build actions.

Image 12

This will ensure that when you build the application, it will include these files in the output directory.

At this point, we are setup with the environment. Now, let’s look at interacting with JavaScript and C#.

Interacting with JavaScript from C# and vice-versa

First, let’s create a simple data object (model) that we can use to pass between C# land and JS land. Due to lack of originality, I will create a Person object as follows:

Code Snippet 5: C# data/model class
public class Person
    public Person( string firstName, string lastName, DateTime birthDate)
        FirstName = firstName;
        LastName = lastName;
        DateOfBirth = birthDate;

    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; set; }
    public int SkillLevel { get; set; }

Next, let’s create a simple Business Logic class that JavaScript can call functions on and interact with our WinForms App. I am also using Json.NET from to serialize / de-serialize any data to / from JSON format.

There is absolutely nothing special about the class JavaScriptInteractionObj class. It’s simply a collection of functions that can be called from JavaScript land.

Code Snippet 6: C# business layer object (equivalent to server-side calls)
public class JavaScriptInteractionObj
    public Person m_theMan = null;

    public JavaScriptInteractionObj()
        m_theMan = new Person( "Bat", "Man" , DateTime .Now);

    public string SomeFunction()
        return "yippieee";

    public string GetPerson()
        var p1 = new Person( "Bruce", "Banner" , DateTime .Now );

        string json = JsonConvert.SerializeObject(p1);
        return json;

    public string ErrorFunction()
        return null;

    public string GetListOfPeople()
        List< Person> peopleList = new List< Person>();

        peopleList.Add( new Person( "Scooby", "Doo" , DateTime .Now));
        peopleList.Add( new Person( "Buggs", "Bunny" , DateTime .Now));
        peopleList.Add( new Person( "Daffy", "Duck" , DateTime .Now));
        peopleList.Add( new Person( "Fred", "Flinstone" , DateTime .Now));
        peopleList.Add( new Person( "Iron", "Man" , DateTime .Now));

        string json = JsonConvert.SerializeObject(peopleList);
        return json;

Next, we need to bind the object JavaScriptInteractionObj to the chromium web browser. We do this with the RegisterJsObject() function.

This function takes the name of the object to be accessed on the JavaScript side and the C# object itself.

Code Snippet 7: Example of Registering a C# object with JavaScript.
private void buttonRegisterCSharpObject_Click(object sender, EventArgs e)
    m_chromeBrowser.RegisterJsObject( "winformObj", new JavaScriptInteractionObj());

    string page = string.Format("{0}HTMLEmbeddedResources/html/WinformInteractionExample.html" , 

In the C# example above (Code Snippet 7) the name “winformObj” will be accessible from JavaScript such that the following JavaScript code is possible:

At this point, we have bound the object winformObj to the window of the Chromium browser. That means, the following code is possible which calls through to GetListOfPeople() in the C# land.

Code Snippet 8: Example of calling C# code from JavaScript.
function CallWinformFunc()
     var list = winformObj.getListOfPeople(); // Call C# Function
     for (var nLoopCnt = 0; nLoopCnt < list.length; nLoopCnt++) {
           var person = list[nLoopCnt];
}<button onclick="CallWinformFunc()">Test Winform Interaction</button>

In the above example, we have an HTML Button that, when clicked, calls the C# function GetListOfPeople() described in code snippet 6.

Note: One thing I have noticed is that RegisterJsObject() function should be called straight after the web browser is created. If you do not do this, the call might fail and in JavaScript land, the object will be null. #

Executing JavaScript Code from WinForms / C#

From the C# side, you can execute any ad-hoc JavaScript code or execute a function simply enough by calling ExecScriptAsync(). For example, the following code snippet will execute script directly on the browser page turning the background red:

Code Snippet 9: Example of executing JavaScript code from C#
private void buttonExecJavaScriptFromWinforms_Click(object sender, EventArgs e)
    var script = " = 'red';";


Returning Data from JavaScript land to C# / WinForms

Imagine the scenario where, from C# / WinForms land, you need to find the value of a variable or the value of a function call (to see if it succeeded or not). Using the above method, we do not get a return value from the

The frequently asked questions for CefSharp explains that this method only returns simple data types (ints, bools, string). Let’s imagine we want to execute the following ad-hoc code in snippet 10 which returns an int.

Code Snippet 10: JavaScript function we will use to call from C#
function tempFunction() {
    var w = window.innerWidth;
    var h = window.innerHeight;

    return w*h;

This is how we would execute that code using Chromium.

Code Snippet 11: Example of executing JavaScript code that returns a value from C#
private void buttonReturnDataFromJavaScript_Click(object sender, EventArgs e)
    StringBuilder sb = new StringBuilder();
    sb.AppendLine("function tempFunction() {");
    sb.AppendLine("     var w = window.innerWidth;");
    sb.AppendLine("     var h = window.innerHeight;");
    sb.AppendLine("     return w*h;");

    var task = m_chromeBrowser.EvaluateScriptAsync(sb.ToString());

    task.ContinueWith(t =>
        if (!t.IsFaulted)
            var response = t.Result;

            if ( response.Success == true )
                MessageBox.Show( response.Result.ToString() );
    }, TaskScheduler.FromCurrentSynchronizationContext());

The above example demonstrates returning a simple int value from JavaScript.

But what if we want to return a complex object from JavaScript to C#?

Well, remember that the function EvaluateScriptAsync() only returns simple data types (ints, bools, string). Therefore, if you need to return a complex object, you need to convert it to JSON first and send the object back as a string.

The following code example demonstrates returning a complex object (a person object) from JavaScript to C#.

Code Snippet 12: Example of executing JavaScript code that returns a value from C#
private void buttonReturnDataFromJavaScript2_Click(object sender, EventArgs e)
    // Step 01: create a simple html page (include jquery so we have access to json object
    StringBuilder htmlPage = new StringBuilder();
    htmlPage.AppendLine("Hello world 2");
    // Step 02: Load the Page
    m_chromeBrowser.LoadHtml(htmlPage.ToString(), "http://customrendering/");
    // Step 03: Define and Execute some ad-hoc JS that returns an object back to C#
    StringBuilder sb = new StringBuilder();
    sb.AppendLine("function tempFunction() {");
    sb.AppendLine("     // create a JS object");
    sb.AppendLine("     var person = {firstName:'John', lastName:'Maclaine', age:23, eyeColor:'blue'};");
    sb.AppendLine("     // Important: convert object to string before returning to C#");
    sb.AppendLine("     return JSON.stringify(person);");
    var task = m_chromeBrowser.EvaluateScriptAsync(sb.ToString());
    task.ContinueWith(t =>
        if (!t.IsFaulted)
            // Step 04: Recieve value from JS
            var response = t.Result;
            if (response.Success == true)
                // Use to convert to object;
    }, TaskScheduler.FromCurrentSynchronizationContext());

As you can see above, in the temporary JavaScript code, we declare a person object like so:

var person = {firstName:"John", lastName:"Maclaine", age:23, eyeColor:"blue"};

The last thing we do is to convert the object to a string using the “JSON.stringify()” method. Once back in C# land, we could use Newtonsoft JSON.NET to convert the string back to an object.

Lower Case Function Calls in JavaScript Land

One thing to look out for is the lower casing of C# method calls from JavaScript land. As you can see below, the C# class has a method named SomeFunction() that returns a sting. Notice the function starts with an uppercase "S".

If you look at the chrome-dev-tools window, you can see that when you attempt to call the function with an upper case letter, you get a Type Error (undefined function). Chrome lowercases the function names. When you call the same function but with a lower case "S", it works.

Image 13

HTML5 Demo Functionality

To scratch the surface of what’s possible, I have included a number of demos in the sample forms. Some of these HTML forms interact with WinForms. Others simply show off the features/possibilities that are available by including Chromium in your project.

I have included Bootstrap in a couple of examples to demonstrate receiving user input from HTML. In another example, I have used the charting library AmCharts to display a simple line graph. Other examples show off canvas functionality and WebGL functionality.

Image 14Image 15

Image 16Image 17


This was a brief introduction to integrating the Chromium web browser into a WinForms application to leverage HTML as a User Interface. The Chromium web browser is a complete and comprehensive modern framework for embedding such functionality. In a nutshell, the Chromium Embedded Framework is a fantastic framework for embedding an HTML5 based GUI in a WinForms application.


Wikipedia entry for Chromium Embedded Framework

Display HTML in WPF and CefSharp by Dirkster99 and Alex Maitland

A good discussion on Stack Overflow on the topic of replacing .NET WebBrowser control with a better browser. A lot of alternatives are discussed.

CefSharp FAQ

.NET Wrapper for the alternative Awesomium, Web-Browser Framework

WebGL Demo

Canvas bubbles

Bootstrap form validation example.

Animated GIF's in this document were created using GifCam

Chromium Dependencies

After including Chromium in your project, you will find a lot more DLLs in your project folder. This page: describes these files and I have included them here for completeness.




CEF core library



Unicode support



Localized resources

locales/ directory

Contains localized strings for WebKit UI controls.
A .pak file is loaded from this folder based on the CefSettings.locale value.

Only configured locales need to be distributed. If no locale is configured the default locale of "en-US" will be used.

Locale file loading can be disabled completely using CefSettings.pack_loading_disabled. The locales folder path can be customized using CefSettings.locales_dir_path.

Other resources

cef_200_percent.pak devtools_resources.pak

Contains WebKit image and inspector resources. Pack file loading can be:

  • Disabled completely using CefSettings.pack_loading_disabled.
  • The resources directory path can be customized using CefSettings.resources_dir_path.

FFmpeg audio and video support


Without this component, HTML5 audio and video will not function.

PDF support


Without this component, printing will not function.

Angle and Direct3D support

d3dcompiler_43.dll (required for Windows XP)
d3dcompiler_47.dll (required for Windows Vista and newer)

Without these components, HTML5 accelerated content like 2D canvas, 3D CSS and WebGL will not function.

Windows Vista 64-bit sandbox support (32-bit distributions only)


Without this component, the 32-bit build of CEF will not run on 64-bit Vista machines with the sandbox enabled.


If / when you see any errors, or have ideas for better ways of doing something, then please feel free to comment and communicate back. Feedback is always welcome!


24-02-206 - Github Project updates & fixes provided courtesy of Alex Maitland

- Upgrade to CefSharp 47.0.2
- Remove packages and bin folders
- Remove AnyCpu target from solution 
- Show DevTools is now an extension method that comes from the CefSharp namespace
- Remove empty folders from project
- Fix up bootstrap example urls - incorrect path
- Cef.Initialize is now called by default in ChromiumWebBrowser 
- Remove Cef.Shutdown call - it will be automatically called when exiting the application


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Written By
United Kingdom United Kingdom
Software development is like a treadmill for your brain. We are constantly learning and trying to "keep up" with the ever changing landscape. I am a just another software developer out there enjoying the journey.

My Blog:

Comments and Discussions

QuestionHow to change ControlTab title to site's custom head? Pin
HkWorker22-Jun-20 4:39
HkWorker22-Jun-20 4:39 
QuestionMissing JS code? Pin
Roryap8-Jan-19 5:11
Roryap8-Jan-19 5:11 
QuestionCan I implement IDialogHandler to set defaultFilePaht Pin
shiko_engin15-May-17 0:50
shiko_engin15-May-17 0:50 
QuestionOpen multiple ChromiumWebBrowsers Pin
LiQuick24-Jan-17 21:51
LiQuick24-Jan-17 21:51 
QuestionAuthentication dialog Pin
Ganesh Ramanathan15-Jun-16 5:53
Ganesh Ramanathan15-Jun-16 5:53 
Questionhow do i give it permission for gps? Pin
Member 1248155724-Apr-16 13:22
Member 1248155724-Apr-16 13:22 
AnswerRe: how do i give it permission for gps? Pin
Ocean Airdrop28-Apr-16 2:39
Ocean Airdrop28-Apr-16 2:39 
PraiseThanks Pin
Member 1097080828-Feb-16 21:53
professionalMember 1097080828-Feb-16 21:53 
GeneralRe: Thanks Pin
Ocean Airdrop1-Mar-16 7:11
Ocean Airdrop1-Mar-16 7:11 
PraiseIt is a very good option to protect the injection by devtools Pin
CMEDINAS26-Feb-16 6:18
CMEDINAS26-Feb-16 6:18 
GeneralRe: It is a very good option to protect the injection by devtools Pin
Ocean Airdrop26-Feb-16 21:04
Ocean Airdrop26-Feb-16 21:04 
GeneralRe: It is a very good option to protect the injection by devtools Pin
CMEDINAS11-Mar-16 4:21
CMEDINAS11-Mar-16 4:21 
GeneralRe: It is a very good option to protect the injection by devtools Pin
Ocean Airdrop28-Apr-16 2:49
Ocean Airdrop28-Apr-16 2:49 
GeneralMy vote of 5 Pin
MB Seifollahi25-Feb-16 20:41
professionalMB Seifollahi25-Feb-16 20:41 
GeneralRe: My vote of 5 Pin
Ocean Airdrop26-Feb-16 20:55
Ocean Airdrop26-Feb-16 20:55 
QuestionAmazing Pin
Member 1066771225-Feb-16 13:49
Member 1066771225-Feb-16 13:49 
AnswerRe: Amazing Pin
Ocean Airdrop26-Feb-16 20:55
Ocean Airdrop26-Feb-16 20:55 
QuestionFabulous Work! Pin
DumpsterJuice25-Feb-16 7:50
DumpsterJuice25-Feb-16 7:50 
AnswerRe: Fabulous Work! Pin
Ocean Airdrop26-Feb-16 20:53
Ocean Airdrop26-Feb-16 20:53 
QuestionA few problems Pin
Andy Hoffmeyer17-Feb-16 2:37
Andy Hoffmeyer17-Feb-16 2:37 
AnswerRe: A few problems Pin
Ocean Airdrop17-Feb-16 5:32
Ocean Airdrop17-Feb-16 5:32 
QuestionHow is this working with Selenium Pin
ndaian_00818-Jan-16 7:18
ndaian_00818-Jan-16 7:18 
AnswerRe: How is this working with Selenium Pin
Ocean Airdrop23-Jan-16 2:10
Ocean Airdrop23-Jan-16 2:10 
QuestionHow to disable the Right Click function Pin
Member 120422766-Dec-15 23:03
Member 120422766-Dec-15 23:03 
AnswerRe: How to disable the Right Click function Pin
Ocean Airdrop23-Jan-16 2:26
Ocean Airdrop23-Jan-16 2:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.