11

Is it possible to use the Blockly google javascript libraries inside a WPF WebBrowser?

In particular, Blockly needs several js scripts. How can we reference the js libraries?

Anm
  • 3,291
  • 2
  • 29
  • 40
oaimac
  • 784
  • 2
  • 12
  • 27
  • The WPF WebBrowser is an embedded Internet Explorer, so it can perfectly use external scripts. Have you tried it? Are there any issues? – Simon Mourier Nov 28 '16 at 07:48
  • Well, using an external simple Javascript function seems to be possible with the InvokeScript method. My problem is to know how to use a Javascript library like Blockly which needs several script declaration, parameters configurations and workspace injection. – oaimac Nov 28 '16 at 08:33
  • 1
    Just reference demos/fixed/index.html from the sample code here https://developers.google.com/blockly/guides/get-started/web and point your webbrowser Source property on it. Beware, you'll have to turn IE11 emulation in the WebBrowser, as explained here: https://blog.malwarebytes.com/101/2016/01/a-brief-guide-to-feature_browser_emulation/ with the name of your .exe file – Simon Mourier Nov 28 '16 at 09:33
  • Thanks for your help! I managed to call the index.html page inside the WebBrowser. But now how can I use the Blockly API passing through this WebBrowser ? – oaimac Nov 28 '16 at 10:42
  • The question is too general. Yes you can use it. What do you want to do? What's the problem with multiple `.js` files. Just reference them in head tag and use them. – Reza Aghaei Nov 28 '16 at 21:00
  • What I want to do is to call the API contained inside the .js files that are referenced in head tag of the .html displayed inside a WPF WebBrowser. How can I do that with my C# code ? Am I obliged to do that inside the .html file directly using a sort of wrapper ? For example how can I call a function of the Blockly API ? – oaimac Nov 29 '16 at 07:29
  • You can call a javascript method using `InvokeScript` method of `WebBrowser` control. – Reza Aghaei Nov 29 '16 at 07:44

1 Answers1

10

Short Answer

You can use all blocky features including UI tools and API functions from WPF WebBrowser control. To do so you should:

  • Create an HTML content which contains script tags referencing Blocky js, or methods which you want to call from C#, and required HTML and XML contents based on your requirement (for example toolbox and workspace). You can load toolbox and workspace dynamically at runtime.
  • Load content to WebBrowser control using Navigate or NavigateToString
  • If you need to call a script use InvokeScript method of WebBrowser control.

Also, to be able to use Blocky you should make the WebBrowser use latest document mode without compatibility mode and show modern content.


Example

This example shows:

  • How you can load a toolbox dynamically
  • How you can load workspace dynamically
  • How you can call Blocky API methods using javascript methods. In the example you can see showCode and runCode proxy methods which are independent from wprkspace and will work with any workspace. You can call javascript methods from C#.

You can use either of Blocky Demos for example. I created an example which shows both using Blocky API methods and Blocky UI Tools. This example is based on Generating Javascript example which shows how to use Blocky API to generate a javascript from Blocky workspace.

enter image description here

Download

You can clone or download working example from:

Creating Example Step by Step

The example contains a simple HTML file which in its head tag required javascript files are added. Also it contains two proxy methods which we created to call from C#.

Also the example contains two xml files. On for Blocky workspace and one for toolbox.

Note: creating those files is not compulsory and you can create workspace or toolbox dynamically at runtime. It's just to show you can load a workspace and toolbox at run-time and they don't need to be static.

1) Create WPF Application

Create a WPF project and name it WpfAppllicatin1.

2) Create blockyWorkspace.xml File

Create blockyWorkspace.xml file using below contents. This file will be used to create Blocky workspace.

