Before I go off and code this, I thought I'd see if anyone here knows of any open source (or paid for) equivalents already built.
I'm looking for a browser control where users can preview a web page and then highlight elements of it and once highlighted, I can get the div or id of the element selected.
Has anyone seen such a thing?
Here's a crude version using the .NET WebBrowser control, which uses Internet Explorer.
namespace WindowsFormsApplication1
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Forms;
public partial class Form1 : System.Windows.Forms.Form
{
private HtmlDocument document;
private IDictionary<HtmlElement, string> elementStyles = new Dictionary<HtmlElement, string>();
public Form1()
{
InitializeComponent();
}
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
this.Text = e.Url.ToString();
this.document = this.webBrowser1.Document;
this.document.MouseOver += new HtmlElementEventHandler(document_MouseOver);
this.document.MouseLeave += new HtmlElementEventHandler(document_MouseLeave);
}
private void document_MouseLeave(object sender, HtmlElementEventArgs e)
{
HtmlElement element = e.FromElement;
if (this.elementStyles.ContainsKey(element))
{
string style = this.elementStyles[element];
this.elementStyles.Remove(element);
element.Style = style;
}
}
private void document_MouseOver(object sender, HtmlElementEventArgs e)
{
HtmlElement element = e.ToElement;
if (!this.elementStyles.ContainsKey(element))
{
string style = element.Style;
this.elementStyles.Add(element, style);
element.Style = style + "; background-color: #ffc;";
this.Text = element.Id ?? "(no id)";
}
}
}
}
I had a similar need and the code originally posted was a tremendous help. I would like to return the kindness of the original author.
The answer provided is insufficient. It only answers the first part of the request: highlighting the rendered HTML. It does not solve the second half of the request where the C# program can tell which node is selected.
As the mouse enters the form and moves towards the desired rendered area, the highlight will follow the mouse. This means that potentially many HTML elements will be visited by the mouse.
So how can the C# tell which element the user wants?
The program has to allow the user to click on the desired rendered area. The click should be trapped by the C# program. In addition, the C# should disable the click being delivered to the element (for example, if its an Anchor element you don't want the click to follow the link).
I solved this in my program as follows:
(1) On the 'document complete' event I added a trap (only once for the document) for the click event at the document level:
HtmlDocument htmlDoc = webBrowser.Document;
_document = webBrowser.Document;
//-- add our handler only once
if (!_documentHandlers.Contains(_document))
{
_document.MouseOver += new HtmlElementEventHandler(document_MouseOver);
_document.MouseLeave += new HtmlElementEventHandler(document_MouseLeave);
mshtml.HTMLDocumentEvents2_Event iEvent;
IHTMLDocument2 currentDoc = (IHTMLDocument2) webBrowser.Document.DomDocument;
iEvent = (mshtml.HTMLDocumentEvents2_Event) currentDoc;
iEvent.onclick += new mshtml.HTMLDocumentEvents2_onclickEventHandler(clickDocumentHandler);
_documentHandlers.Add(_document);
}
The 'document completed' event can be triggered several times for the same document (try CNN.com). The '_documentHandlers' variable is a HashSet to make sure we only add the handler one time per a given document.
(2) I also chose to trap the click at the element level. On the 'mouse over' I added:
mshtml.HTMLElementEvents2_Event iEvent;
iEvent = element.DomElement as mshtml.HTMLElementEvents2_Event;
if (iEvent == null)
return;
iEvent.onclick += new mshtml.HTMLElementEvents2_onclickEventHandler(clickElementHandler);
and at the 'mouse leave' I unregistered the click handler:
mshtml.HTMLElementEvents2_Event iEvent;
iEvent = element.DomElement as mshtml.HTMLElementEvents2_Event;
if (iEvent == null)
return;
iEvent.onclick -= new mshtml.HTMLElementEvents2_onclickEventHandler(clickElementHandler);
(3) the click handlers are simple:
private bool clickDocumentHandler(IHTMLEventObj pEvtObj)
{
IHTMLElement element = (mshtml.IHTMLElement)pEvtObj.srcElement;
pEvtObj.cancelBubble = true;
pEvtObj.returnValue = false;
return false;
}
private bool clickElementHandler(IHTMLEventObj pEvtObj)
{
IHTMLElement element = (mshtml.IHTMLElement)pEvtObj.srcElement;
pEvtObj.cancelBubble = true;
pEvtObj.returnValue = false;
return false;
}
Note that they cancel the bubbling of event and return a value of 'false' to prevent the click from percolating upwards.
In these handlers you can add your specific code to save the element being clicked and then use it elsewhere in your application.
Note that at the element level, not every IHTMLElement supports an onclick handler hence the check for null.
-Enjoy
David
Related
I'm using Visual Studio Community in C# (.Net 4.5).
I have a simple form, with one button and one webBrowser control.
I want to check if "tremblay jean" has a trademark registered in his name in Canada (I know he has two).
So when I click my button I load the trademarks search page in my webBrowser control, I wait for it to be complete, then I insert his name in their textbox and click their button.
If I pause the program using a MessageBox.Show after loading the page, it works, there's two documents found.
But if I don't pause the program using a MessageBox it doesn't work. It gives me 500 results, unrelated to "tremblay jean".
So the line of code waiting for the ReadyState to be Complete doen't seem to work.
Does anyone know why?
private void button1_Click(object sender, EventArgs e)
{
string website = "http://www.ic.gc.ca/app/opic-cipo/trdmrks/srch/home?lang=eng";
webBrowser1.Navigate(website);
while (webBrowser1.ReadyState != WebBrowserReadyState.Complete) Application.DoEvents();
MessageBox.Show(webBrowser1.ReadyState.ToString()); // to pause the program
webBrowser1.Document.GetElementById("search-crit-1").SetAttribute("value", "tremblay jean");
HtmlElementCollection elc = webBrowser1.Document.GetElementsByTagName("button");
foreach (HtmlElement el in elc)
{
if (el.GetAttribute("type").Equals("submit"))
{
if (el.InnerText == " Search ")
{
el.InvokeMember("Click"); //comment this line to see if textbox is filled
break;
}
}
}
}
The first thing to do, when you're using a WebBrowser control, is to initialize it with this html string:
<meta http-equiv='x-ua-compatible' content='IE=edge,chrome=1'>
This allows to set the compatibility mode of the control's underlying activex (Internet Explorer) to the most recent locally available version.
With webBrowser1.ScriptErrorsSuppressed = true;, the scripting error popup is disabled.It's a just in case measure.
Then, subscribe the DocumentCompleted event, that will raise when the page has been loaded. As already noted in the comments, this event might be raised more than once, because of the interaction of Scripting and IFrame.
The WebBrowser.ReadyState is used to verify that page is indeed completed.
It's true that, sometimes, the inner scripting can cause some trouble
here, but since this is not the case, I'll leave it as a side
note.
One other thing you'll notice is that the DocumentCompleted event is unsubscribed after the WebForm button is clicked. This is done to avoid further notifications from the WebBrowser, as the needed action is already been performed and no further action is required on other pages.
So, the event will be active only when you'll be requesting new results to the server (e.g. clicking your UI Search button).
Here, the private string SearchName; is a placeholder for a procedure the defines the new search criteria.
private string SearchName;
private void button1_Click(object sender, EventArgs e)
{
SearchName = "tremblay jean";
webBrowser1.ScriptErrorsSuppressed = true;
webBrowser1.Navigate("");
webBrowser1.Document.Write("<!DOCTYPE html><head><meta http-equiv='x-ua-compatible' content='IE=edge,chrome=1'></head>");
webBrowser1.Navigate("http://www.ic.gc.ca/app/opic-cipo/trdmrks/srch/home?lang=eng");
webBrowser1.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(this.WBDocCompleted);
}
protected void WBDocCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
WebBrowser browser = ((WebBrowser)sender);
if (browser.ReadyState == WebBrowserReadyState.Complete)
{
if (browser.Document != null)
{
browser.Document.GetElementById("search-crit-1").SetAttribute("value", this.SearchName);
foreach (HtmlElement button in browser.Document.GetElementsByTagName("button"))
{
if (button.GetAttribute("type") == "submit" && button.Name == "")
{
browser.DocumentCompleted -= this.WBDocCompleted;
button.InvokeMember("click");
break;
}
}
}
}
}
I have a tab control that only responds to changing tabs with the mouse click.
Do I need to manually code in an event handler for tab control despite having the Surface SDK? Or is there a better control handler that I could use here?
I feel like this is entirely counter productive to the point of having the SDK. Especially because I plan on having a lot of different, unique tabs in my program and don't want to be handling each tab individually with nested ifs in a button_TouchDown function. I already have custom buttons that that have button_TouchDown setup and adding individual tab controls would be a headache and hell of a mess of code.
I tried searching but came up empty handed which makes me think that perhaps I am missing something and it should work. Is it because I have a predefined button_TouchDown function?
private void TabItem_TouchDown(object sender, TouchEventArgs e)
{
TabItem tab = sender as TabItem;
TabControl control = tab.Parent as TabControl;
control.SelectedItem = tab;
e.Handled = true;
}
XAML
<TabItem x:Name="hccontactTab" Header="Phone" TouchDown="TabItem_TouchDown">
Based on the above answer, but enhanced to account for scrolling per touch. Use a ClassHandler to handle this cleanly within your application (I use AutoFac's IStartable to auto-register it during building the container):
using System.Windows;
using System.Windows.Controls;
using Autofac;
namespace ...ClassHandlers
{
public class TabItemTouchClassHandler : IStartable
{
public void Start()
{
Register();
}
public void Register()
{
EventManager.RegisterClassHandler(typeof(TabItem), UIElement.TouchDownEvent, new RoutedEventHandler(OnTabItemTouchDown));
}
//must be static! otherwise memory leaks!
private static void OnTabItemTouchDown(object ender, routedEventArgs e)
{
var tab = sender as TabItem;
var control = tab?.Parent as TabControl;
if (control != null && !Equals(tab, control.SelectedItem))
{
control.SelectedItem = tab;
e.Handled = true;
}
}
}
}
so what I need to do is to use this code:
WebBrowser browser = new WebBrowser();
browser.Navigate("www.somthing.com");
browser.Document.GetElementById("ID").InvokeMember("click");
and then i need to find the id of the button in the page and put in my code but my problem is some of the buttons don't have Id's! what should I do then? they only have type and class and some other things but no Id. i realized that some buttons might be java and maybe that's why i can't click on them by the usual way. so do you know what should I do?
You said your button element has a className ,you can get this element with like this:
public Form1()
{
InitializeComponent();
webBrowser1.Navigate("To your register account url");
}
int on = 0;
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
foreach (HtmlElement btn in webBrowser1.Document.GetElementsByTagName("button"))
{
if (btn.GetAttribute("className") == "yourclassname")
{
btn.InvokeMember("Click");
break;
}
}
}
I'm using radtreeview control from Telerik Q3 2010 for winForms and I want to do the following, upon addition of a new node it should be added in Edit mode, well this is not supported in telerik 2010 so I need to simulate pressing F2 to make the node editable.
I found this thread about the same topic: How to simulate pressing F2 button with keyboard focus on treeview in wpf?, but I need to do that in Winform not WPF, can you help me?
Edit
I used sendKeys method + tree.BeginEdit but it didn't work out !
This is my code:
private void radButton6_Click(object sender, EventArgs e)
{
RadTreeNode newNode = new RadTreeNode();
newNode.Text = "new Cabinet";
newNode.Tag = "new Cabinet";
cabinetsTree.Nodes.Add(newNode);
cabinetsTree.SelectedNode = cabinetsTree.Nodes[cabinetsTree.Nodes.Count-1];
cabinetsTree.ScrollToBottom(); //To set the focus on the new added node
cabinetsTree.Focus();
cabinetsTree.AllowEdit = true;
SendKeys.Send("{F2}");
cabinetsTree.BeginEdit();
}
You should first allow editing nodes in the control and then use the BeginEdit method of the node:
public partial class Form1 : Form
{
RadTreeView tree = new RadTreeView();
public Form1()
{
InitializeComponent();
this.Controls.Add(tree);
tree.Size = new Size(500, 500);
tree.AllowEdit = true;
}
private void button1_Click(object sender, EventArgs e)
{
RadTreeNode newNode = new RadTreeNode();
newNode.Text = "new Cabinet";
tree.Nodes.Add(newNode);
newNode.BeginEdit();
}
}
The node should have a BeginEdit() method that allows the user to rename the node. Based on the documentation.
Use the BeginEdit() method to initiate editing on the selected node
You might be able to solve this by catching the keypress events on your keyboard and setting the editmode to true whenever you get the f2 key pressed.
For example, using code and no user input, how would I have my program click the "Search" button on google (assuming I've already filled in the search box and am at google.com)
webBrowser1.Navigate("http://www.google.com");
If you have an ID use this:
webBrowser1.Document.GetElementById("id").InvokeMember("click");
If you have TagName use this
webBrowser1.Navigate("http://www.google.com");
In Web Browser DocumentCompleted event
HtmlElement textElement = webBrowser1.Document.All.GetElementsByName("q")[0];
textElement.SetAttribute("value", "your text to search");
HtmlElement btnElement = webBrowser1.Document.All.GetElementsByName("btnG")[0];
btnElement.InvokeMember("click");
If you have name Class use this:
HtmlElementCollection classButton = webBrowser1.Document.All;
foreach (HtmlElement element in classButton)
{
if (element.GetAttribute("className") == "button")
{
element.InvokeMember("click");
}
}
For adding text in a TextBox to search google.com, use this:
webBrowser1.Document.GetElementById("gs_tti0").InnerText = "hello world";
Try the following code:
public WebBrowser webBrowser1 = new WebBrowser();
private void WebForm_Load(object sender, EventArgs e)
{
try
{
webBrowser1.Height = 1000;
webBrowser1.Width = 1000;
this.Controls.Add(webBrowser1);
webBrowser1.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(WebBrowser_DocumentCompleted);
this.webBrowser1.Navigate("www.google.com.au");
}
catch
{ }
Write down the following function in your c# form:
public void WebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
var webBrowser = sender as WebBrowser;
webBrowser.DocumentCompleted -= WebBrowser_DocumentCompleted;
HtmlElement textElement = webBrowser.Document.All.GetElementsByName("q")[0];
textElement.SetAttribute("value", "mlm company");
HtmlElement btnElement = webBrowser.Document.All.GetElementsByName("btnG")[0];
btnElement.InvokeMember("click");
}
In addition to using InvokeMember and others, if your web page has issues responding when called by ID or Class, you can try using {TAB} & {ENTER} using the SendKeys class within .NET. I've written a lot of scripts for web pages and have found that I've had to use a combination of both (even though SendKeys is far messier than the methods in #AleWin's answer).
Here is the link to the SendKeys class.