Using an Embedded Browser in Magic XPA

Saturday, 8th April, 2017

Hello. Excuse the mess, it's been awhile since I last wrote a technical blog post and I'm just trying to figure out how best to arrange and present this.

I've built embedded HTML interfaces into enterprise Magic applications for many years to allow very slick dashboards, charts and all kinds of deep statistical analysis, things like chat-messaging modules and kiosk-based menu systems too. Anything that goes beyond the typical battle-ship grey of desktop Magic-based business applications.

It happens a lot that I'm asked how to achieve this; this time, I thought as well as build a demo for a client, I may as well write-up how easy it is to do for the greater Magic-Community.

Using a .Net Embedded Browser Control with Magic XPA 3.x

The objective here is to allow HTML to be rendered in desktop application forms for better UI/UX - but also allow activity in that embedded browser to trigger events and actions in Magic and give Magic the ability to interact and update the browser document. I'm not suggesting you build everything like this (I may suggest you build everything as a web app, but that's another story) but it's a very useful trick in your arsenal.

My goal is to make this as simple and clear as possible so you can apply the concepts in your own applications. There are no complicated javascript frameworks and sexy CSS/bootstrap designs, no Handlebars, no ReactJS, no Angular, no jQuery - it's plain vanilla just to demo the essence of the integration points. We'll get cute and more besides in later articles.

It's easy but not entirely straightforward. Let's get to it.

  • Demo

  • This is the demo, ugly as hell. Run program "Embedded Browser Demo" in the Demo project. There are 2 panes, one an embedded .Net browser control, the other with simple Magic controls. Basically, there are 2 buttons, one in each pane, that allow a text message to be sent from each to the other. Understand the process behind this, and there's nothing in your imagination you can't achieve: the best-looking, most functional applications you've dreamed of. Any UI you've seen anywhere on the web or mobile, you can now have in your Magic desktop application.

    But yes, examples of the good-looking parts come in later articles. This just shows how it can work. Small moves.

    The source code of the demo is located here: https://github.com/craigcode/xpa-embedded-browser

    Let me walk you rapidly through what's happening and why.

  • 1. Composite Resource Repository

  • First, to use the WebBrowser control, you need to let Magic know about it by defining a reference to System.Windows.Forms as a .Net resource. Make sure you use v4 of the .Net framework. I also create a .Net Alias called WebBrowser to System.Windows,Forms.WebBrowser I'll shortly use to assign to a .Net variable to drop the control on a program form for use.

  • 2. Program Repository - utility sub-programs

  • I've created two sub-programs used by the demo as follows:

    Get Base Href - this program evaluates and returns the location of the HTML directory beneath the project folder using the %WorkingDir% logical name for the purposes of the demo. In practice this can point to anywhere local or remote the web assets used by the HTML are located - here, I have a single, simple plain-vanilla javascript file to handle interaction and raising/responding to events but in more complete solutions, this would resolve the location of things like CSS, javascript libraries and images etc. I use a file:/// reference to locate my local directory, flip slashes and replace spaces with uri-friendly %20 tokens.

    Get Demo HTML Page - this program loads the HTML template I use to display the message and button to send messages from the browser to Magic and later display the message from any Magic events. I replace a merge tag in the document with the base href value so the javascript I need in magic.js is referenced correctly.

  • 3. Embedded Browser Demo Program

  • This is the program to run and present the demo. Here's some data on what's going on inside.

    • General Approach
    • The nuts and bolts of how this works are like this: The browser form is displayed and works like any web page. I have a button to send a message to Magic that triggers a javascript function called raiseMagicEvent() which takes an "action" parameter I store in a placeholder in document.title (unseen for an embedded browser) and update the browser document.location.href to an unnamed anchor tag to trigger a navigate which Magic can detect by handling the DocumentCompleted browser event. OK, now, handling that event, Magic can check if the document.title has data that is marked for its attention with MAGIC_EVENT and data about which event fired. Magic will then use this to call the getPayload javascript function via the browser control's InvokeScript method to get whatever data the developer requires passed back, in this case a message. (In some cases, if the payload is small, it can be concatenated in the original MAGIC_EVENT title message, for example, which menu option was clicked on the webpage - however the document.title property is limited to about 512 characters whereas InvokeScript returns an arbitrary System.Object which I place in a Magic Blob, so for larger amounts of data from the page, you need something like this technique.

      See it work, trace it through, and it will become clear.

      Magic, on the other hand, when it wishes to initiate some action directly in the browser, calls in via InvokeScript - in my demo, using the magicHandler javascript function to orchestrate what is to be done on the page. In my two examples, either update a DIV with a message from Magic or change the background color. Easy.

      Other details worth highlighting for attention are the use of a c# .Net snippet to construct a System.Object array that the browser InvokeScript method uses to pass arguments in the BuildArray function in Magic and the use of DNSET() in task prefix to assign the HTML page source to the browser control to render the page.

    • HTML and Javascript
    • The final piece of the puzzle is the HTML in demo.html and the javascript in magic.js that acts as glue binding everything together. Let's look at those.

      Worth noting is the use of the onclick="javascript:raiseMagicEvent('sendMsg')" action for the Send Message to Magic button

      raiseMagicEvent(action) triggers an event in Magic, getPayload(action,options) allows Magic to retrieve data for an event while magicHandler(functionName, data) will execute script when requested by Magic.

  • Next steps
  • 1. Have a nice cool beer.

    2. Get more sophisticated and add some bootstrap, framework or admin template code to build a slick interface, menu system or dashboard.

You're welcome.

Who's the more foolish: the fool, or the fool who follows him?
Obi-Wan Kenobi