VB.NET/C# and JavaScript communication
2010-05-17 10:45
357 查看
Introduction
This article is about passing data between VB.NET/C# WinForms and JavaScript.Before reading further, let me warn you that this article is not about ASP.NET. Concepts covered in the article are applied to Desktop applications.
Background
I was working on a project which required data transfer between my VB.NET WinForms application and the JavaScript (inside an HTML page). Along the way, I hit certain problems, and trying to resolve them cost plenty of time on the web (on Google mostly), so I thought of this article as a platform for developers looking to sort out similar issues. There isn't much detail on this topic on the web apart from a couple of Hello World examples from Microsoft on MSDN.Starting point
OK, without any further talk, I will dig in to the subject.Hello World
To start off, we'll start with a very simple example; all this will do is call a JavaScript function from VB.NET to display an alert with message 'Hello world'. Similarly, from the HTML page using JavaScript, we'll call a VB.NET function which will again display a messagebox with the message 'Hello world'. These are the steps you need to do to make it happen:Calling JavaScript from VB.NET
In Visual Studio, create a new WinForms application, name it anything you like.Add an import statement like
Imports System.Security.Permissionsin your
form1(main form).
Add a couple of attributes to
form1, like:
Collapse
Copy Code
<PermissionSet(SecurityAction.Demand, Name:="FullTrust")> _ <System.Runtime.InteropServices.ComVisibleAttribute(True)> _ Public Class Form1 End Class
All they do is tell the .NET Framework that we want fulltrust and make the class visible to COM so this class is visible to JavaScript.
Add a
WebBrowsercontrol to the form and set its
urlproperty to c:/temp/mypage.html (just an example path, you should set it to the HTML page you'll be using).
Add a
Buttoncontrol on the form, and on its click handler, write this code:
Collapse
Copy Code
Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click Me.WebBrowser1.Document.InvokeScript("showJavascriptHelloWorld") End Sub
In your HTML page, add this code:
Collapse
Copy Code
<script type="text/javascript"> function showJavascriptHelloWorld() { alert("Hello world"); } </script>
Calling VB.NET from JavaScript
Continuing with the last project, add the following lines of code in yourform1:
Collapse
Copy Code
Public Sub showVbHelloWorld() MsgBox("Hello world") End Sub
In your
form1load event, add this line of code:
Collapse
Copy Code
Me.WebBrowser1.ObjectForScripting = Me
What this line does is, it exposes your
form1class to the JavaScript on the HTML page. You could expose any class as long as you make it visible to COM and set its permission to fulltrust.
In your HTML page, add a
Button, and on its click event, call this function:
Collapse
Copy Code
<script type="text/javascript"> function showVbHelloWorld() { window.external.showVbHelloWorld(); } </script>
That is it. We've accomplished two way communication. But, most developers must have seen similar examples on MSDN or in different forums. This is just the starting point, our next step is to pass arguments to the functions.
Passing arguments to functions
Passing arguments to JavaScript functions
To pass arguments to a JavaScript function, you need to simply declare functions in your JavaScript code like you normally do. In your VB.NET code, you can pass a single argument like:Collapse
Copy Code
Me.WebBrowser1.Document.InvokeScript("javascriptFunction", _ New String() {"Message from vb.net to javascript"})
If you want to pass more than one variable, you can simply add it like:
Collapse
Copy Code
Me.WebBrowser1.Document.InvokeScript("javascriptFunction", _ New String() {"Message from vb.net to javascript", _ "Argument 1","Argument 2" , "Argument n"})
So far, I am only passing string type arguments, but you are not restricted to simple strings. You can easily pass string, integers, booleans without any special additions. E.g.:
Collapse
Copy Code
Me.WebBrowser1.Document.InvokeScript("javascriptFunction", _ New Object() {"Message from vb.net to javascript",200,true})
Arguments are not restricted to simple types, you can pass any type you want. The only restriction is you'll have to make it COM visible. In this example, I am going to pass a
Personobject to JavaScript. To make it work, I'll follow these steps:
Create a class called
Person, like:
Collapse
Copy Code
<PermissionSet(SecurityAction.Demand, Name:="FullTrust")> _ <System.Runtime.InteropServices.ComVisibleAttribute(True)> _ Public Class Person Public Sub New(ByVal firstName As String, _ ByVal lastName As String, ByVal age As Integer) Me._firstName = firstName Me._lastName = lastName Me._age = age End Sub Private _firstName As String Private _lastName As String Private _age As Integer Public Function getFirstName() As String Return Me._firstName End Function Public Function getLastName() As String Return Me._lastName End Function Public Function getAge() As Integer Return Me._age End Function End Class
In your
form1, call it like this:
Collapse
Copy Code
Me.WebBrowser1.Document.InvokeScript("sendPerson", _ New Person() {New Person("John", "Smith", 37)})
In your JavaScript, you can use the
Personobject like:
Collapse
Copy Code
<script type="text/javascript"> function sendPerson(person) { alert("First Name:" + person.getFirstName() + "Last Name:" + person.getLastName() + "Age:" + person.getAge()); } </script>
Most developers should be aware of this, but I must point out that when you pass your object to JavaScript, it is fully exposed (all public methods/properties), and JavaScript can modify your object. As an example, if I add this function in my
Personclass:
Collapse
Copy Code
Public Sub setFirstName(ByVal firstName as String) Me._firstName=firstName End Sub
Now, in your JavaScript, modify the function
sendPersonlike:
Collapse
Copy Code
function sendPerson(person) { person.setFirstName('Roger'); }
After this call, your person will have first name as 'Roger' instead of 'John'. To avoid this situation, there are two options. Make
setFristNameprivate/protected, but that means you won't be able to access this method from VB.NET. The better solution is to add an attribute to
setFirstName, like:
Collapse
Copy Code
<System.Runtime.InteropServices.ComVisibleAttribute(False)> _
Public Sub setFirstName(ByVal firstName as String) Me._firstName=firstName End Sub
Now, your function is visible in your VB.NET code, but JavaScript won't be able to call it, and as a result modify it in case you don't want to expose a particular method to JavaScript for any reason.
Passing arrays to JavaScript functions (unsuccessful attempt)
OK, passing arrays to JavaScript. Some developers might think why is it a separate section. The reason is it is not straightforward to pass arrays as most people would think. If you try this code:Collapse
Copy Code
Dim firstNames() As String = {"John", "Richard", "Micheal"} Dim lastNames() As String = {"Smith", "Stone", "Day"} Me.WebBrowser1.Document.InvokeScript("passNameArrays", _ New Object() {firstNames, lastNames})
In your JavaScript, if you try doing this:
Collapse
Copy Code
<script type="text/javascript"> function passNameArrays(firstNames, lastNames) { alert(firstNames[0]); } </script>
you'll get a JavaScript error (if error messages are enabled).
So, why didn't that work, you must be asking. The reason is pretty simple. You can only pass simple types, and arrays are not simple types. You'll have to put a workaround to pass arrays.
Passing arrays to JavaScript functions (successful attempt)
OK, it is understood that we can't pass arrays to JavaScript (at least, I am not aware of a way for passing them; if anyone knows a method, they are welcome to correct me). The simplest workaround is to create a type which wraps your array and passes that type instead of a simple array. E.g., you could wrap your array of strings like:Collapse
Copy Code
<PermissionSet(SecurityAction.Demand, Name:="FullTrust")> _ <System.Runtime.InteropServices.ComVisibleAttribute(True)> _ Public Class myArr Implements IList(Of String) Private _list As New List(Of String) Public Sub New() End Sub Public Sub New(ByVal arr() As String) For Each Str As String In arr Me._list.Add(Str) Next End Sub Public Sub Add(ByVal item As String) _ Implements System.Collections.Generic.ICollection(Of String).Add Me._list.Add(item) End Sub Public Sub Clear() _ Implements System.Collections.Generic.ICollection(Of String).Clear Me._list.Clear() End Sub Public Function Contains(ByVal item As String) As Boolean _ Implements System.Collections.Generic.ICollection(Of String).Contains Return _list.Contains(item) End Function Public Sub CopyTo(ByVal array() As String, ByVal arrayIndex As Integer) _ Implements System.Collections.Generic.ICollection(Of String).CopyTo End Sub Public ReadOnly Property Count() As Integer _ Implements System.Collections.Generic.ICollection(Of String).Count Get Return Me._list.Count End Get End Property 'Rest of the class method needs to be implemented here End Class
And now, if we take on the example from the previous section where we unsuccessfully tried to pass an array of first and last names, let's give it another try:
Collapse
Copy Code
Dim firstNames As New myArr(New String() {"John", "Richard", "Micheal"}) Dim lastNames As New myArr(New String() {"Smith", "Stone", "Day"}) Me.WebBrowser1.Document.InvokeScript("passNameArrays", _ New Object() {firstNames, lastNames})
In your JavaScript, you could use it like:
Collapse
Copy Code
<script type="text/javascript"> function passNameArrays(firstNames, lastNames) { alert(firstNames.item(0)); } </script>
And now, you should see an alert with the text "John". All the rest of the functions like
count,
indexOfetc. are available from JavaScript.
I have implemented the class as
IList(Of String), but you could implement it as
IList(Of Object)and pass any type of array (as long as they are COM visible).
An important fact to remember
One important fact which most developers should be aware of is that you are not bound to pass a set number of arguments to the JavaScript function. You could pass any number of arguments to the JavaScript function, e.g., our last JavaScript function could be written and used like this:Collapse
Copy Code
<script type="text/javascript"> function passNameArrays() { alert(arguments[0].item(0)); } </script>
Passing arguments from JavaScript to VB.NET functions
We have spend enough time passing arguments to JavaScript. Let's move on to the next step, passing arguments to VB.NET functions. You could simply do it like this in JavaScript:Collapse
Copy Code
<script type="text/javascript"> function sendPerson() { window.external.sendPerson("John", "Smith", 26); } </script>
and on your VB.NET side, you could define the function to receive these arguments, like:
Collapse
Copy Code
Public Sub sendPerson(ByVal firstName As String, _ ByVal lastName As String, ByVal age As Integer) MsgBox(String.Format("First name: {0} Last Name: {1} Age: {2}", _ firstName, lastName, age)) End Sub
and you are done.
Again, you are not bound to pass simple types as arguments. You could pass JavaScript objects as arguments, if required. Take a look at the last example in a different way. The JavaScript will be:
Collapse
Copy Code
<script type="text/javascript"> function sendPerson() { var person = new Array(); person["firstName"] = "John"; person["lastName"] = "Smith"; person["age"] = 26; window.external.sendPerson(person ); } </script>
and you can use the
Personobject on the VB.NET side, like:
Collapse
Copy Code
Public Sub sendPerson(ByVal person As Object) MsgBox(String.Format("First name: {0} Last Name: {1} Age: {2}", _ person.firstName, person.lastName, person.age)) End Sub
similar example but different method to achieve the same result.
Returning values from functions
Returning a value from a JavaScript function to VB.NET
What about returning a value from functions? Simple, you could easily get the value from a JavaScript function; e.g., if we call this line in VB.NET:Collapse
Copy Code
Dim str As String = _ Me.WebBrowser1.Document.InvokeScript("getJavascriptString") MsgBox(str)
and in JavaScript, we could write the function to return a string like:
Collapse
Copy Code
<script type="text/javascript"> function getJavascriptString() { return "String returned from javascript"; } </script>
Again, you are not bound to simple types. You can return your custom JavaScript objects very easily. E.g., let's create a
Personclass in JavaScript this time, and write a function which will return the
Personobject back to VB.NET.
Collapse
Copy Code
<script type="text/javascript"> function person(name,age) { this.name = name; this.age = age; this.getName = function() { return this.name; } this.getAge = function() { return this.age; } } function getPersonObject() { myPerson = new person("Chris McCreadie", 48); return myPerson; } </script>
On your VB.NET side, you can do something like this (must warn you the code below is not going to work):
Collapse
Copy Code
Dim jsPerson As Object = Me.WebBrowser1.Document.InvokeScript("getPersonObject") MsgBox(String.Format("Name: {0} Age:{1}", jsPerson.getName(), jsPerson.getAge()))
If you try the above code, the first line will run fine, but at runtime (as it is late binding), you'll receive an exception on the second line. If you are asking why it doesn't work, frankly speaking, I don't know, but if you know the answer, you are welcome to explain it.
So, what is the solution? The solution is pretty simple. You take help from
System.Reflection, and modify the above code to something like this:
Collapse
Copy Code
Dim jsPerson As Object = Me.WebBrowser1.Document.InvokeScript("getPersonObject") Dim jsPersonType As Type = jsPerson.GetType() Dim name As String = jsPersonType.InvokeMember("getName", _ Reflection.BindingFlags.InvokeMethod, Nothing, jsPerson, Nothing) Dim age As Integer = jsPersonType.InvokeMember("getAge", _ Reflection.BindingFlags.InvokeMethod, Nothing, jsPerson, Nothing) MsgBox(String.Format("Name: {0} Age: {1}", name, age))
The above code should work perfectly and give you the desired results. I won't go in to the details of explaining the
InvokeMemberfunction, there's plenty of help on MSDN and Google for that.
What's the next step now? We can access simple types and complex (custom) types. Can we access the objects (HTML buttons, text fields, div) on the page? Yes, we can. And surprisingly, they are easier to access. Let's pull our sleeves and write a simple function in JavaScript which will return a
Buttonobject which happens to be on the page.
Collapse
Copy Code
<script type="text/javascript"> function getHtmlButton() { //assuming there's a button with id myBtn on the page return document.getElementById('myBtn'); } </script>
Now, on the VB.NET side, we could do something like this:
Collapse
Copy Code
Dim htmlButton As Object = Me.WebBrowser1.Document.InvokeScript("getHtmlButton") htmlButton.value = "Set from vb.net"
The above code will change the button text to "Set from vb.net". We could slightly improve the above code by doing a couple of things:
Add a reference in your project for "Microsoft HTML Object Library", can be found under the COM section.
And now, change the first line of the above code to:
Collapse
Copy Code
Dim htmlButton As mshtml.HTMLInputElement = _ Me.WebBrowser1.Document.InvokeScript("getHtmlButton")
What these changes will do is, firstly, it will make our
htmlButtonobject a strongly type one instead of the
Objecttype. Secondly, you'll get intellisence from Visual Studio, and it will make your job easier in trying to figure out what method/properties to call/set on the
htmlButton.
I would give another example just to show the possibilities.
Collapse
Copy Code
'assuming JavaScript got a function which will return a div on the page Dim htmlDiv As mshtml.HTMLDivElement = _ Me.WebBrowser1.Document.InvokeScript("getHtmlDiv") htmlDiv.style.background = "Red"
Most of the types in MSHTML are self explanatory, and you can guess what will be converted from the HTML to the MSHTML type. But if you can't tell, here's a small tip: Set your variable to type
Objectinitially, and once you have received an HTML object from JavaScript (assuming you've setup a break point), hover your mouse over it and Visual studio will let you know the exact type, or alternatively, you can look in the Autos or Local window in Visual Studio.
A tip: All (most) developers know this, but if anyone doesn't, here is a little tip if you want more than one value back from JavaScript. If you are passing objects, JavaScript can modify those objects. If we take the example where we created a
list(of String)in VB.NET and if we need to get the list of first names and last names from JavaScript, we could do something like this:
Collapse
Copy Code
Dim firstNames As New myArr() Dim lastNames As New myArr() Me.WebBrowser1.Document.InvokeScript("getNames", _ New Object() {firstNames, lastNames})
and on the JavaScript side, we could do something like:
Collapse
Copy Code
function getNames(firstNames, lastNames) { firstNames.add("John"); firstNames.add("Chris"); lastNames.add("Martin"); lastNames.add("Churn"); }
and as a result, we could easily get multiple values back from JavaScript. I agree this is not the best example, but I hope you get the concept I am trying to explain.
Returning a value from a VB.NET function to JavaScript
I am sure I don't need this section, but just for completeness, I'll give an example here. On the VB.NET side, we could write a simple function to return the current date and time like:Collapse
Copy Code
Public Function getCurrentTime() As DateTime Return DateTime.Now End Function
and on the JavaScript side:
Collapse
Copy Code
function getCurrentTime() { alert(window.external.getCurrentTime()); }
相关文章推荐
- VB.NET/C# and JavaScript communication
- C# to VB.NET and VB.NET to C#
- VB.NET and C# Comparison
- VB.NET and C# Comparison
- VB.NET and C# 语法比较手册
- (转)VB.NET and C# Comparison
- VB.NET and C# 语法比较手册
- ASP.NET Razor - C# and VB Code Syntax
- Wrox Professional ASP.NET 4 in C# and VB
- VB.NET and C# Comparison
- VB.NET and C# Comparison
- VB.NET and C# 语法比较手册
- [轉]VB.NET and C# Comparison
- 在ASP.NET访问Excel文件 (VB and C#)
- VB.NET and C# Comparison
- Hands-on Labs for Windows® Workflow Foundation in C# and VB.NET
- 在ASP.NET访问Excel文件 (VB.NET and C#)
- VB.NET and C# Comparison
- VB.NET and C# Comparison【转】
- VB.NET and C# Comparison