Navigating trough objects of a web page, using an embedded web browser - c#

I have a Windows Forms application that uses a WebBrowser control to display an embedded web page. The file is (successfully) loaded using:
webHelp.DocumentStream=
Assembly.GetExecutingAssembly()
.GetManifestResourceStream("MyAssembly.help.html");
In order for this to work (i.e. the file to be loaded/displayed) I set the webHelp.AllowNavigation = false;. I don't fully understand why, but if it's set to true, the page is not displayed.
In my HTML document (see bellow) I want to be able to navigate trough different sections. But when I click on a link, the browser control does not go to the targeted element. The web page works fine in the stand-alone Internet Explorer 10, so it must have something to do with the control, more specifically the AllowNavigation property. MSDN didn't help much.
How can I achieve this navigation behavior? Is there another way of loading the HTML file without setting the AllowNavigation property to false?
This is my simple HTML file:
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>Using this tool</title>
</head>
<body>
<h3>Description</h3>
<div><p id="contents">Contents</p></div>
<div>
<p id="general">Using the file converter</p>
<p>*converter description*</p>
Go To Top!
</div>
<div class="divBlock" >
<p id="selectOption">Selecting a conversion action</p>
<p>*action selection*</p>
Go To Top!
</div>
</body>
</html>
EDIT: After additional tests I found the root of the problem. The problem appeared after setting a value for the URL property, running the application and afterwards clearing this value. The embedded page is not loaded any more, unless the AllowNavigation property is set to false. There are two solutions, described in my answer bellow.

I also have my own WebBrowser. I've tested it and it loads your HTML file perfectly.
I simply used:
webBrowser1.Navigate("C:\\myPath\\SofNavigate.html");
When I click on links it goes to "#contents" without problems.
I am not sure why you need to use webHelp.Docstream instead of simple Navigate.
By the way, when I turn off navivation, then I am not able to go anywhere from the page that I started on. So Navigation must be on in order to go anywhere from the "home page".
Try to debug that part, as it appears to be the bigger problem that you have.
Here is a good example on how to set up simple webBrowser. Try to use it as a base and see what you do differently that messes up your navigation.

[EDITED] Win8/IE10, your code works for me unmodified inside Form.Load event on a simple form which has just a single WebBrowser control with all default settings (and WebBrowser.AllowNavigation is true by default). Check the properties of your WebBrowser control in the Designer, you may have something wrong in there.
[/EDITED]
You're using HTML5, which handles anchor links via id attribute (i.e. <p id="contents"> ... <a href="#contents">. By default, WebBrowser control works in legacy IE7 mode with HTML5 disabled. You need to turn it on with FEATURE_BROWSER_EMULATION feature control, before WebBrowser object gets created. The best place to do this is a static constructor of your form:
static MainForm()
{
SetBrowserFeatureControl();
}
private static void SetBrowserFeatureControl()
{
// http://msdn.microsoft.com/en-us/library/ee330730(v=vs.85).aspx#browser_emulation
// FeatureControl settings are per-process
var fileName = System.IO.Path.GetFileName(Process.GetCurrentProcess().MainModule.FileName);
// make sure the control is not running inside Visual Studio Designer
if (String.Compare(fileName, "devenv.exe", true) == 0 || String.Compare(fileName, "XDesProc.exe", true) == 0)
return;
// web pages containing standards-based !DOCTYPE directives are displayed in Standards mode
using (var key = Registry.CurrentUser.CreateSubKey(
#"Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION",
RegistryKeyPermissionCheck.ReadWriteSubTree))
{
key.SetValue(fileName, (UInt32)9000, RegistryValueKind.DWord);
}
}
Try it and your links should work as expected. This solution does NOT require admin rights, the affected key is under HKEY_CURRENT_USER.
[UPDATE] There may be a better solution, it works at least for IE10 here on my side. Add <meta http-equiv="X-UA-Compatible" content="IE=edge" /> as below and leave the registry intact. If you see document.compatMode: CSS1Compat, document.documentMode: 10, you should be good to go, but test with older IE versions too.
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title></title>
<script type="text/javascript">
window.onload = function () {
info.firstChild.data =
"document.compatMode: " + document.compatMode +
", document.documentMode: " + document.documentMode;
}
</script>
</head>
<body>
<pre id="info"> </pre>
</body>
</html>

