I am wondering if I am able to access Html elements in aspx from code behind by custom attribute name. Like
HtmlElement[] allElements = Page.FindElementByCustomName("custom-name");
and it will give me an array of all elements with that attribute suppose my aspx is as below
<a runat="server" custom-name = "any">Faf</a>
<a runat="server">AB</a>
<a runat="server" custom-name = "any">Amla</a>
and allElements will have two a elements i.e
<a runat="server" custom-name = "any">Faf</a>
<a runat="server" custom-name = "any">Amla</a>
Is it possible?
You can iterate through all controls in a page, but it will have to be done recursively. For example, start with Page.Controls, then, for each control, iterate through its Controls collection. For a control to take attributes it needs to implement IAttributeAccessor; you can check if the control in your iteration implements this interface. It is an interface that is required when you are to insert custom attributes on markup. For example, WebControl implements it. If now, when you try to add a custom attribute, ASP.NET will fail saying that there is no property with that name.
Something like:
public static void ListControls(ControlCollection controls, List<Control> controlsFound)
{
foreach (var control in controls.OfType<Control>())
{
if (control is IAttributeAccessor)
{
controlsFound.Add(control);
ListControls(control.Controls, controlsFound);
}
}
}
Which you should call from your page:
var controlsFound = new List<Control>();
ListControls(this.Controls, controlsFound);
In the end, just iterate through controlsFound, which you know is a collection of IAttributeAccessor and retrieve attribute attribute-name:
var attr = (control as IAttributeAccessor).GetAttribute("attribute-name");
Related
I've been trying to use mshtml to modify a third party web api. Right now I am trying to change the display property on two elements to none so they will be invisible.
I know their id's.
The first one is an img and the id is zendbox_close. The second is a div and the id is zenbox_scrim.
The html looks like this
<div class="zenbox_header">
<img id="zenbox_close"></img>
</div>
...
<div id="zenbox_scrim...></div>
All I want to do is add in some inline styling so it looks like this
<div class="zenbox_header">
<img id="zenbox_close" style="display:none;"></img>
</div>
...
<div id="zenbox_scrim style="display:none;"...></div>
In my code behind for the WPF WebBrowser that is opening up the webpage, I have gotten this far:
IHTMLDocument3 doc = (IHTMLDocument3)this._browser.Document;
IHTMLImgElement element = (IHTMLImgElement)doc.getElementById("zenbox_close");
I saw in another post someone was talking about injecting scripts and they said you can use
IHTMLElement scriptEl = doc.CreateElement("script");
I am not sure what the HTML element analog to this would be though. Also I had to use IHTMLDocument3 to use the method getElementById, but that class doesn't appear to contain anything similar to CreateElement().
My question is how can I inject inline styling into a Document being loaded in my WPF WebBrowser?
Yes you can manipulate styles inline.
A good way to do this is to work with the IHTMLStyle or IHTMLCurrentStyle interfaces when working with an IHTMLElement. There are some differences with the values reflected by those two and they are not always synch'd. A better explanation of why that is:
IHTMLStyle vs IHTMLCurrentStyle
Code sample would look like:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
wb.LoadCompleted += wb_LoadCompleted;
wb.Source = new Uri("http://www.google.com");
}
void wb_LoadCompleted(object sender, NavigationEventArgs e)
{
var doc = wb.Document as HTMLDocument;
var collection = doc.getElementsByTagName("input");
foreach (IHTMLElement input in collection)
{
dynamic currentStyle = (input as IHTMLElement2).currentStyle.getAttribute("backgroundColor");
input.style.setAttribute("backgroundColor", "red");
}
}
}
I am working on a solution in C# and ASP.NET 4.0 I am trying to get the value of a radiobutton from my page that was dynamically created based on some database information.
Here is what gets generated in the page source:
<td>
<input id="masterMain_3Answer_0" type="radio" name="ctl00$masterMain$3Answer"
value="Y" onclick="return answeredyes(3);" />
<label for="masterMain_3Answer_0">Y</label>
</td>
<td>
<input id="masterMain_3Answer_1" type="radio" name="ctl00$masterMain$3Answer"
value="N" onclick="return answeredno(3,'desc');" />
<label for="masterMain_3Answer_1">N</label>
</td>
Inside the OnClick function of my submit button I want to gather wether Y or N has been selected based on the user's input.
Here is what I have written so far:
RadioButton _rbAnswer = new RadioButton();
RadioButtonList _rbList = new RadioButtonList();
ContentPlaceHolder cp = (ContentPlaceHolder)Master.FindControl("masterMain");
_rbAnswer = (RadioButton)Master.FindControl("masterMain_3Answer_0");
HtmlInputRadioButton rb = (HtmlInputRadioButton)Master.FindControl("masterMain_3Answer_0");
_rbAnswer = (RadioButton)cp.FindControl("masterMain_3Answer_0");
_rbList = (RadioButtonList)cp.FindControl("masterMain_3Answer_0");
I am able to get the ContentPlaceHolder without any issues but the rest of the objects are null after it attempts to get the . I have also attempted removing the "masterMain_" but still doesn't want to find the controls.
Here is the code in which the individual radiobuttonlists are added
TableRow _tempRow = new TableRow();
TableCell _cellOK = new TableCell();
RadioButtonList _rbList = new RadioButtonList();
_rbList.ID = r[0].ToString()+"Answer";
_rbList.RepeatDirection = RepeatDirection.Horizontal;
//add options for yes or no
ListItem _liOk = new ListItem();
_liOk.Value = "Y";
ListItem _linotOk = new ListItem();
_linotOk.Value = "N";
_rbList.Items.Add(_linotOk);
//add cell to row
_rbList.Items.Add(_liOk);
_cellOK.Controls.Add(_rbList);
_tempRow.Cells.Add(_cellOK);
//add the row to the table
stdtable.Rows.Add(_tempRow);
To be able to quickly find dynamically created controls, add a dictionary to your page class:
private Dictionary<string, Control> fDynamicControls = new Dictionary<string, Control>();
then when a new control is created in code and its ID is assigned:
fDynamicControls.Add(newControl.ID, newControl);
and when you need control's reference:
Control c = fDynamicControls["controlIdThatYouKnow"];
When using FindControl don't use the id that's generated by the page. Use the ID that you specified inthe aspx.
If this is inside a Repeateror another DataBound control, you have to first find the current record. (GridViewRow or RepeaterItem) first, an use that item's .FindControl function.
See this (different - not duplicate) question to see a code example of how to do it: How to find control with in repeater on button click event and repeater is placed with in gridview in asp.net C#
When you create dynamic controller give specific ids for them. This facilitate to generate controls with our own id. therefore then we can access the controls with this id.
And also use OnInit life cycle event to generate dynamic controllers, this is the best place to generate them.
RadioButton _rbAnswer = new RadioButton();
_rbAnswer.ID="ranswerid";
Given your update, you'll find that your control heirarchy is fairly deep. You have a RadioButtonList inside a cell inside a row inside a table ...
FindControl is a method that needs to be called on a specific object and can only find objects that are actual children of that object. In this case, you either need to build a recursive method or go directly to the control in question. Since so many of these controls are generated dynamically, you'll have no real way of accessing them directly so building the recursive function may be simplest. However, on very large pages this method can be very resource consuming:
public static WebUserControl FindControlRecursive(this WebUserControl source, string name)
{
if (source.ID.Equals(name, StringComparison.Ordinal))
return source;
if (!source.Controls.Any()) return null;
if (source.Controls.Any(x => x.ID.Equals(name, StringComparison.Ordinal))
return source.FindControl(name);
WebUserControl result = null;
// If it falls through to this point then it
// didn't find it at the current level
foreach(WebUserControl ctrl in source.Controls)
{
result = ctrl.FindControlRecursive(name);
if (result != null)
return result;
}
// If it falls through to this point it didn't find it
return null;
}
This is an extension method that would allow you to call this on your ContentPlaceHolder control:
var _cp = (ContentPlaceHolder)Master.FindControl("masterMain");
RadioButtonList _rbList = _cp.FindControlRecursive("3Answer");
if (_rbList != null)
// ... Found it
Note: Treat the above as psuedo-code. It has not be implemented by me anywhere so may (likely) require tweaking to behave exactly right.
For example, if i have on the aspx page:
<asp:PlaceHolder ID="tab_0" runat="server" Visible="false"></asp:PlaceHolder>
<asp:PlaceHolder ID="tab_1" runat="server" Visible="false"></asp:PlaceHolder>
and i want to access these properties in the code behind page using values from a configuration file for example
string enabledTabs = "0,1,2,3";
if there a way i can use reflection to set them to enabled or disabled e.g.
foreach(var id in enabledTabs.Split(','))
{
// <use reflection to get the correct tab control>
// Set property of the tab
tab.Visible = true;
}
I could acheive the result i want by using a switch statement and setting the particular control property, but i'd like to use reflection to get the tab to make it cleaner.
Could anyone help?
Thanks!
You don't need reflection. Use Page.FindControl:
foreach(var id in enabledTabs.Split(','))
{
PlaceHolder control = (PlaceHolder)this.FindControl("tab_"+id));
control.Visible = true;
}
foreach(var id in enabledTabs.Split(','))
{
// Set property of the tab
Page.FindControl("tab_" + id.ToString()).Visible = true;
}
Try the following:
Control tab = Control.FindControl("tab_"+id);
My User Control has the following properties:
private String _requestIP;
public String RequestIP
{
get { return _requestIP; }
set { _requestIP = value; }
}
When adding a instance of the Control to an aspx page at design time it's easy to assign the attributes which can be utilized in the codebehind file...
<uc:Item ID="Testing" runat="server" RequestIP="127.0.0.1" />
However, if I try to create the control at runtime in the aspx.cs file, how am I able to assign values to these attributes?
Control ItemX = (Control)Page.LoadControl("/controls/item.ascx");
There is no ItemX.Attributes.Add() method which I would expect to be there, and no ItemX.RequestIP property to set.
Is there a way to set this dynamically in the aspx page using <%= Users_IP_Address %> tags or some other method?
Well, you just need to cast it to the appropriate type (whatever the class name of your user control is).
I have a custom control which includes a property of the following definition:
[PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate Template {
get { return template; }
set { template = value; }
}
The control overrides CreateChildControls(), and adds several HtmlGenericControls and an asp:Panel control.
The actual actual implementation of the control looks something like this:
<user:Frame runat="server">
<Template>
<asp:Literal runat="server" ID="SomeControl" Text="SomeValue" />
</Template>
</user:Frame>
While the page renders as intended, it has a number of consequences of varying severity, including:
Controls enclosed within the Template cannot be referenced directly, and FindControl is required. This is fine.
I've been unable to use validation controls on them.
Is there a better way to design my custom control? Or perhaps just a way to get validation working?
By default the framework assumes that you may have more than one template in a control, like say in a Repeater. In your case you have to tell it that you intend to have a single template by using the TemplateInstance property. E.g.
[PersistenceMode(PersistenceMode.InnerProperty)]
[TemplateInstance(TemplateInstance.Single)]
public ITemplate Template {
get { return template; }
set { template = value; }
}
This will allow you to reference the templated controls directly, and should fix your validation problems as well.
One way to get validation to work in this case is to add the validation controls programatically. For example:
var c = parentControl.FindControl("id");
parentControl.Controls.AddAt(
parentControl.Controls.IndexOf(c) + 1,
new RequiredFieldValidator() { ControlToValidate = c.D });