This tutorial explains how to display HTML in WPF applications using DotNetBrowser and Chrome Developer Tools debugging feature. This can be particularly useful if you want to improve how the appearance of your application's UI looks. 


This is what your example app UI will look like, when you compile and run this sample in the Visual Studio:



You can also watch this guide here:



Getting Started

Launch the Visual Studio (Visual Studio 2015 is used in this guide) and create a new WPF application.

Then, add DotNetBrowser via the the NuGet manager. You can easily find the DotNetBrowser package by searching in the “Browse” tab.



After you click "Install", the package will be added to your Toolbox. 


Add the Licence File

DotNetBrowser is a proprietary component, it only runs in projects containing its licence file. 

You can obtain a free 30-days evaluation licence or purchase a commercial licence for your project on the DotNetBrowser homepage


To add the licence file to the project, right-click on your project's name in the Solution Explorer and choose Add -> Existing Item. In the dialog window specify the directory, where your licence file is stored. Please note, you should set File Type to "All Files" to see the licence file.

After the file is added to the project, select it in the Solution Explorer and set "Build Action" property to "Embedded Resources".



Add two BrowserView components through XAML

Let’s set an initial window size to the whole desktop. To do this, let's include the DotNetBrowser namespace and add its components. Name the components as browserView1 and browserView2. Divide Grid into two parts with same sizes and put the created DotNetBrowser components into the created parts. Also, you could add default URL through XAML:


<Window x:Class="CreatingHtmlUI.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:wpf="clr-namespace:DotNetBrowser.WPF;assembly=DotNetBrowser"
        xmlns:local="clr-namespace:CreatingHtmlUI"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525" WindowState="Maximized">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <wpf:WPFBrowserView Name="browserView1" Grid.Column="0"/>
        <wpf:WPFBrowserView Name="browserView2" Grid.Column="1"/>
    </Grid>
</Window>


Now let’s include a web page with a style sheet to the current project and set the parameters to those files. 

Here is the source code of the web page and the style sheet (please note, that the web page doesn’t contain "OnClick" event on the "Submit" button):


UI.html

<html>
<head>
    <link rel='stylesheet' type='text/css' href='stylesheet.css'>
</head>
<body>
    <form action="#">
        <label for="login">Login</label>
        <input type="text" id="login" name="login" value="User" />
        <label for="password">Password</label>
        <input type="password" id="password" name="password" value="123456" />
        <input type="submit" value="Submit" />
    </form>
</body>
</html>


stylesheet.css

body {
  background-color: rgba(50, 60, 60, 0.85);
  color: white;
  font: 14px/1.72 'Open Sans', Arial, sans-serif;
}

form {
  height: 264px;
  width: 272px;
  margin: auto;
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
}

label {
  display: block;
  text-transform: capitalize;
  margin: 14px 0 6px;
}

input[type="text", type="password"] {
  height: 36px;
  width: 100%;
  padding: 5px 10px;
  color: #000;
  font-weight: normal;
  border: 1px solid #ccc;
  border-radius: 2px;
  -webkit-box-shadow: 0 0 0 1px #fff;
  box-shadow: 0 0 0 1px #fff;
  outline: none;
  font: 14px/1.72 'Open Sans', Arial, sans-serif;
}

input[name="password"] {
  margin-bottom: 22px;
}

input[type="text", type="password"]:focus {
  box-shadow: 0 0 0 1px #ffe5ac;
  border-color: #ffe5ac;
}

input[type="submit"] {
  width: 175px;
  height: 46px;
  background: 0 0;
  color: #fff;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
  text-align: center;
  white-space: nowrap;
  border: 2px solid #fff;
  border-radius: 4px;
  padding: 0 30px 0;
  vertical-align: middle;
  cursor: pointer;
  -webkit-transition: all .15s ease-in;
  font: 14px/1 'Open Sans', Arial, sans-serif;
}

input[type="submit"]:focus {
  background: rgba(255, 255, 255, .1);
  outline: 5px auto -webkit-focus-ring-color;
  outline-offset: -2px;
}

input[type="submit"]:active {
  background-image: none;
  outline: 0;
  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
  box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
}

input[type="submit"]:hover {
  color: #fff;
  background: rgba(255, 255, 255, .1);
}


Add a FinishLoadingFrameEvent listener for browserView1. It will help us track when a page is completely loaded.


Implement the previously designed window

