Skip to content

WebView

Introduction

WebView is a control that can be used to display a web page on a chart or an indicator output window.

WebView is based on Microsoft Edge WebView2. As a result, it is fully compatible with all modern web standards.

You can also execute JavaScript code on a page and get the results via WebView.

Note

WebView only works on .NET 6 or later indicators and cBots.

How to Use WebView in cTrader

As an example, we will display the cTrader.com website inside a window and the main chart.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using cAlgo.API;

namespace cAlgo.Robots
{
    [Indicator(AccessRights = AccessRights.None, IsOverlay = true)]
    public class WebViewOnWindow : Indicator
    {
        private WebView _webView;

        [Parameter("On Window?", DefaultValue = false)]
        public bool OnWindow { get; set; }

        protected override void Initialize()
        {
            _webView = new WebView
            {
                DefaultBackgroundColor = Color.Red
            };

            _webView.Loaded += OnWebViewLoaded;

            if (OnWindow)
            {
                var window = new Window
                {
                    Child = _webView
                };

                window.Show();
            }
            else
            {
                Chart.AddControl(_webView);
            }           
        }

        public override void Calculate(int index)
        {
        }

        private void OnWebViewLoaded(WebViewLoadedEventArgs args)
        {
            _webView.NavigateAsync("https://ctrader.com/");
        }
    }
}

After creating an instance of the below indicator, you will be able to navigate between various web pages on cTrader.com. Your chosen output window will behave similarly to a web browser.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
using cAlgo.API;

namespace cAlgo.Robots
{
    [Robot(AccessRights = AccessRights.None)]
    public class WebViewOnWindow : Robot
    {
        private WebView _webView;

        private TextBox _addressTextBox;

        private TextBox _scriptTextbox;

        private Button _executeScriptButton;

        protected override void OnStart()
        {
            var goBackButton = new Button
            {
                Text = "←",
                Margin = 3
            };

            goBackButton.Click += OnGoBackButtonClick;

            var goForwardButton = new Button
            {
                Text = "→",
                Margin = 3
            };

            goForwardButton.Click += OnGoForwardButtonClick;

            _addressTextBox = new TextBox
            {
                Text = "https://ctrader.com/",
                Margin = 3,
                Width = 150,
                MinWidth = 150,
                MaxWidth = 150
            };

            var goButton = new Button
            {
                Text = "→",
                Margin = 3
            };

            goButton.Click += OnGoButtonClick;

            var reloadButton = new Button
            {
                Text = "Reload",
                Margin = 3
            };

            reloadButton.Click += OnReloadButtonClick;

            var stopButton = new Button
            {
                Text = "x",
                Margin = 3
            };

            stopButton.Click += OnStopButtonClick;

            _scriptTextbox = new TextBox
            {
                Text = "alert('Hi');",
                Margin = 3,
                Width = 150,
                MinWidth = 150,
                MaxWidth = 150,
                IsEnabled = false
            };

            _executeScriptButton = new Button
            {
                Text = "Execute Script",
                Margin = 3,
                IsEnabled = false
            };

            _executeScriptButton.Click += OnExecuteScriptButtonClick;

            var addressBarPanel = new StackPanel
            {
                MaxHeight = 50,
                VerticalAlignment = VerticalAlignment.Top,
                BackgroundColor = Color.Black,
                Orientation = Orientation.Horizontal
            };

            addressBarPanel.AddChild(goBackButton);
            addressBarPanel.AddChild(goForwardButton);
            addressBarPanel.AddChild(_addressTextBox);
            addressBarPanel.AddChild(goButton);
            addressBarPanel.AddChild(reloadButton);
            addressBarPanel.AddChild(stopButton);
            addressBarPanel.AddChild(_scriptTextbox);
            addressBarPanel.AddChild(_executeScriptButton);

            _webView = new WebView
            {
                DefaultBackgroundColor = Color.Red
            };

            _webView.NavigationCompleted += OnWebViewNavigationCompleted;
            _webView.WebMessageReceived += OnWebViewWebMessageReceived;
            _webView.Loaded += OnWebViewLoaded;
            _webView.Unloaded += OnWebViewUnloaded;

            var mainGrid = new Grid(2, 1);

            mainGrid.Rows[0].SetHeightToAuto();
            mainGrid.Rows[1].SetHeightInStars(1);

            mainGrid.AddChild(addressBarPanel, 0, 0);
            mainGrid.AddChild(_webView, 1, 0);

            var window = new Window
            {
                Child = mainGrid
            };

            window.Show();
        }

