How to Call JavaScript from C#

 

Note: this feature requires the Professional Edition or higher

 

General Concepts

  • A CSHTML5 project is mainly written in C#/XAML. When you compile the project, your C#/XAML code gets automatically compiled into JavaScript and HTML.
  • If you want, you can place JavaScript code directly inside your C# code by calling the "Interop.ExecuteJavaScript" method. Such JavaScript code is copied "as is" when the project is compiled.
  • For example, the following C# code:
    MessageBox.Show("The current URL is: " + CSHTML5.Interop.ExecuteJavaScript("location.toString()"));
    gets compiled into the following JavaScript code:
    alert('The current URL is: ' + location.toString());
  • The following C# example shows how to retrieve the current Width of the browser window by reading the JavaScript "screen.width" property:
    double screenWidth = Convert.ToDouble(CSHTML5.Interop.ExecuteJavaScript("screen.width"));
  • You can pass C# variables to your JavaScript code by passing them as additional arguments to the "Interop.ExecuteJavaScript" method, and using the placeholders $0, $1, $2.... Here is an example:
    void DisplayAMessage(string messageToDisplay)
    {
       CSHTML5.Interop.ExecuteJavaScript("alert($0)", messageToDisplay);
    }
    IMPORTANT: creating JavaScript code by concatenating strings such as in "alert(" + messageToDisplay + ")" or by using String.Format will NOT work, because the JavaScript code needs to exist at compile-time rather than at runtime. In other words, the first argument of the "ExecuteJavaScript" method must be a string literal.

    For example, the following code will NOT work:
    CSHTML5.Interop.ExecuteJavaScript("alert('" + messageToDisplay + "')");
    
    The following code will also NOT work:
    CSHTML5.Interop<.ExecuteJavaScript(String.Format("alert({0})", messageToDisplay));
    However, the following code WILL work:
    CSHTML5.Interop.ExecuteJavaScript("alert($0)", messageToDisplay);
    The reason is that the compiler needs to know the string at compile-time, whereas the "String.Format" method is only executed at runtime.
  • You can also do it the other way round, that is, call C# from within your JavaScript code.

    To call C# from JavaScript, you need to pass a C#-based callback to your "ExecuteJavaScript" code, like in the following C# example:
    void Main()
    {
        CSHTML5.Interop.ExecuteJavaScript(@"
    
            // This is JavaScript code
            alert('Hello from JavaScript');
    
            // Let's call the C# method "MyCSharpMethod" from this JavaScript code. Due to the fact that it is passed as the first parameter to this ExecuteJavaScript call, we can access it with $0:
            $0();
            
        ", (Action)MyCSharpMethod);
    }
    
    void MyCSharpMethod()
    {
        System.Windows.MessageBox.Show("Hello from C#");
    }
    You can see another example in the source code of the WebSocket extension (where the JavaScript-based websocket calls the C#-based OnOpenCallback method) or the File Open Dialog extension (where the Open Dialog of the browser calls the OnFileOpened method of C#).

 

How to embed JavaScript/CSS files in your CSHTML5 project

You can add JS/CSS files to your CSHTML5 project. Those files will be automatically copied "as is" to the output folder.

You can then load them by calling "Interop.LoadJavaScriptFile()" and "Interop.LoadCssFile()".

 

"I have a JavaScript library that needs a <div> or another DOM element in order to render stuff. How can I obtain it?"

You can use the method CHSTML5.Interop.GetDiv(FrameworkElement) in order to get the DIV associated to a XAML element. For this method to succeed, the XAML element must be in the Visual Tree. To ensure that it is in the Visual Tree, you can read the IsLoaded property, or you can place your code in the "Loaded" event handler. This approach works best with simple XAML elements, such as Border or Canvas.

Alternatively, you can use the HtmlPresenter control to put arbitrary HTML/CSS code in your XAML, and then read the ".DomElement" property of the HtmlPresenter control to get a reference to the instantiated DOM element in order to pass it to the JavaScript library.

 

How to create C#/XAML-based wrappers around JS libraries

A C#/XAML-based wrapper around a JavaScript library is useful because it allows the person who uses the wrapper to consume the JavaScript library in pure C# and XAML, without ever having to write any JavaScript code.

For example, the ZIP Compression extension is a C#-based wrapper around the JSZip JavaScript library. It encapsulates all the interop between JS and C# so that the rest of the application can interact with the JavaScript library using only pure C# code.

To create such a wrapper, follow these steps:

  • Find the .JS files that correspond to the library. You can either point to an online location where those JS files are hosted (such as those referenced on CDN JS) or you can download the JS files and add them to your CSHTML5 project.
  • In your CSHTML5 project, create a new C# class and write the "Interop.LoadJavaScriptFile()" code that will load the JS file.
  • Now use the "Interop.ExecuteJavaScript()" method to interact with the JavaScript library from within your C# code.
  • Read the other sections of this page to learn more, look at some examples, or contact us.
  • You can automate the creation of such a wrapper by importing TypeScript Definition files.

 

Examples

Example Source Code How it works
WebSockets extension Link It wraps the native JavaScript "WebSocket" class into a C#-based class that can be consumed by CSHTML5 applications.
Print extension Link It extracts the HTML DOM element that corresponds to the specified FrameworkElement, copies it into a new browser window, and then calls the browser Print command on the new window.
File Open Dialog extension Link It adds the <input type='file'> tag to the HTML DOM, and listens to the JavaScript "Change" event to return a JS blob or text.
File Save extension Link It wraps the open-source JavaScript "FileSaver.js" library into a C#/XAML class for consumption by CSHTML5-based apps.
Unofficial ArcGIS Mapping Control for CSHTML5 Link It wraps the JavaScript-based version of the ArcGIS mapping library into a C#/XAML-based control that can be consumed from pure C#/XAML code.
ZIP Compression extension Link It wraps the open-source JavaScript "JSZip.js" library into a C#/XAML class that mimics the method signatures of Ionic.Zip (DotNetZip).

Many more open-source extensions can be found on the Extensions Gallery page.

 

Reference

Interop.ExecuteJavaScript(...)

Use this method to call JavaScript code from within your C# code.

For example, the following C# code will display the current URL:
MessageBox.Show("The current URL is: " + CSHTML5.Interop.ExecuteJavaScript("location.toString()"));
The following C# code will retrieve the current Width of the browser window by reading the JavaScript "screen.width" property:
double screenWidth = Convert.ToDouble(CSHTML5.Interop.ExecuteJavaScript("screen.width"));
You can pass C# variables to your JavaScript code by passing them as additional arguments to the "Interop.ExecuteJavaScript" method, and using the placeholders $0, $1, $2.... Here is an example:
void DisplayAMessage(string messageToDisplay)
{
   CSHTML5.Interop.ExecuteJavaScript("alert($0)", messageToDisplay);
}
Note that creating JavaScript code by concatenating strings such as in "alert(" + messageToDisplay + ")" would NOT work, because the JavaScript code needs to exist at compile-time rather than at runtime.

You can also do it the other way round, that is, call C# from within your JavaScript code

To call C# from JavaScript, you need to pass a C#-based callback to your "ExecuteJavaScript" code, like in the following C# example:

void Main()
{
    CSHTML5.Interop.ExecuteJavaScript(@"

        // This is JavaScript code
        alert('Hello from JavaScript');

        // Let's call the C# method "MyCSharpMethod" from this JavaScript code. Due to the fact that it is passed as the first parameter to this ExecuteJavaScript call, we can access it with $0:
        $0();
        
    ", (Action)MyCSharpMethod);
}

void MyCSharpMethod()
{
    System.Windows.MessageBox.Show("Hello from C#");
}

You can see another example in the source code of the WebSocket extension (where the JavaScript-based websocket calls the C#-based OnOpenCallback method) or the File Open Dialog extension (where the Open Dialog of the browser calls the OnFileOpened method of C#).

 

await Interop.LoadJavaScriptFile(string url)

Use this method to load a JavaScript file from either an online location (http/https) or the local project.

The following C# example loads the "FileSaver.js" file from an online location:

await CSHTML5.Interop.LoadJavaScriptFile("https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2014-11-29/FileSaver.min.js");

To load a file that you have added locally to your CSHTML5 project, use any of the two following URL syntaxes:

  • ms-appx:///AssemblyName/Folder/FileName.js
  • /AssemblyName;component/Folder/FileName.js

Here is an example:

await CSHTML5.Interop.LoadJavaScriptFile("ms-appx:///MyProject/FileSaver.min.js");

Important note: in the current version, you must take care by yourself of ensuring that the same file does not get loaded twice. You can do that for example by creating a static boolean variable that remembers whether the JavaScript file was already loaded or not.

 

Interop.LoadJavaScriptFilesAsync(IEnumerable<string> urls, Action callback)

Use this method to load a list of JavaScript files from either an online location (http/https) or the local project.

Unlike the method LoadJavaScriptFile, this one does not use the async/await pattern. Instead, the provided "callback" is called when the scripts have been loaded.

The following C# example loads two JavaScript files from an online location and displays a message when done:

CSHTML5.Interop.LoadJavaScriptFilesAsync(new string[]
{
    @"https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.2/jspdf.min.js",
    @"https://cdnjs.cloudflare.com/ajax/libs/kendo-ui-core/2014.1.416/js/kendo.core.min.js"
},
() =>
{
    MessageBox.Show("The scripts have been loaded.");
});

To load a file that you have added locally to your CSHTML5 project, use any of the two following URL syntaxes:

  • ms-appx:///AssemblyName/Folder/FileName.js
  • /AssemblyName;component/Folder/FileName.js

Important note: in the current version, you must take care by yourself of ensuring that the same file does not get loaded twice. You can do that for example by creating a static boolean variable that remembers whether the JavaScript file was already loaded or not.

 

await Interop.LoadCssFile(string url)

This method works the same as Interop.LoadJavaScriptFile()

 

Interop.LoadCssFilesAsync(string url, Action callback)

This method works the same as Interop.LoadJavaScriptFilesAsync()

 

Interop.GetDiv(FrameworkElement fe)

This method returns the HTML DOM element that corresponds to the specified FrameworkElement.

Important note: the FrameworkElement must be in the Visual Tree for this method to succeed. To ensure that the element is in the Visual Tree, you can read the IsLoaded property, or you can place your code in the "Loaded" event handler.

Here is an example:

public partial class MainPage : Page
{
  public MainPage()
  {
    this.InitializeComponent();

    this.Loaded += MainPage_Loaded;
  }

  void MainPage_Loaded(object sender, RoutedEventArgs e)
  {
    object rootDiv = CSHTML5.Interop.GetDiv(this);

    MessageBox.Show("The width of the root div is: " + Convert.ToDouble(CSHTML5.Interop.ExecuteJavaScript("$0.offsetWidth", rootDiv)).ToString());
  }
}

You can see a more advanced example by looking at the source code of the File Open Dialog extension.

You may also be interested by the HtmlPresenter control.

 

Interop.IsRunningInTheSimulator

Returns "True" if the application is running in C# inside the Simulator, and "False" if the application is running in JavaScript in the web browser.

This property is useful when you need to do something different depending on whether the application is running in the browser or in the Simulator, due to the fact that some JavaScript features are not possible in the Simulator.

For example, if you want to open a new browser window, which cannot be done in the Simulator, you may want to display a "Not supported" message if the value of this property is True.

 

FrameworkElement.IsLoaded

Returns True if the FrameworkElement is in the Visual Tree, and False otherwise.

This property is particularly useful when used in conjunction with the Interop.GetDiv method, which only works when the FrameworkElement is in the Visual Tree.

When this property returns True, it also means that the FrameworkElement is present on the HTML DOM tree.

Note: you can listen to the Loaded and Unloaded events to be notified when this property changes.

 

See Also