EDIT: After finding the cause of the problem (see the edit to the question) I can now propose three solutions:
1. WebBrowser control replacement:
Simply delete the existing WebBrowser control and add a new one. This solution does not require any modification of the AllowNavigation property. DO NOT modify the URL property.
2. When deleting and adding a new WebBrowser control is not an option:
Since the AllowNavigation property was influencing the loading and displaying of the web page, there was no reason for it to be left to false afterwards. Setting back the property in the Shown event solved the navigation problem, without requiring other alterations (e.g. in the HTML file or the Registry):
private void helpForm_Shown(object sender, EventArgs e)
{
webHelp.AllowNavigation = true;
}
3. Reseting the Document
It seams that the Document property gets (automatically) initialized if URL property is at one time set and reset. Adding webHelp.Document.OpenNew(true); before loading the resource stream solves the problem without the need for re-adding the WebBrowser and without modifying the AllowNavigation property.

Related

Setting custom cursor via css in winforms WebBrowser control

I'm using the old WebBrowser control in a winforms app (C#) to display some static html document. So far, I managed to display what I want, react in a custom way to link clicks, show custom rich tooltips etc.
I'm struggling to set custom mouse cursors, though. The Control itself does not support the ctrl.Cursor property, and setting cursors via CSS in the HTML does only work for standard cursors, NOT for custom cursors.
I'm aware of the neccessity to set the engine to latest (see meta tag in head). The question is, how can I point to a .cur / .png file; I tried with relative paths, absolute paths (just for tests), or doesn't the WebBrowser control maybe allow custom cursors at all?
Here's my snippet:
this.webBrowser1.DocumentText = #"<!DOCTYPE html>
<html>
<head>
<meta http-equiv=""X-UA-Compatible"" content=""IE=edge"" />
<title>Foo</title>
<style>
.customcursor {cursor:url('C:\path\to\stuff.cur'), auto !important;}
</style>
</head>
<body>
<a href='...' class='customcursor'>Foo</a>
</body>
</html>";

WebBrowser 'steals' shortcut keys