Let’s initialize the primary settings of the MainWindow. We added the Chromium switch to turn on the Chrome Developer Tools remote debugging feature and set the default URL for both DotNetBrowser components. In this way we'll be able to control the web page with the style sheet:

С#

public MainWindow()
{
    BrowserPreferences.SetChromiumSwitches("--remote-debugging-port=9222");
    InitializeComponent();

    browserView1.Browser.LoadURL(System.IO.Path.GetFullPath("UI.html"));
    browserView2.Browser.LoadURL(browserView1.Browser.GetRemoteDebuggingURL());
}

VB.NET

Public Sub New()
    BrowserPreferences.SetChromiumSwitches("--remote-debugging-port=9222")
    InitializeComponent()

    browserView1.Browser.LoadURL(System.IO.Path.GetFullPath("UI.html"))
    browserView2.Browser.LoadURL(browserView1.Browser.GetRemoteDebuggingURL())
End Sub


Implement the FinishLoadingFrameEvent handler

Now we will implement the FinishLoadingFrameEvent handler. We should check whether the page is loaded completely before obtaining all HTML elements as a DOM tree. It will help us to find a Submit button and add an event listener to the OnClick event. Let’s name the event handler OnSubmitClicked:

C#

private void browserView1_FinishLoadingFrameEvent(object sender, DotNetBrowser.Events.FinishLoadingEventArgs e)
{
    if (e.IsMainFrame)
    {
        DOMDocument document = e.Browser.GetDocument();
        List<DOMNode> inputs = document.GetElementsByTagName("input");
        foreach (DOMNode node in inputs)
        {
            DOMElement element = node as DOMElement;
            if (element.GetAttribute("type").ToLower().Equals("submit"))
            {
                element.AddEventListener(DOMEventType.OnClick, OnSubmitClicked, false);
            }
        }
    }
}

VB.NET

Private Sub browserView1_FinishLoadingFrameEvent(ByVal sender As Object, ByVal e As DotNetBrowser.Events.FinishLoadingEventArgs)
    If e.IsMainFrame Then
        Dim document As DOMDocument = e.Browser.GetDocument()
        Dim inputs As List(Of DOMNode) = document.GetElementsByTagName("input")

        For Each node As DOMNode In inputs
            Dim element As DOMElement = TryCast(node, DOMElement)

            If element.GetAttribute("type").ToLower().Equals("submit") Then
                element.AddEventListener(DOMEventType.OnClick, OnSubmitClicked, False)
            End If
        Next
    End If
End Sub


Implement the OnSubmitClicked handler

This handler will read the form data and display a message with obtained values in a separate thread. Such approach allows to avoid deadlocks during call.

DotNetBrowser has a specific API to access the value property directly, not using cycles. So, we should return the web page as a DOM tree, save the obtained values to the temporary variables, and invoke the MessageBox:

C#

private void OnSubmitClicked(object sender, DOMEventArgs e)
{
    Task.Run(new Action(() =>
    {
        string login = string.Empty;
        string password = string.Empty;

        DOMDocument document = browserView1.Browser.GetDocument();

        login = ((DOMInputElement)document.GetElementById("login")).Value;
        password = ((DOMInputElement)document.GetElementById("password")).Value;

        Application.Current.Dispatcher.BeginInvoke(new Action(() =>
        {
            MessageBox.Show(this, "Login: " +
                login + "\nPassword: " + password, "Data");
        }));
    }));
}

VB.NET

Private Sub OnSubmitClicked(ByVal sender As Object, ByVal e As DOMEventArgs)
    Task.Run(New Action(Sub()
        Dim login As String = String.Empty
        Dim password As String = String.Empty
        Dim document As DOMDocument = browserView1.Browser.GetDocument()
        login = (CType(document.GetElementById("login"), DOMInputElement)).Value
        password = (CType(document.GetElementById("password"), DOMInputElement)).Value
        Application.Current.Dispatcher.BeginInvoke(New Action(Sub()
            MessageBox.Show(Me, "Login: " & login & vbLf & "Password: " & password, "Data")
        End Sub))
    End Sub))
End Sub


Compile the project and check the result

To check if the OnClick event works as expected click the Submit button. A message with the current login and password data should appear. 



You could also change values in the input fields and Submit button will return up-to-date information. In the same way you could modify the web page using Chrome Developer Tools and watch the changes taking place immediately. 

 


The source code provided in this guide is also available in the attached archive.