        private void OnWebViewLoaded(WebViewLoadedEventArgs args)
        {
            Print($"Webview loaded, IsLoaded: {args.WebView.IsLoaded}");

            _webView.NavigateAsync(_addressTextBox.Text);
        }

        private void OnWebViewUnloaded(WebViewUnloadedEventArgs args)
        {
            Print($"Webview unloaded, IsLoaded: {args.WebView.IsLoaded}");
        }

        private void OnStopButtonClick(ButtonClickEventArgs args)
        {
            _webView.StopAsync();
        }

        private void OnExecuteScriptButtonClick(ButtonClickEventArgs args)
        {
            var result = _webView.ExecuteScript(_scriptTextbox.Text);

            Print($"IsSuccessful: {result.IsSuccessful} | Json: {result.Json}");
        }

        private void OnReloadButtonClick(ButtonClickEventArgs args)
        {
            _webView.ReloadAsync();
        }

        private void OnGoForwardButtonClick(ButtonClickEventArgs args)
        {
            _webView.GoForwardAsync();
        }

        private void OnGoBackButtonClick(ButtonClickEventArgs args)
        {
            _webView.GoBackAsync();
        }

        private void OnGoButtonClick(ButtonClickEventArgs args)
        {
            _webView.NavigateAsync(_addressTextBox.Text);
        }

        private void OnWebViewWebMessageReceived(WebViewWebMessageReceivedEventArgs args)
        {
            Print($"Source: {args.Source} | Message: {args.Message}");
        }

        private void OnWebViewNavigationCompleted(WebViewNavigationCompletedEventArgs args)
        {
            Print($"{args.HttpStatusCode} | {args.IsSuccessful} | {args.Url}");

            _addressTextBox.Text = args.Url;
            _scriptTextbox.IsEnabled = true;
            _executeScriptButton.IsEnabled = true;
        }
    }
}

How to Use Scripts and Handle Message Events

The WebView class has two powerful members, namely the ExecuteScript() method and the WebMessageReceived event. You can use them to create algos that access a web page of your choice (e.g., a Forex news feed or any page with custom HTML), execute custom JavaScript on this page, and correctly handle any messages sent from the WebView back to the algo.

Executing Scripts

To execute a custom script inside your WebView, all you need to do is call the WebView.ExecuteScript(String javaScript) method as shown below.

1
2
private _webView = new WebView();
_webView.ExecuteScript("document.getElementById('nextButton').style.display = 'none';");

The script that needs to be executed inside the WebView can simply be passed as a string to the ExecuteString() method.

Note that this string has to contain fully accurate JavaScript. For example, it has to include a semicolon after every executable line.

Multi-line JavaScript

You can freely pass multi-line JavaScript to the ExecuteScript() method as shown in the example below (note the use of a string literal).

