I'm utilizing a System.Windows.Forms.WebBrowser (used in a separate .dll file) from my WPF application in order to print some HTML content in the background.
My problem is that the HTML elemnts are not being affected by the javascript in the page.
I know that there are no errors in the page because that if I copy its content after the DocumentCompleted event and paste it into a plain HTML file the script works.
My HTML template:
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<script>
function loaded() {
document.getElementById("content").innerHTML = "after javascript";
}
</script>
</head>
<body onload="loaded()">
<h1 id="content">before javascript</h1> <!--<= This value does not change-->
</body>
</html>
I use this HTML as follows:
private void PrintTest()
{
string curDir = Directory.GetCurrentDirectory();
string fullPath = Path.Combine(curDir, "PrintDocs\\HTMLPage1.html");
string html = File.ReadAllText(fullPath);
HtmlPrint.Print(html);
}
public static class HtmlPrint
{
public static void Print(string html, string documentTitle = "")
{
var wb = new System.Windows.Forms.WebBrowser { DocumentText = html };
wb.DocumentCompleted += wb_DocumentCompleted;
}
private static void wb_DocumentCompleted(object sender, System.Windows.Forms.WebBrowserDocumentCompletedEventArgs e)
{
var wb = ((System.Windows.Forms.WebBrowser)sender);
wb.Print();
wb.Dispose();
}
}
What am I missing here?
You forgot the Braces in onload:
<body onload="loaded()">
As mentioned in my comments you can call the InvokeScript() method from your C# code and pass in your js function name as which should do the trick.
wb.Document.InvokeScript("loaded");
I think you have to change onload="loaded" to onload="loaded()"
Related
I would like to access a variable and a set of functions from within a javascript segment. I have two files:
testJSP.jsp
<%#page import="test.tester"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<%
String thing = "Exciting String";
tester testy = new tester(thing);
%>
<input type="submit" onclick="test()" value="Sample Text" />
<div id="testDiv" >Boring String</div>
<script language="javascript" >
funciton test() {
document.getElementById("testDiv").innerHTML = <%=testy.getString()%>;
}
</script>
</body>
</html>
tester.java
package test;
public class tester {
private String privy;
public tester(String testString) {
privy = testString;
}
public String getString() {
return "new " + privy;
}
}
Essentially, when the button is clicked, I want the element with id "testDiv" to be changed according to the function in the java file. I may be overlooking a very simple method, or perhaps there is a far more complex way of approaching this seemingly simple problem.
I'm unable to capture a custom Javascript event in a C# WPF mshtml WebBrowser control. I've created the example code below. A button click raises a custom event. I realise that there are easy ways to capture a button click event. I need to use a custom event. I've used a button just for this example and testing. What am I doing wrong?
XAML file:
<Window x:Class="WebEvent2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<WebBrowser Name="webBrowser1" Loaded="webBrowser1_Loaded" LoadCompleted="webBrowser1_LoadCompleted"></WebBrowser>
</Grid>
</Window>
C# file:
namespace WebEvent2{
public partial class MainWindow : Window{
public MainWindow(){
InitializeComponent();
}
private void webBrowser1_Loaded(object sender, RoutedEventArgs e{
webBrowser1.Navigate(#"file:///D:/test.html");
}
private void webBrowser1_LoadCompleted(object sender, NavigationEventArgs e){
try{
var evtListener = new EventListener();
var window = ((IHTMLDocument2)webBrowser1.Document).parentWindow as IHTMLWindow3;
window.attachEvent("MyCustomEvent", evtListener);
// Also not working:
// ((HTMLDocument)webBrowser1.Document).attachEvent("MyCustomEvent", evtListener);
}catch (UnauthorizedAccessException err){
Console.WriteLine("OOPS: " + err);
}
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
public class EventListener{
[DispId(0)]
public void handler(IHTMLEventObj evt){
MessageBox.Show("message received");
}
}
}
}
HTML file:
<html>
<head>
<title>Test page</title>
<script>
function sendCustomEvent() {
var event;
if (typeof(Event) === 'function') {
event = new Event('MyCustomEvent');
} else {
event = document.createEvent('HTMLEvents');
event.initEvent('MyCustomEvent', true, false);
}
document.dispatchEvent(event);
}
</script>
</head>
<body>
<button onclick="sendCustomEvent()">Send custom event</button>
</body>
</html>
I was able to achieve the required functionality by using the WebBrowser controls 'ObjectForScripting' property. Nice and simple.
XAML remains the same as above.
WebInterface.cs:
namespace WebEvent2
{
[System.Runtime.InteropServices.ComVisibleAttribute(true)]
public class WebInterface
{
public void callMe()
{
MessageBox.Show("hello");
}
}
}
MainWindow.xaml.cs:
namespace WebEvent2
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
WebInterface wi = new WebInterface();
private void webBrowser1_Loaded(object sender, RoutedEventArgs e)
{
webBrowser1.ObjectForScripting = wi;
webBrowser1.Navigate(#"file:///D:/test.html");
}
}
}
test.html:
<html>
<head>
<title>Test page</title>
</head>
<body>
<button onclick="window.external.callMe()">Send custom event</button>
</body>
</html>
I am able to get HTML source code using following code. but when I am trying with https://marriott.medallia.com/sso/marriott/homepage.do?v=bnAaQvo3*lVHsqtnwluPh_CMCsIHyFkti&alreftoken=6d0d31c7eb7583b964d0ecb89b55e12b
The page URL is getting changed dynamically and on next generated page when I see source view I only get the following code in the HTML body:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>IdP Selection</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="style.min.css">
</head>
<body>
<div id="app-container" class="app-container"></div>
<script>
AppContext = {
idps: '[{"entityId":"MI-PROD-SAML2-IDP-MEDALLIA","name":"Marriott International (any associate w/ EID)"},{"entityId":"https://identity.starwoodhotels.com","name":"Starwood Hotels"}]'
};
</script>
<script src="main.min.js"></script>
</body>
</html>
when I inspect on generated radio button I am able to get HTML element in browser developer elements tab.
My C# code is as follows:
public Form1()
{
InitializeComponent();
this.webBrowser1.ObjectForScripting = new MyScript();
}
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
webBrowser1.Navigate("javascript: window.external.CallServerSideCode();");
}
[ComVisible(true)]
public class MyScript
{
public void CallServerSideCode()
{
var doc = ((Form1)Application.OpenForms[0]).webBrowser1.Document;
var renderedHtml = doc.GetElementsByTagName("HTML")[0].OuterHtml;
var marelement = doc.GetElementById("MI-PROD-SAML2-IDP-MEDALLIA");
HtmlElementCollection eCollections = doc.GetElementsByTagName("HTML");
string strDoc = eCollections[0].OuterHtml;
}
}
I think It's because of ajax! After ajax previous handler of element is not updates and you should attach handler on OnPropertyChanged event:
var element = webBrowser.Document.GetElementsByTagName("HTML")[0];
element != null ? element.AttachEventHandler("onpropertychange", handler) : return;
private string renderedHtml;
private void handler(Object sender, EventArgs e)
{
var element = webBrowser.Document.GetElementsByTagName("HTML")[0];
if (element != null)
renderedHtml = element.OuterHtml;
}
So the page is rendered by ReactJS so you are going to have a bad time getting this to work. The best thing I can think of is creating a something that 'waits' for the element to be created in the WebBrowserControl....
!(function() {
function check(){
if(!document.getElementById("MI-PROD-SAML2-IDP-MEDALLIA")) {
setTimeout(check, 100);
} else {
window.external.CallServerSideCode();
}
}
check();
}());
Which can then be minified to something you can use...
webBrowser1.Navigate(#"javascript:!(function(){function c(){if(!document.getElementById('MI-PROD-SAML2-IDP-MEDALLIA')){setTimeout(c, 100);}else{window.external.CallServerSideCode();}}c();}());");
I am trying to render a chart in an Android WebView using HighCharts.
What my app does should be simple :
- Load the HTML page and external (but local) css and js files
- Load data from the Java part of the app
- Call a javascript function with the previously-loaded data as a parameter
I setup my webview by activating Javascript :
mWebView.getSettings().setJavaScriptEnabled(true);
And also by setting a WebClient to catch javascript's console messages :
mWebView.setWebChromeClient(new WebChromeClient() {
public boolean onConsoleMessage(ConsoleMessage cm) {
Log.d(TAG, cm.message() + " -- From line "
+ cm.lineNumber() + " of "
+ cm.sourceId() );
return true;
}
});
This is my HTML page :
<!DOCTYPE html>
<html lang="fr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="style.css" />
<title>Chart</title>
<script src="jquery.js"></script>
</head>
<body>
<script src="highstock.js"></script>
<script src="exporting.js"></script>
<script language="javascript">
function plot() {
console.log("Hello !");
}
</script>
<div id="container" style="height: 100%; width: 100%"></div>
</body>
</html>
I load this page from the app's assets by calling
mWebView.loadUrl("file:///android_asset/page.html");
My external CSS file seems to be read and I suppose the external JS files are also correctly loaded
I read everywhere that I can call my javascript method anytime by calling
mWebView.loadUrl("javascript:plot()");
However, I always get the error
Uncaught ReferenceError: plot is not defined -- From line 1 of null
Is there anything I might have forgotten ?
Note that I load my page and call the javascript method right after the loadUrl call.
Thanks !
Ok, I'm sorry for your time loss. I was looking for an answer for hours, and then within 15 minutes of posting the question I found the solution.
The problem seemed to be the two consecutive calls to loadUrl. Maybe the page wasn't properly loaded yet while I was already calling the javascript function.
I added a load listener on the WebView like this
mWebView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if (url.equals("file:///android_asset/page.html")) {
mWebView.loadUrl("javascript:plot();");
}
}
});
so that I call the javascript method only when the page is completely loaded.
Now it works properly on every call.
Thank you again for your attention !
Try this:
<body>
<script src="highstock.js"></script>
<script src="exporting.js"></script>
<script language="javascript">
function plot() {
console.log("Hello !");
}
plot(); //Note the calling of the function within the HTML file once loaded into webView
</script>
<div id="container" style="height: 100%; width: 100%"></div>
</body>
And remove the line:
mWebView.loadUrl("javascript:plot()");
If you want to pass data to your javascript function, hen send your data through the URL like this:
mWebView.loadUrl("file:///android_asset/page.html?data=something");
and use:
var param1var = getQueryVariable("data");
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if (pair[0] == variable) {
return pair[1];
}
}
alert('Query Variable ' + variable + ' not found');
}
in your JavaScript like what is done here. You can also check out this example.
I have two pages one is the main page and the another one is the inner page:
Page names: main.jsp , sidebar.jsp
I want to call the onload function on both of these pages. Is that possible. If yes How?
Below is the code for main.jsp:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%# include file="/pages/common/init.jsp"%>
<%# taglib prefix="sx" uri="/struts-dojo-tags"%>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>J.C. Taylor - Broker Website</title>
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
<link rel="stylesheet" href="/css/default.css" media="screen" type="text/css" />
</head>
<body onload="prepopulateFields();load(17);">
<s:form name="continue" id="continue_id" action="continue" method="POST" validate="true" enctype="multipart/form-data">
<div id="wrapper">
<div id="main">
<div id="sidebar">
<%# include file="/pages/common/sidebar.jsp"%>
<span class="clearIt"></span>
</div>
The sidebar.jsp is:
<body onload="setSelected();">
//Some static content here
</body>
So Basically I want is to call prepopulateFields() Javascript method which belongs to onload() event of the main .jsp page and setSelected() which belongs to onload() method of the sidebar.jsp symulatneously and separately.
I know I can call the setSelected(); method inside the prepopulateFields() method but that I dont want to do. All I want is when the page is loaded both the onload functions should be called separately.
If you have some suggestions please do let me know!
I know I am being little bit ridiculous here but if I could do that My job will be very easy.
i don't think you can call more than one onload function.
best way is to call the method from already called function
function prepopulateFields(){
if //condition which check the current page where you want other onload function
setSelected();
}
<body onload="prepopulateFields();load(17);">
</body>
You cannot nest HTML <body> elements, it would only malform HTML.
Best is to put it as a <script> at the bottom of sidebar.jsp.
<script type="text/javascript">setSelected()</script>
If you use firebug to inspect the rendered html page of main.jsp. You would see there is only one < body > element. The < body > element in your sidebar.jsp is not rendered since it will malform HTML as html or body not allowed in included jsp.
Be careful that the included file does not contain <html>, </html>, <body>, or </body> tags
The solution is:
either put your setSelected() into main.jsp body onload event if the sidebar.jsp is always loaded;
or do as BalusC suggested.
window.onload = codeAddress; should work. Here's a demo. And the full code:
<script type="text/javascript">
function codeAddress() {
alert('ok');
}
window.onload = codeAddress;
</script>
Put the class definition in the parent jsp, and instantiate as many onloads as you need in the includes.
<SCRIPT>
// this portion was placed above the Class definition for convenience.
// make sure the Class definition gets loaded first in your code.
new OnLoad(function(){
alert("document loaded");
},100);
</SCRIPT>
...
<SCRIPT>
// Class Definition
OnLoad = function(taskFunction,miliseconds) {
var context = this;
context.cnt = 0;
context.id = null;
context.doTask=taskFunction;
context.interval = function() {
if(document.readyState == "complete"){
try{ context.stop();} catch(e){ throw new Error("stop error: " + context.id); }
try{ context.doTask();} catch(e){ throw new Error("load error: " + context.id); }
}
};
context.start = function(timing) {
if(context.id && context.id!=null)
context.stop();
context.cnt=0;
context.id=setInterval(context.interval,timing);
};
context.stop = function() {
var _id = context.id;
clearInterval(context.id);
context.id=null;
};
context.start(miliseconds ? miliseconds : 100);
};
</SCRIPT>