PROBLEM:
I have a WebBrowser and have exposed its ActiveX methods. In my Form in which the WebBrowser is positioned I have a MainMenuStrip with shortcuts. Only when I am typing in a textbox on my Form do the shortcuts properly work. For example, when I press CTRL + N, a shortcut of my Form - instead of a new Form opening, the page the WebBrowser is currently on opens in IE.
WHAT I'VE TRIED:
I have tried focusing the form every 100ms using a timer which didn't work (this would not be a viable option anyway as it is not very subtle and the program has to carefully navigate the WebBrowser using SendKeys etc).
Code for WebBrowser:
this.webBrowser1.WebBrowserShortcutsEnabled = false;
this.webBrowser1.AllowWebBrowserDrop = false;
this.webBrowser1.IsWebBrowserContextMenuEnabled = false;
Inheriting from WebBrowser and overriding ProcessCmdKey:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == (Keys.Control | Keys.N))
{
newToolStripMenuItem.PerformClick();
return true; // or false
}
}
WHAT I'M TRYING TO ACHIEVE:
That the WebBrowser's shortcuts are disabled (I read somewhere that the ActiveX prevents this...why?).
What I would like is that unless one is currently typing/navigating in the WebBrowser the Form's shortcuts work. Focus should be taken from the WebBrowser WHEREEVER there's a click outside the control, not just when other textboxes are in focus (e.g. when there's a click in the blank of the form.
Try inheriting from WebBrowser control and override Control.IsInputKey. See if Ctrl+N arrives in there.
After investigating a lot, we came to know it is browser compatibility issue.
We have added meta tag into the HTML page,then shortcuts are working fine. Below is the sample code.
<html>
<body>
<Head>
<meta http-equiv="X-UA-Compatible" content="IE=IE8" />
</head>
<form>
First name:<br>
<input type="text" name="firstname">
<br>
Last name:<br>
<input type="text" name="lastname">
</form></body>
</html>
There are three different solutions for this problem.
Adding meta tag to make the Web site browser compatible.
Override "PreocessCmdKey" method and handle the shortcuts.
Emulate browser by adding the key under FEATURE_BROWSER_EMULATION.
If you don't want to set the meta tag in html code, you can assign meta tag to the Document text property of webbrowser control before navigating the URL. Below is the sample.
//Setting compatible mode of IE.
this.m_oWebBrowser.DocumentText = #"<html>
<head><meta http-equiv=""X-UA-Compatible"" content=""IE=IE8"" /> </head>
<body></body>
</html>";
this.m_oWebBrowser.Navigate("www.google.com");

Pre-fill a web page from a web page

We have a feature in a Windows application that opens a web browser, navigates to certain pre-configured web sites, and auto-fills the forms with data from our database -- it's a convenience feature for users.
Now we want to build an Asp.Net version of this feature so that from our web app, the user clicks a link/button, and we open the page (with a redirect? with JavaScript?). Then we fill in the form so that the user can review the data and submit it.
To do this, I think that we would either need to inject javascript into the browser frame that is loading the external window, or we would need to be able to interact with that window.
We are worried that browser security might not allow this -- it could look like some sort of spoofing attack. What would be a good way to do this?
If the Domains, Protocols, and Ports do not match between the website opening the popup and the website being opened in the popup then your code will violate the Same Origin Policy and either be silently ignored or throw an exception.
Here's an example to demonstrate:
<!doctype html>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<script type="text/javascript">
openPopUp = function(href) {
var props = "width=500,height=500,menubar=no,toolbar=no,scrollbars=yes";
var win = window.open(href, href, props);
if (win == null) alert("Your popup blocker is ruining my demo.");
return win;
};
openMe = function(url){
href = (url=="")?document.location:url;
pu = openPopUp(href);
//This will be ignored silently if Same Origin Policy is violated
pu.onload = function() {
p = pu.document.createElement("p");
p.appendChild(pu.document.createTextNode("onload was here."));
pu.document.body.appendChild(p);
};
//This will throw an exception if Same Origin Policy is violated
setTimeout(function() {
p = pu.document.createElement("p");
p.appendChild(pu.document.createTextNode("setTimeout was here."));
pu.document.body.appendChild(p);
},3000);
return false;
}
</script>
<body>
Self
Google
You need to make a bookmarklet. From you asp.net page, make a link which the user drags to his browsers bookmarks. The user clicks a link to the page you want to fill out. Then he clicks the newly created bookmarklet.
In pure javascript this is not possible which is why you need to use bookmarklet.
Scenario: you have two pages base.aspx and popup.aspx. On clicking a link on base.aspx you want to open popup.aspx filled with desired data.
You are in control of both base.aspx and popup.aspx
These are few ways I think you can achieve this
If the data you want on popup.aspx is already loaded on base.aspx. Then send this data in querystring (if its not too long)
If the data you want on popup.aspx is already loaded on base.aspx. Read form fields from base.aspx using window.opener in popup.aspx. Note: window.opener will only work if both base and popup are in same domain.
Send an identifier string in popup.aspx URL (http:///popup.aspx?actiodId=1234QWE6789), now in page_load of popup.aspx fetch data from DB quering on actiodId
The best way to do what you are trying to do is to create a browser plugin. Try looking here for some more information. But with a browser plugin, you get complete control of the DOM and you can inject javascript code to do what you are doing. If needed, you can even call web services from your plugin.

C#: Open a browser and POST to a url from a windows desktop app

I have a small WPF app (although I guess it doesn't really matter whether it's a wpf form or a webform app?) that I want to have launch a new browser window and POST to a specific url. I've been messing around with:
System.Diagnostics.Process.Start("http://myurl.com");
to launch the window but I don't think I can use the same process to actually post to a url...I've also experimented with HttpWebRequest but I would like the user to be able to use the app after I have posted to this url, not just show them the results...What can I look at to able to do something like this?
There is no direct way to do it. What you could do is generate a HTML page with a form filled with the data you need to post, and a bit of javascript to post the page automatically when it is loaded. Then you just have to open that page in the browser...
The generated HTML could look like that :
<html>
<head>
<script language="Javascript">
function submitForm() {
var theForm = document.getElementById("theForm");
theForm.submit();
}
</script>
</head>
<body onload="submitForm()">
<form id="theForm" action="http://myurl.com" method="POST">
<input type="text" name="username" value="myusername"/>
<input type="password" name="password" value="mypassword"/>
</form>
</body>
</html>
If the page must be displayed in your application, load it in a WebBrowser control
Use the WebBrowser Class instead.
There are multiple solutions, not sure which one would be the best for you...
Proceed with your original approach
Embed web browser control in your applicaiton as suggested in other answers
Do everything programmatically "behind the scene"
For #3 you may want to look here: http://geekswithblogs.net/rakker/archive/2006/04/21/76044.aspx
If you want to go with #1 - it is more tricky, since you need to control external application and different browsers would behave differently.
I've used "javascript:" protocol and the code below with IE as default browser when dealing with one "user-unfriendly" application. Please note that it's not "production-ready" code. There is no error handling, user may shift focus away from launched browser, or use browser without "javascript:" protocol support etc.
static void Main()
{
Settings s = Settings.Default;
Process.Start(s.URL1);
Thread.Sleep(s.Delay1);
SendKeys.SendWait("%D");
Thread.Sleep(100);
SendKeys.SendWait(EncodeForSendKey(s.URL2));
SendKeys.SendWait("{ENTER}");
}
public static string EncodeForSendKey(string value)
{
StringBuilder sb = new StringBuilder(value);
sb.Replace("{", "{{}");
sb.Replace("}", "{}}");
sb.Replace("{{{}}", "{{}");
sb.Replace("[", "{[}");
sb.Replace("]", "{]}");
sb.Replace("(", "{(}");
sb.Replace(")", "{)}");
sb.Replace("+", "{+}");
sb.Replace("^", "{^}");
sb.Replace("%", "{%}");
sb.Replace("~", "{~}");
return sb.ToString();
}
URL1: http://www.google.com
URL2: javascript:function x(){document.all.q.value='stackoverflow';document.forms[0].submit();} x();
You can create a hidden WebBrowser control and do Navigate() (using the overload that allows you to specify the request method). You will need to specify a "_blank" target frame to cause the navigation to happen in a new browser window.

Response.Redirect with # anchor, does not work in IE7

Response.Redirect(string.Format("myprofile.aspx?uid={0}&result=saved#main",user.UserID));
said code translates to
IE7 - myprofile.aspx?uid=933fdf8e-1be0-4bc2-a269-ac0b01ba4755&result=saved
FF- myprofile.aspx?uid=933fdf8e-1be0-4bc2-a269-ac0b01ba4755&result=saved#main
why does IE7 drop my anchor?
edit: I should mention I am using this in conjunction with jQuery UI's tab control. I want the postback to tab into a specific tab.
It would be quite the hack, but if IE isn't behaving, you could do this.
pass a param as well, e.g.
uid=933fdf8e-1be0-4bc2-a269-ac0b01ba4755&result=saved&hash=main#main
Then on your page (before the body close tag, or as an onload event), only for IE, extract the parameter if present and set the hash manually.
<!--[if IE]>
<script>
if(document.location.href.indexOf('&hash=') != -1){
//extract value from url...
document.location.hash = extractedValue;
}
</script>
<![endif]-->
Maybe it is a regression issue from IE6?
Did you try working around it by prepending it with an &?
I used RegisterClientScriptBlock to issue window.location.
Something like this:
string url = String.Format("{0}RegForm.aspx?regId={1}#A", this.CurrentFolderUrl, this.RegId);
Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "redirectToClientPage", String.Format("window.location='{0}'", url), true);

Categories

Resources