1
2
3
4
_webView.ExecuteScript(@"
const element = document.getElementById('nextButton');
element.style.display = 'none';
"); 

Handling Message Events

The WebMessageReceived event is raised when the content displayed in a WebView sends a message. You can react to this event by subscribing to any suitable event handler.

1
2
3
4
5
6
private _webView = new WebView();
_webView.WebMessageReceived += OnWebMessageReceived;
private void OnWebMessageReceived(WebViewWebMessageReceivedEventArgs args)
{
    Print($"Source: {args.Source} | Message: {args.Message}");
}

To send a message from a WebView, all you have to do is call window.postMessage() from the JavaScript code inside the HTML page. For instance, you can pass the script in the below example to calculate the percentile difference between two numbers (e.g., possible entry and exit prices for a position).

1
2
3
4
5
function percentChange(oldVal, newVal) {
  let change = ((newVal - oldVal) / oldVal) * 100;
  window.postMessage('Change: ${change}%');
}
percentChange(1.0784, 1.0975);

Variables and String Interpolation

Context is not shared between C# and JavaScript when you call the ExecuteScript() method. If you want to pass a variable from the C# scope to the JavaScript scope, you will have to use string interpolation.

1
2
var a = 2 + 2;
_webView.ExecuteScript($"window.postMessage({a});");

Placing Controls Inside a WebView

It is possible to combine ExecuteScript() and OnWebMessageReceived to place controls inside a page opened via WebView that, on interaction, send messages back to the cBot. In the example below, we use this functionality to allow trading directly inside the WebView. Note that the WebView opens a custom HTML page rather than an existing resource.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using cAlgo.API;
using cAlgo.API.Collections;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using System.Text.Json;

namespace cAlgo.Robots
{
    [Robot(AccessRights = AccessRights.None)]
    public class WebViewInteractionsExample : Robot
    {
        private WebView _webView;

        protected override void OnStart()
        {
            var window = new Window();           
            _webView = new WebView();

            _webView.Loaded += WebView_Loaded;
            _webView.NavigationCompleted += WebView_NavigationCompleted;
            _webView.WebMessageReceived += WebView_WebMessageReceived;

            window.Child = _webView;
            window.Width = 500;
            window.Height = 500;
            window.Show();
            window.Closed += args => Stop(); // stop cBot when window is closed
        }

        private void WebView_WebMessageReceived(WebViewWebMessageReceivedEventArgs args)
        {
            Print("Message received from WebView: {0}", args.Message);

            var tradeMessage = JsonSerializer.Deserialize<TradeMessage>(args.Message);;
            var tradeType = tradeMessage.side == "buy" ? TradeType.Buy : TradeType.Sell;

            ExecuteMarketOrder(tradeType, tradeMessage.symbolName, tradeMessage.volume);
        }

        private void WebView_Loaded(WebViewLoadedEventArgs args)
        {
            args.WebView.NavigateToStringAsync(@"
            <script>
                function setSymbols(symbols) // called from the cBot
                {
                const symbolSelector = document.getElementById('symbolHtml');
                symbolSelector.innerHTML = '';
                symbols.forEach((symbol) => {  
                    symbolSelector.add(new Option(symbol));
                });
                }

                function trade(operation)
                {
                const symbolSelector = document.getElementById('symbolHtml');
                const selectedSymbolName = symbolSelector.options[symbolSelector.selectedIndex].text;

                window.postMessage({
                    side: operation,
                    volume: Number(document.getElementById('volumeHtml').value),
                    symbolName: selectedSymbolName
                }); //send message to cBot
                }
            </script>

            <body bgcolor='white'>
            <div style='display:flex;flex-direction:column;width:300px'>
                <h1>This is HTML page</h1>
                <div>Symbol:</div>
                <select id='symbolHtml'></select>
                <span style='margin-top: 10px'>Volume:</span>
                <input id='volumeHtml' value='1000' />
                <div style='margin-top:10px'>
                    <input type='button' value='Buy' onclick='trade(`buy`)' />
                    <input type='button' value='Sell' onclick='trade(`sell`)' />
                </div>
            </div>
            </body>
        ");          
        }

        private void WebView_NavigationCompleted(WebViewNavigationCompletedEventArgs obj)
        {
            SendSymbolsToHtml();
        }

        private void SendSymbolsToHtml()
        {
            var allSymbols = "";
            foreach (var symbol in Symbols)
            {
                allSymbols = allSymbols + "'" + symbol + "',";
            }

            _webView.ExecuteScript("setSymbols([" + allSymbols + "]);"); // invokes the corresponding function inside HTML
        }        
    }

    class TradeMessage
    {
        public string side { get; set; }
        public double volume { get; set; }
        public string symbolName { get; set; }
    }
}

Via WebView, the cBot opens a custom HTML page containing controls that allow us to place new orders. Using the SendSymbolsToHtml() method, the cBot fills a dropdown menu inside the HTML with a list of all symbols that an account can trade. While the cBot is running, the user can select a symbol, specify the order volume, and then place a new order directly inside the web page without ever interacting with controls inside cTrader.