<xml>
<block type="controls_if" inline="false" x="20" y="20">
    <mutation else="1"></mutation>
    <value name="IF0">
    <block type="logic_compare" inline="true">
        <field name="OP">EQ</field>
        <value name="A">
        <block type="math_arithmetic" inline="true">
            <field name="OP">ADD</field>
            <value name="A">
            <block type="math_number">
                <field name="NUM">6</field>
            </block>
            </value>
            <value name="B">
            <block type="math_number">
                <field name="NUM">7</field>
            </block>
            </value>
        </block>
        </value>
        <value name="B">
        <block type="math_number">
            <field name="NUM">13</field>
        </block>
        </value>
    </block>
    </value>
    <statement name="DO0">
    <block type="text_print" inline="false">
        <value name="TEXT">
        <block type="text">
            <field name="TEXT">Don't panic</field>
        </block>
        </value>
    </block>
    </statement>
    <statement name="ELSE">
    <block type="text_print" inline="false">
        <value name="TEXT">
        <block type="text">
            <field name="TEXT">Panic</field>
        </block>
        </value>
    </block>
    </statement>
</block>
</xml>

3) Create blockyToolbox.xml File

Create blockyToolbox.xml file using below contents. This file will be used to create Blocky toolbox.

<xml>
    <block type="controls_if"></block>
    <block type="logic_compare"></block>
    <block type="controls_repeat_ext"></block>
    <block type="math_number"></block>
    <block type="math_arithmetic"></block>
    <block type="text"></block>
    <block type="text_print"></block>
</xml>

4) Create blockyHTML.html File

Create blockyHTML.html file using below contents. This file just contains reference to Blocky scripts and also our javascript methods which will be called from our application using C# code:

<html>
<head>
  <meta http-equiv="X-UA-Compatible" content="IE=10" />
  <script src="https://blockly-demo.appspot.com/static/blockly_compressed.js"></script>
  <script src="https://blockly-demo.appspot.com/static/blocks_compressed.js"></script>
  <script src="https://blockly-demo.appspot.com/static/javascript_compressed.js"></script>
  <script src="https://blockly-demo.appspot.com/static/msg/js/en.js"></script>
</head>
<body>
    <div id="host" style="height: 480px; width: 600px;"></div>
    <script>
        var workspace;
        function init(toolboxXML, workspaceXML) {
            workspace = Blockly.inject('host',
            { media: '../../media/', toolbox: toolboxXML });
            var wx = Blockly.Xml.textToDom(workspaceXML)
            Blockly.Xml.domToWorkspace(wx, workspace);
        }
        function showCode() {
            Blockly.JavaScript.INFINITE_LOOP_TRAP = null;
            var code = Blockly.JavaScript.workspaceToCode(workspace);
            return code;
        }
        function runCode() {
            window.LoopTrap = 1000;
            Blockly.JavaScript.INFINITE_LOOP_TRAP =
                'if (--window.LoopTrap == 0) throw "Infinite loop.";\n';
            var code = Blockly.JavaScript.workspaceToCode(workspace);
            Blockly.JavaScript.INFINITE_LOOP_TRAP = null;
            try { eval(code); } catch (e) { alert(e); }
        }
    </script>
</body>
</html>

5) Write C# Code

Put a WebBrowser control and name it browser and handle its LoadCompleted event. Also put two Button controls on windows and name them showCodeButton and runCodeButton and handle their Click events like this:

public MainWindow()
{
    InitializeComponent();
    showCodeButton.IsEnabled = false;
    runCodeButton.IsEnabled = false;
    browser.NavigateToString(System.IO.File.ReadAllText(@"d:\blockyHTML.html"));
}
private void browser_LoadCompleted(object sender, NavigationEventArgs e)
{
    showCodeButton.IsEnabled = true;
    runCodeButton.IsEnabled = true;
    var toolboxXML = System.IO.File.ReadAllText(@"d:\blockyToolbox.xml");
    var workspaceXML = System.IO.File.ReadAllText(@"d:\blockyWorkspace.xml");
    //Initialize blocky using toolbox and workspace
    browser.InvokeScript("init", new object[] { toolboxXML, workspaceXML });
}
private void showCodeButton_Click(object sender, RoutedEventArgs e)
{
    var result = browser.InvokeScript("showCode", new object[] { });
    MessageBox.Show(result.ToString());
}
private void runCodeButton_Click(object sender, RoutedEventArgs e)
{
    browser.InvokeScript("runCode", new object[] { });
}

