Today, I have decided to migrate WebView control to WebView2 control in a VB.net application.
I have installed WebView2 from NuGet and I have changed declarations and initialization lines (only 4 lines in my VB program because I'm using 2 WebView2).
When I build my application, Visual Studio 2019 indicates that ScriptNotify event is not an event of WebView2!
Private Sub wvSelect_ScriptNotify(
sender As Object,
e As WebViewControlScriptNotifyEventArgs) Handles wvSelect.ScriptNotify
Using old WebView control, this event is generated using windows.external.notify Javascript function.
function clickPlus(e) { window.external.notify("+PLUS:"); }
Which event is replacing ScriptNotify event in WebView2 control?
I'm interested to know the new event and also how this event is called from Javascript.
A few possible scenarios to handle event notifications with WebView2:
See also: Use JavaScript in WebView for extended scenarios.
Generic initialization:
The WebView2 instance is named myWebView2.
Using basic methods: for example, the JSON deserialization is performed using JavaScriptSerializer. You probably have Json.Net or System.Text.Json instead.
Also assuming WinForms, you haven't specified the GUI framework
Private Async Sub Load(sender As Object, e as EventArgs) Handles MyBase.Load
await myWebView2.EnsureCoreWebView2Async(Nothing)
End Sub
Also, I'm using this simple class model, used to deserialize the JSON message received, since WebView2.WebMessageReceived assumes a JSON format; you can pass whatever string you want, though.
Private Class JsonEventMessage
Public Property Element As String
Public Property Value As String
End Class
1 - In this scenario, javascript Functions are already defined in the HTML
Using JSON.stringify() to emphasize that the return value is a JSON object
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
function SomeFunction(e, v) {
var json = JSON.stringify({ Element:e.target.id, Value:v });
window.chrome.webview.postMessage(json);
}
</script>
</head>
<body>
<script>
document.getElementById("div1").addEventListener("click", function(e) {SomeFunction(e, '+PLUS:')});
</script>
<div id="div1">Some Text to Click</div>
</body>
</html>
In this case, you just need to subscribe to the WebMessageReceived event and deserialize the JSON:
AddHandler myWebView2.WebMessageReceived, AddressOf myWebView2_WebMessageReceived
' [...]
Private Sub myWebView2_WebMessageReceived(sender As Object, e As CoreWebView2WebMessageReceivedEventArgs)
Dim message = New JavaScriptSerializer().Deserialize(Of JsonEventMessage)(e.TryGetWebMessageAsString())
Console.WriteLine(message.Element)
Console.WriteLine(message.Value)
End Sub
Note: I'm using e.TryGetWebMessageAsString() and not e.WebMessageAsJson since the latter is enclosed in quotes (double-stringified). It's better used when you pass a simple string.
2 - Here, a JavaScript Function that can notify a message is defined in HEAD, but no event handlers are added anywhere else.
Assume you will add HTML Elements at run-time or you don't want to add the event handlers in the HTML or anything else.
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
function SomeFunction(e, v) {
var json = JSON.stringify({ Element:e.target.id, Value:v });
window.chrome.webview.postMessage(json);
}
</script>
</head>
<body>
<div id="div1">Some Text to Click</div>
</body>
</html>
Subscribe to the WebMessageReceived event as before and to the NavigationCompleted event. This event is raised when the Document is loaded. We need this event, since, in the example, the event handlers are added to Elements in the Body, which is not available before this point.
In NavigationCompleted, we can add the JavaScript Function to a string and call the WebView2.ExecuteScriptAsync() method, which executes the script after the DOM is ready, otherwise GetElementById() would not find the target.
AddHandler myWebView2.WebMessageReceived, AddressOf myWebView2_WebMessageReceived
AddHandler myWebView2.NavigationCompleted, AddressOf myWebView2_NavigationCompleted
' [...]
Private Sub myWebView2_WebMessageReceived(sender As Object, e As CoreWebView2WebMessageReceivedEventArgs)
Dim message = New JavaScriptSerializer().Deserialize(Of JsonEventMessage)(e.TryGetWebMessageAsString())
' [...]
End Sub
Private Async Sub myWebView2_NavigationCompleted(sender As Object, e As CoreWebView2NavigationCompletedEventArgs)
Dim func As String =
"document.getElementById('div1').
addEventListener('click', function(e) {
SomeFunction(e, '+PLUS:')
});"
Await myWebView2.ExecuteScriptAsync(func)
End Sub
3 - This time, no JavaScript Functions are defined anywhere in the HTML.
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="div1">Some Text to Click</div>
</body>
</html>
Case 1:
Subscribe to the WebMessageReceived and NavigationCompleted events as before.
In NavigationCompleted, the JavaScript Function that posts the message is added to the event handler directly. Call ExecuteScriptAsync() to execute the script and add the Event Listener to a HTML Element.
AddHandler myWebView2.WebMessageReceived, AddressOf myWebView2_WebMessageReceived
AddHandler myWebView2.NavigationCompleted, AddressOf myWebView2_NavigationCompleted
' [...]
Private Sub myWebView2_WebMessageReceived(sender As Object, e As CoreWebView2WebMessageReceivedEventArgs)
Dim message = New JavaScriptSerializer().Deserialize(Of JsonEventMessage)(e.TryGetWebMessageAsString())
' [...]
End Sub
Private Async Sub myWebView2_NavigationCompleted(sender As Object, e As CoreWebView2NavigationCompletedEventArgs)
Dim func As String =
"document.getElementById('div1').
addEventListener('click', function(e) {
var json = JSON.stringify({Element:e.target.id, Value:'+PLUS:'});
window.chrome.webview.postMessage(json);
});"
Await myWebView2.ExecuteScriptAsync(func)
End Sub
Case 2:
Subscribe to the WebMessageReceived and WebView2.CoreWebView2InitializationCompleted.
In this case, we're adding an event listener to the Document, then determine which Element has been clicked using the Event.Target member and any other property or value that can be useful to handle the notification.
Here, I'm simply passing the [Event].target.id and [Event].target.innerText as JSON properties. You can of course get any other attribute necessary in your use-case.
AddHandler myWebView2.WebMessageReceived, AddressOf myWebView2_WebMessageReceived
AddHandler myWebView2.CoreWebView2InitializationCompleted, AddressOf myWebView2_CoreWebView2InitializationCompleted
' [...]
Private Sub myWebView2_WebMessageReceived(sender As Object, e As CoreWebView2WebMessageReceivedEventArgs)
Dim message = New JavaScriptSerializer().
Deserialize(Of JsonEventMessage)(e.TryGetWebMessageAsString())
' [...]
End Sub
Private Async Sub myWebView2_CoreWebView2InitializationCompleted(
sender As Object,
e As CoreWebView2InitializationCompletedEventArgs)
Dim func as String =
"document.addEventListener
('click', function(e) {
window.chrome.webview.postMessage(
JSON.stringify({ Element:e.target.id, Value:e.target.innerText })
);
});"
Await myWebView2.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(func)
End Sub
My problem is now solved and my solution is following ...
I want to display following HTML page
<div id="table">
<div id='Keyboard' class='panel'>
<div class='krow'>
<span class='k digit'>7</span>
<span class='k digit'>8</span>
<span class='k digit'>9</span>
<span class='k back'>⭠x</span>
</div>
<div class='krow'>
<span class='k digit'>4</span>
<span class='k digit'>5</span>
<span class='k digit'>6</span>
<span class='k delete'>x⭠</span>
</div>
<div class='krow'>
<span class='k digit'>1</span>
<span class='k digit'>2</span>
<span class='k digit'>3</span>
<span class='k clear'>⇤⋆</span>
</div>
<div class='krow'>
<span class='k empty'></span>
<span class='k digit'>0</span>
<span class='k point'>.</span>
<span class='k plus'><b>+</b></span>
</div>
</div>
</div>
To intercept click on digit <div>, I have written following Javascript lines
$(document).ready(function ()
{
$('.digit').click(function (ev) { clickDigit(ev); });
...
}
function clickDigit(e)
{
eDigit = e.target;
var sValue = eDigit.innerText;
window.chrome.webview.postMessage("+DIGIT:" + sValue);
}
As you can see, I call postMessage() function to send a notification to VB.Net code.
To initiaze and display my HTML code, I have written following lines
Private Sub Form_Load(sender As Object, e As EventArgs) Handles MyBase.Load
InitializeAsync()
End Sub
Async Sub InitializeAsync()
Await wvSelect.EnsureCoreWebView2Async()
wvSelect.NavigateToString(sHtmlText)
End Sub
where WebView2 control is defined in Designer part of my Form
Friend WithEvents wvSelect As Microsoft.Web.WebView2.WinForms.WebView2
Finally, to intercept JavaScript notification, I have written following VB.Net Code
Private Sub wvSelect_WebMessageReceived(sender As Object, e As CoreWebView2WebMessageReceivedEventArgs) _
Handles wvSelect.WebMessageReceived
Dim sValue As String = e.TryGetWebMessageAsString
Dim iPos = sValue.IndexOf(":")
Dim sAction = sValue.Substring(0, iPos)
sValue = sValue.Substring(iPos + 1)
Select Case sAction
Case "+DIGIT"
SendMessage(txtInput.Handle, WM_CHAR, AscW(sValue.Chars(0)), 0)
I hope than what I have reported in this answer can help others users and complete Jimi answer.
I am trying to send a third party an RSA encrypted string. They have provided me with a public key. I have multiple systems that need to transmit the data.
One such system is vb.net/asp.net, and is using javascript to do the encryption which works perfectly, via jsencrypt: https://unpkg.com/browse/jsencrypt#2.3.1/README.md
The result is consistently 344 characters and ends with =
I also have a desktop app in vb.net that needs to do the same, so I'm using System.Security.Cryptography.RSACryptoServiceProvider, but the result is much longer than what I see sending via the JS code. (It also doesn't end with "=" like the JS code)
The result is consistently 392 and never ends with =
Here is my vb.net code:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim strPublicKey As String = "<RSAKeyValue><Modulus>[Redacted]</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>"
Dim fStringToSend As String = "This is a test."
Dim byStringToSend As Byte() = Encoding.UTF8.GetBytes(fStringToSend)
Dim RSA As System.Security.Cryptography.RSACryptoServiceProvider = New System.Security.Cryptography.RSACryptoServiceProvider(1024)
RSA.FromXmlString(strPublicKey)
Dim strEncryptedStringToSend As String = Convert.ToBase64String(RSA.Encrypt(byStringToSend, True))
TextBox1.Text = strEncryptedStringToSend
End Sub
What am I missing? Let me know if any further data is needed to diagnose & thanks!
EDIT (adding full code, with a key I generated myself to test with)
EncTest.aspx
<%# Page Language="VB" AutoEventWireup="false" CodeFile="EncTest.aspx.vb" Inherits="EncTest" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script type="text/javascript" src="jsencrypt.js"></script>
<script type="text/javascript">
const publicKey = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC0qoqRSNKH+sq7mZKm9f822bhqtD4y4DIRD8++9d0zok5FQrjZHSuGc917zzUGd2IiUzRrLOt/ZMenk42/Wm55rkYf9/MsdHo94vqcHj/5yFYg3QUhgN7l7okIRfAzCkxMl/6x4RymVInB1td5F6N2mChqRm+hzWr9d0I4wLWOpwIDAQAB';
function encText() {
var textToEncrypt = 'This is a test.';
const crypt = new JSEncrypt();
crypt.setKey(publicKey);
const encryptedText = crypt.encrypt(textToEncrypt);
document.getElementById('Label2').innerHTML = encryptedText;
}
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Button ID="Button1" runat="server" Text="net" /><br />
<br />
<asp:label ID="Label1" runat="server"></asp:label><br />
<br />
<div id="Label2"></div><br />
<br />
<div onclick="encText();">js</div>
</div>
</form>
</body>
</html>
EncTest.aspx.vb
Partial Class EncTest
Inherits System.Web.UI.Page
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim strPublicKey As String = "<RSAKeyValue><Modulus>MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC0qoqRSNKH+sq7mZKm9f822bhqtD4y4DIRD8++9d0zok5FQrjZHSuGc917zzUGd2IiUzRrLOt/ZMenk42/Wm55rkYf9/MsdHo94vqcHj/5yFYg3QUhgN7l7okIRfAzCkxMl/6x4RymVInB1td5F6N2mChqRm+hzWr9d0I4wLWOpwIDAQAB</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>"
Dim fStringToSend As String = "This is a test."
Dim byStringToSend As Byte() = Encoding.UTF8.GetBytes(fStringToSend)
Dim RSA As System.Security.Cryptography.RSACryptoServiceProvider = New System.Security.Cryptography.RSACryptoServiceProvider(1024)
RSA.FromXmlString(strPublicKey)
Dim strEncryptedStringToSend As String = Convert.ToBase64String(RSA.Encrypt(byStringToSend, False))
Label1.Text = strEncryptedStringToSend
End Sub
End Class
And here are examples of the encrypted strings:
.net:
JNG3rMmqZkzWXwIIOLVCa2e3vTV2doPEKiSyxBFtgYEbnKpkkCKiri7Vdkovn+2T+ArPuVzgTpPEelWN/3gBtvTxSQ4Vt1+H4MMUjFcCwkYoom1V2FyzIBIZ0eqjNQfqos9F10FIQ3NkqiDCDoNHKA5yNfPLBeNQfB5C/bCzCffE6hgHqc7+zkc651V9WSBTuZq7KzgHFUmYHB+juLuZxZGt
javascript:
UGjvEQy5ZFSDJt/SKL/39giZP7iYqlR3u3f6P5FpCf/z5p/lnIIyhSNNoSOwKQspPajbczO9DxSFElKKmQSEH9keQrhj4DtcSyPMHInFWra13/fWK9VA5XiktEc2vb9XUQVeZ7DS19ZDYVq7cpA9vApiYztSMClS8CCMuatO0Gs=
To achieve interoperability with .NET I managed to give a twist to JSEncrypt and fix paddings. I also added PrivateKey encryption for JS possibility.
You can read here Decrypting RSA with JavaScript someone that used my version of JSEncrypt.
I have seen various examples that use a onClientClick to avoid postback.
However, I don't want this. I want to contain the linkbutton's links within vb.net code and prevent the page from performing a postback when clicked. The reason for this is when my page loads it sets the default show() and hide() div displays again which loses the user's current spot on the page when the new window opens. So how can I do this without causing the main page from resetting?
I have tried OnClientClick="javascript:return false;" but prevented user from clicking linkbutton.
I have also tried AutoPostBack = "false" and it didn't work
Attempted other ways as well. ... no luck... including trying HyperLink but those gave me issues too.
<asp:linkbutton ID="aCapShip" runat="server" OnClick="aCapShip_Click" style="text-decoration:none;" >
<i class="fa glyphicon glyphicon-plane fa-lg"></i> Cap Shipping <!--<span class="arrow"></span>-->
</asp:linkbutton>
JavaScript
$(document).ready(function () {
$("#shipping").show();
$("#Ul2List").hide();
$("#manufacture").hide();
$("#Ul1List").hide();
$("#reList").hide();
.........
VB.net
Protected Sub aCapShip_Click(ByVal sender As Object, ByVal e As System.EventArgs)
Dim url As String = "http://dummyinfo/dummyinfo/dummyinfo.aspx"
Process.Start(url)
End Sub
So my work around is the following:
VB.net
If Not Page.IsPostBack Then
startUp()
End If
Public Sub startUp()
Response.Write("<SCRIPT LANGUAGE=""JavaScript"">startSort()<" & "/" & "SCRIPT>")
End Sub
JavaScript
$(function startSort() {
$("#shipping").show();
$("#Ul2List").hide();
$("#manufacture").hide();
$("#Ul1List").hide();
$("#reworkList").hide();
});
I'm trying for hours to change a text area inside a webbrowser in VB.net. I was not able to do it... This is the code from that textarea :
<textarea name="message_html" class="textCtrl MessageEditor BbCodeWysiwygEditor " id="ctrl_message_html" style="height: 260px; display: none;" data-options='{"autoSaveFrequency":"60","bbCodes":[]}' data-auto-save-url="forums/Snapchat-Girls/save-draft" data-dialog-url="index.php?editor/dialog&style=1" data-css-url="css.php?style=1&css=editor_contents&d=1447310869"><p>This is where the text will be</p></textarea>
Note : Loog for "This is where the text will be" inside the code.
Codes i tried :
For Each element As HtmlElement In Me.WebBrowser1.Document.All
If element.OuterHtml.Contains("textCtrl MessageEditor") Then
element.SetAttribute("value", "TEST")
End If
Next
WebBrowser1.Document.GetElementById("ctrl_message_html").SetAttribute("value","TEST")
For Each element As HtmlElement In Me.WebBrowser1.Document.All
element.SetAttribute("value", "TEST")
Next
None of these worked...
PS : Is not my website. So don't ask me to add ids or something.
I am sure this is not needed anymore, but for reference to those who might still be looking for an answer to this question....maybe this might help your needs
Dim Wctl as string = WebBrowser1.DocumentText.Replace("[searchvalue1]", "test1").Replace("[searchvalue2]", "test2")
Then
WebBrowser1.DocumentText = "0"
WebBrowser1.Document.OpenNew(True)
WebBrowser1.Document.Write(Wctl)
WebBrowser1.Refresh()
I have a login page that stores a few values to localStorage (html5) then continues to a VB.Net page. I am looking for a method in VB that can read back those stored values and make them VB vars. Any ideas?
The VB.NET code-behind is running on the server and has no direct access to the local storage API of the browser.
You can, however, easily fill some hidden fields on the login page, using JavaScript, which will be posted on submit and can be read from the code-behind of the .NET page.
Something like this (not tested):
this.document.getElementById("HIDDEN_FIELD_ID").value = localStorage.STORED_VALUE;
...
<input type="hidden" id="HIDDEN_FIELD_ID" />
...
On the .NET page the value can be read like:
Request.Form("HIDDEN_FIELD_ID")
(There are other ways, but this one is easy to grasp.)
Be aware that login data in localStorage can be accessed (and modified) by the user, so make sure you are not creating a security risk.
This sample uses the above concept with VB Code:
Here is the html body element:
<body>
<form id="form1" runat="server">
<asp:HiddenField ID="hfLoaded" runat="server" />
<asp:HiddenField ID="hfLocalStorage" runat="server" />
</form>
<script type="text/javascript">
// Load LocalStorage
localStorage.setItem('strData', 'Local storage string to put into code behind');
function sendLocalStorageDataToServer()
{
// This function puts the localStorage value in the hidden field and submits the form to the server.
document.getElementById('<%=hfLocalStorage.ClientID%>').value = localStorage.getItem('strData');
document.getElementById('<%=form1.ClientID%>').submit();
}
// This checks to see if the code behind has received the value. If not, calls the function above.
if (document.getElementById('<%=hfLoaded.ClientID%>').value != 'Loaded')
sendLocalStorageDataToServer();
</script>
Here is the page load event:
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim s As String
s = hfLocalStorage.Value
'This next line prevents the javascript from submitting the form again.
hfLoaded.Value = "Loaded"
End Sub
Now your code behind has the localStorage value available to it.