I've got a System.Windows.Form.WebBrowser control on a form. I navigate that browser object to a url.
Once the page has finished loading, I'd like to analyse various aspects of the page that loaded.
In particular I'm interested to see the attribute 'writingMode' which is found on the IHTMLStyle3 interface.
Something like:
public void MyMethod(HtmlElement element)
{
IHTMLElement2 element2 = element.DomElement as IHTMLElement2;
IHTMLStyle3 style3 = element2.currentStyle as IHTMLStyle3;
string writingMode = style3.writingMode;
...
The problem is, the style3 value is null. I assume this means IHTMLElement2.currentStyle doesn't support IHTMLStyle3.
I've tried also tried casting IHTMLELement.style. But while that does cast happily as IHTMLStyle3 it doesn't seem to contain the style as it's been applied to the Html element.
Looking at the documentation, I believe you need to get the IHTMLElement2.currentStyle property as the regular style property is the inline style only. This difference is indicated in the remarks for IHTMLElement2.currentStyle:
The values returned by the properties
of the IHTMLStyle and
IHTMLCurrentStyle interfaces differ
when the style of an object is not set
inline. For example, if the author of
a Web page sets the color property of
a paragraph to red only through a
linked or embedded style sheet, and
not inline, then the
IHTMLCurrentStyle::color property
returns the value red, and the
IHTMLStyle::get_color property does
not return a value. However, if the
author specifies the value of the
color property inline, as in the
following example, both the
IHTMLCurrentStyle::color and
IHTMLStyle::get_color properties
return the value red.
currentStyle provides a IHTMLCurrentStyle interface, which when queried for IHTMLCurrentStyle2 will give you writingMode as you require.
Related
How can I hide the class property for the swagger doc, but allow the user to set value, if he knows the property name?
I tried [JsoneIgnoreAttribute], but when I add the swagger example, again the property appears in the documentation (For adding the swagger example I used Swashbuckle.AspNetCore.Filters).
Also, I found [IgnoreDataMember] - this one always hides, and when you set a value, it defaults the value, like null.
I have noticed the Tag properties with controls. Is it okay to use this to reference my custom objects, or should I stay away from it as it would require boxing and unboxing which has been mentioned as unsafe and is not recommended.
TreeNode tn = new TreeNode();
CustClass o = new CustClass()
o.number = 123;
tn.Tag = o;
class CustClass
{
public int number {get; set;}
}
The purpose of the Tag property is for you to use it for any purpose you want. You can safely store anything in there you want.
It is customary to declare a class that is specifically intended for being stored in the Tag property (like you did with your CustClass). But if you need only one value in it, then there is nothing wrong with storing an int in it directly.
Where did you read that boxing/unboxing is “unsafe”? That is absolutely not true. (Some people claim that it is inefficient, but even that is not true.) Furthermore, your code does not even contain an example of boxing at all. CustClass is a reference type. Only value types are boxed when assigned to object.
I don't think this would be a boxing or unboxing problem to use it the way you want. As far as I know, that Tag property is for the programmer's purpose only, so you can use it quite the way you need it to be used.
Please see Control.Tag property on MSDN for further reference on how to use this property.
For instance, I used to use it to input instructions to the user in Windows Forms applications. When the control GotFocus event triggered, the instructions Label.Text property was assigned the value of my control Tag property which contained the instruction string.
From links also mentioned by others here:
at https://msdn.microsoft.com/en-us/library/system.windows.forms.control.tag.aspx
one reads:
Any type derived from the Object class can be assigned to this
property. If the Tag property is set through the Windows Forms
designer, only text can be assigned.
A common use for the Tag property is to store data that is closely
associated with the control. For example, if you have a control that
displays information about a customer, you might store a DataSet that
contains the customer's order history in that control's Tag property
so the data can be accessed quickly.
and at https://msdn.microsoft.com/en-us/library/system.windows.forms.treenode.tag.aspx one reads:
...example creates a root tree node to assign child tree nodes to. A
child tree node for each Customer object in an ArrayList is added to
the root tree node as well as a child tree node for each Order object
assigned to the Customer object. The Customer object is assigned to
the Tag property, and the tree nodes representing Customer objects are
displayed with Orange text. This example requires that you have a
Customer and Order object defined, a TreeView control on a Form, and
an ArrayList named customerArray that contains Customer objects.
Well, you could create your own TreeNode derived class:
class MyNode : TreeNode {
public int number {get; set;}
}
But then you'll be casting when you retrieve the node from the tree, no improvement over casting the Tag property. And you ought to override the Clone() method.
A cleaner approach is to leverage TreeNode.Name and make that a key in a dictionary to find your custom data back. Good when CustClass gets to be non-trivial. The Name property isn't used for anything else.
I'm looking for a way to get the JavaScript code defined inside of onclick.
I'm using .NET 2.0 C# Visual Studio 2005.
Example:
<span id="foo" onclick+"window.location.href='someURL'>click here</span>
My goal is to get the string "window.location.href='someURL'".
Scenario:
A user clicks on web page element, the tag shown above for instance, inside of WebBrowser control. Then the clicked tag is refereed to HtmlElement object.
In WebBrowser control I then call HtmlElement object's getAttribute("onclick"), it just gives me "System.__ComObject".
I've searched how to deal with it then found that it can be casted then get the value.
if (tag.GetAttribute("onclick").Equals("System.__ComObject"))
{
Console.WriteLine("dom elem >>>>>>>>>>> " + tag.DomElement.ToString());
mshtml.HTMLSpanElementClass span = (mshtml.HTMLSpanElementClass)tag.DomElement;
Console.WriteLine("js value ===>" + span.onclick);
}
Output:
dom elem >>>>>>>>>>> mshtml.HTMLSpanElementClass
js value ===> System.__ComObject
As it shown, span.onclick still give me System.__ComObject, what am I doing wrong?
In Why does HtmlElement's GetAttribute() method return “mshtml.HTMLInputElementClass” instead of the attribute's value? this guy said it worked in his case, and I've followed it, but mine is somewhat not working...
UPDATE
Research, research.....
I can add reference VisualBasic.dll to my C# project then call the method to find out who is this System.__ComObject really is.
Console.WriteLine(Microsoft.VisualBasic.Information.TypeName(span.onclick));
Output:
JScriptTypeInfo
It looks like this is a JScript type... how can I access this object?
More detail
The above description is based on my current project. The project is to create something like Selenium IDE. It uses WebBrowser control instead.
Selenium IDE creates 3 different things to record an element in the web document.
1. actionType
2. xpath
3. value
For instance,
type, //input[#id=foo], "hello world"
clickAndWait, //link=login, ""
Selenium IDE recognize page load so it changes actionType between "click" and "clickAndWait". My case, I want to make it simple.
If I click on the element and if it is anchor tag or has page load kind of javascript
such as onclick=window.location.href='blah' then I want to set the actionType to "clickAndWait".
There are number of ways you can do it.
There is an Event object in DOM, which will give you information about which element generated this event.
You can look at here, http://msdn.microsoft.com/en-us/library/ff975965%28v=VS.85%29.aspx
This one is good, you can use this easily, you will get the event object as method parameter which you can investigate parameters to find out the source of the event. http://support.microsoft.com/kb/312777
Another alternative is to use a custom navigation url and act upon it
Override BeforeNavigate event
Check for Navigation url if it contains "mycommand:click" or "mycommand:clickandwait" 3. If it contains any of this, then set cancel as true. (this will stop navigation by browser).
Then you can navigate your webbrowser code from your C# code and pass cancel as true.
Another Alternative method is to use External object, WebBrowser allows you to set an ObjectForScripting which you can access within Javascript of HTML.
ObjectForScripting in .NET 2.0
[ComVisible(true)]
public class MyClass
{
// can be called from JavaScript
public void ShowMessageBox(string msg){
MessageBox.Show(msg);
}
}
myBrowser.ObjectForScripting = new MyClass();
// or you can reuse instance of MyClass
And you can call,
window.external.ShowMessageBox("This was called from JavaScript");
Cast the element object to mshtml.IHTMLDOMNode, then read the attributes via IHTMLDOMNode.attributes. HtmlElement.GetAttribute is getting the IDispatch interface of the jscript function generated from the embedded attribute.
As per Sheng Jiang's response, here is some working sample:
IHTMLElement element = YourCodeToGetElement();
string onclick = string.Empty;
IHTMLDOMNode domNode = element as IHTMLDOMNode;
IHTMLAttributeCollection attrs = domNode.attributes;
foreach (IHTMLDOMAttribute attr in attrs)
{
if (attr.nodeName.Equals("onclick"))
{
string attrValue = attr.nodeValue as string;
if (!string.IsNullOrEmpty(attrValue))
{
onclick = attr.nodeValue;
break;
}
}
}
You can try to parse webBrowser1.DocumentText property using HtmlAgilityPack and then get desired result using XPath.
If you don't HAVE to do it with C# (you can do it with JS and create a Postback) you should take a look at THIS question.
You can parse it yourself easily, by first reading obj.outerHtml. That should give you the entire html for that obj, then search it for the value onclick="????" and extract the ???? part.
I'm using the XamlServices.Transform to take an object model and serialize it to Xaml.
I've implemented a class which inherits from XamlXmlWriter which overrides WriteValue. I'm using this to reinstantiate a custom MarkupExtension back into the rendered Xaml. My code works fine except when the value of the property is null, in which case the WriteValue doesn't fire and I don't get chance to "swap out" the value in the overriden class.
A related issue is where a property has the same value as that specified by the System.ComponentModel.DefaultValue() attribute. For example say i've got a property in my object model decorated like this:
[DefaultValue(true)]
public Boolean IsVisible {get; set;}
Then the WriteValue method only fires if the IsVisible property is false (which kind of makes sense).
The Remarks section in the documentation (http://msdn.microsoft.com/en-us/library/system.xaml.xamlxmlwriter.writevalue.aspx) mentions something about null values, but I don't understand it:
The input value may be null, which
supports explicitly writing out null
as a serialized value. This behavior
uses the XamlLanguage.Null definition
as WriteStartObject input and then
immediately calls WriteEndObject.
How to I a) make the "WriteValue" fire when the property is null, and b) make the "WriteValue" fire when the property is the same as the DefaultValue Attribute?
I'm not sure if they are related, a solution for either of them would be very welcome.
Thanks,
Daniel
To answer ‘b’ first: The XamlObjectReader’s intended behavior is to skip properties whose value is the declared “default value” and we have no configuration feature to override that. Note that “default value” here is the one declared with the [DefaultValue()] attribute, not the C# language default(T), so things may not be as bad as you fear. I mean not every “0” in an “Int” property is skipped because it is the “default value”.
‘a’: The XamlXmlWriter’s output Node stream for a null value is not “WriteValue(null)”, but instead is “WriteStartObject(nullExtensions); WriteEndObject()”. This is the behavior the documentation you quoted was describing. So you should be fine. Look for StartObject “nullExtension” instead of value “null”.
I got below code from http://msdn.microsoft.com/en-us/library/dd584174(office.11).aspx for adding custom property in webpart tool pane. What does square bracket ([]) mean in the below code?
[Category("Custom Properties")]
[WebPartStorage(Storage.Personal)]
[FriendlyNameAttribute("Custom Color")]
[Description("Select a color from the dropdown list.")]
[Browsable(true)]
[XmlElement(typeof(System.Drawing.KnownColor))]
public System.Drawing.KnownColor MyColor
{
get
{
return _myColor;
}
set
{
_myColor = value;
}
}
As #Spencer Ruport said, they're attributes. They're used within .NET for declarative programming.
You can find information on each of these attributes at MSDN. However, you should know that the name of the attribute can be shortened. In your case, for example, Category is the short form of the class name CategoryAttribute and XmlElement is the short form of the class name XmlElementAttribute. When declaring attributes, the Attribute portion of the class name can be left out.
I've used most of these attributes in conjunction with the PropertyGrid control (see here for an example), although in your case, they are used for a Web Part property pane. The purpose is still the same. The attributes are used by the control to know how to display the property to the user. By using a combination of the various attributes that the control understands, it is possible to declaratively dictate this behavior.
I hope that helps a little bit, but Spencer is correct, you'll learn a lot more reading about attributes via Google than I can explain here.
They're called attributes.
Here's a quick example of how they can be used: http://www.codeproject.com/KB/cs/attributes.aspx