6) Run Application

When you run the application, after the buttons got enabled, click on first button and then you can get the result of showCode method which uses blocky API to generate javascript code from blocky workspace.

Also you can run the code which you created using blocky by click on the second button.

enter image description here enter image description here

Community
  • 1
  • 1
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • Thanks a lot ! so you use a javascript interface contained in the HTML file (generate function). Is it the best solution ? and do you know if it is possible to have a global workspace object in the HTML file and to call all the Blockly API directly from C# (to avoid having as interface as API to call) ? – oaimac Nov 29 '16 at 12:15
  • 1
    **1)** You can have Blocky workspace in the HTML file, like most demo examples of Blocky. I used a different file to show How you can inject the workspace. If you want to use UI tools of Blocky, it's enough to create the toolbox and workspace in the same html file (or even separate files) and load the HTML file into `WebBrowser` control. – Reza Aghaei Nov 29 '16 at 17:33
  • 1
    **2)** Calling Blocky API functions directly is possible, but since methods may need some contexts and you may need to call multiple methods in a single context to gain desired result, I believe creating a single javascript interface which performs such tasks for you is more friendly. – Reza Aghaei Nov 29 '16 at 17:34
  • @RezaAghaei thanks for this amazing solution but I got a problem. I'm unable to use mouse in my WPF form. I'm unable to drag items in drag area and so on. – Sunny Bhadana Jun 29 '18 at 02:37
  • 1
    @SunnyBhadana At the time of answering the question, I tried it and it was working well and the screenshot has taken from the working instance. I guess you have problem regarding to showing modern contents in WebBrowser control. To fix it, take a look at [this post](https://stackoverflow.com/q/38514184/3110834). – Reza Aghaei Jun 29 '18 at 12:38
  • @SunnyBhadana How did you end up solving the issue of mouse events not working (drag&drop as well as left and right click events) in the WebBrowser? – Mark Diedericks Aug 01 '18 at 08:59
  • @MarkDiedericks I used WefSharp – Sunny Bhadana Aug 01 '18 at 09:01
  • For future reference, if continuing to use the default WPF WebBrowser control. The drag&drop as well as mouse event issues seem to be fixed by setting the websites compatibility to IE10. `` is placed inside the header of the html file. Also, if you come across the control not loading the website due to proction, simply add the localhost MOTW ``. Read more at; https://learn.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/compatibility/ms537628(v=vs.85). – Mark Diedericks Aug 01 '18 at 12:27
  • 1
    @MarkDiedericks I created a repository for this example on GitHub and added the link to the answer. Thanks for the feedback :) – Reza Aghaei Aug 01 '18 at 13:41
  • 1
    @SunnyBhadana I made it working based on [this post](https://stackoverflow.com/q/38514184/3110834). Instead of setting it up for `Edge`, set it up for `IE 10` as also mentioned by Mark. Thanks for the feedback. – Reza Aghaei Aug 01 '18 at 13:44
  • Tried example code mentioned above in VS 2017 IE 10 mode. I get JavaScript errors but after dismissing the error popup everything seems to work - initial mouse click that hides the main window :-(. Any IE version except 10 results in non-functional mouse events. Creating a WPF project and adding CefSharp to the project via NuGet Package Manager generates an error partially through the process. Downloaded the latest stable CefSharp3 code - building generates a number of errors and ultimately fails. I'm curious, despite the chatter, is anyone really using Blockly in a .Net application? – Andrew Mar 13 '19 at 19:09
  • 1
    @Andrew It was working at time of writing the example. I will check it again. – Reza Aghaei Mar 14 '19 at 01:35
  • I'm now using CefSharp with Blockly, it works fine. – James MA Apr 08 '19 at 08:47