get checkbox Controls by custom attribute name in C# - c#

I have a collection of checkbox some 40-50 nos and i have set a attribute 'attr-ID' for each checkbox which is a unique value ID in database. how can i get the control by attribute name in c# code. I want to check some of the checkbox according to dB values on page load event.
<input type="checkbox" id="rdCervical" attr-ID='111' runat='server' />

Is this what you want?
protected void Page_Load(object sender, EventArgs e)
{
var cb = FindControlByAttribute<HtmlInputCheckBox>(this.Page, "attr-ID", "111");
}
public T FindControlByAttribute<T>(Control ctl, string attributeName, string attributeValue) where T : HtmlControl
{
foreach (Control c in ctl.Controls)
{
if (c.GetType() == typeof(T) && ((T)c).Attributes[attributeName]==attributeValue)
{
return (T) c;
}
var cb= FindControlByAttribute<T>(c, attributeName, attributeValue);
if (cb != null)
return cb;
}
return null;
}

if (rdCervical.Attributes["attr-ID"] != null)
{
string id = rdCervical.Attributes["attr-ID"];
rdCervical.Checked = true;
}
I assume you are adding the checkboxes programatically. In that case, make a container <div id="checkContainer" runat="server"></div> and put all your checkboxes inside it. Then just use checkContainer.InnerHtml and parse that code with this library. You can then easily use the library API to find elements by attribute, I think the method was SelectNodes
Without this library, there is no easy way to navigate through HTML elements from code.

Related

Check which Controls have Borders c#

I am making a Winforms application. Because I want to redraw some borders I want to loop through the controls and check which controls have a border. Unfortunately I have no idea how to accomplish this.
I know panels and textboxes, etc. have a property BorderStyle but I can not access it while looping through Controls. I use the function from this link : https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.controls?view=netframework-4.8 , to loop through the controls.
If you have a panel you can foreach on the panel. I use form load as an event
private void Form1_Load(object sender, EventArgs e)
{
foreach (var item in this.Controls)
{
switch (item.GetType().Name)
{
case "Label":
if (((Label)item).BorderStyle == BorderStyle.None)
{
//Your commands
}
break;
case "TextBox":
if (((TextBox)item).BorderStyle == BorderStyle.None)
{
//Your commands
}
break;
}
}
}
Or you can check them dynamic
I recommend you to use the dynamic way.
in this way, your app wouldn't encounter exceptions or errors
foreach (var item in this.Controls)
{
//Get item from form
dynamic element = item;
//check if there is any property called Borderstyle exists
var res = item.GetType().GetProperties().Where(p => p.Name.Equals("BorderStyle")).FirstOrDefault();
//if it exists and value is not equal None(Control have border)
if (res !=null && !(res.GetValue(item).Equals("None")))
{
res.SetValue(item, BorderStyle.FixedSingle, null);
//your other commands
}
}
You could use a foreach for every type of control you use (TextBox, Label, etc.)
var controlsWithoutBorders = new List<Control>();
// Check textboxes
foreach (var control in controls.OfType<TextBox>())
{
if (control.BorderStyle != BorderStyle.None)
{
controlsWithoutBorders.Add(control);
}
}
//Check labels
foreach (var control in controls.OfType<Label>())
{
if (control.BorderStyle != BorderStyle.None)
{
controlsWithoutBorders.Add(control);
}
}
Alternatively, you could use a single foreach on all the controls, and try to cast the control to each type. The cast will be successful if the control is actually what you want to cast it to, otherwise it will return null (e.g. trying to cast a Control that is a TextBox to a Label will return null)
var controlsWithoutBorders = new List<Control>();
foreach (var control in controls)
{
var controlAsTextBox = control as TextBox;
var controlAsLabel = control as Label;
if (controlAsTextBox != null && controlAsTextBox.BorderStyle != BorderStyle.None)
{
controlsWithBorders.Add(control);
}
if (controlAsLabel != null && controlAsLabel.BorderStyle != BorderStyle.None)
{
controlsWithBorders.Add(control);
}
}
Though this method is not the fastest at run time, your problem could be cleanly solved with the use of C#'s dynamic typing feature. Consider the below snippet of code.
public void DealWithBorder(List<Control> lsControls) {
foreach(var element in lsControls){
dynamic dynamicElement = element;
try{
BorderStyle style = dynamicElement.BorderStyle;
// Handle if property does exist in the control
}
catch{
// Handle if property doesnt exist in the control
}
}
}
In English, it will try to act as if the property exists in the object but if it does not, an exception will thrown. For more info on dynamic typing, click here.

Dynamically adding rows to ASP.NET repeater in reaction to event

I am trying to create ASP.NET server control (pure code, without ascx template - because control must be completly contained in .dll and it must not rely on external .ascx files), and I have a problem with dynamically adding items to repeater.
I want to add item to repeater in reaction to SelectedIndexChanged event, but when i do second DataBind() in that event, i lose data from ViewModel (for example, textboxes contains default data instead of text entered by user).
Simplified version of my code (in large portion borrowed from MS composite control example - http://msdn.microsoft.com/en-us/library/3257x3ea%28v=vs.100%29.aspx):
[ToolboxData("<{0}:FilterControl runat=server />")]
public class FilterControl : CompositeControl, IPostBackDataHandler
{
private List<FilteringProperty> elements = new List<FilteringProperty>();
private DropDownList filteringElementsDropDownList;
private Repeater usedFiltersRepeater;
[Bindable(true), DefaultValue(null), Description("Active filters")]
public List<FilteringProperty> UsedElements
{
get
{
EnsureChildControls();
if (ViewState["UsedElements"] == null)
{
ViewState["UsedElements"] = new List<FilteringProperty>();
}
return (List<FilteringProperty>)ViewState["UsedElements"];
}
set
{
EnsureChildControls();
ViewState["UsedElements"] = value;
}
}
protected override void RecreateChildControls()
{
EnsureChildControls();
}
protected override void CreateChildControls()
{
Controls.Clear();
filteringElementsDropDownList = new DropDownList { AutoPostBack = true };
usedFiltersRepeater = new Repeater();
foreach (var element in elements)
{
filteringElementsDropDownList.Items.Add(new ListItem(element.DisplayName));
}
filteringElementsDropDownList.SelectedIndexChanged += (sender, e) =>
{
string selectedText = filteringElementsDropDownList.SelectedValue;
FilteringProperty condition = elements.First(x => x.DisplayName == selectedText);
var toRemove = filteringElementsDropDownList.Items.Cast<ListItem>().FirstOrDefault(x => x.Text == condition.DisplayName);
if (toRemove != null)
{
filteringElementsDropDownList.Items.Remove(toRemove);
}
UsedElements.Add(condition);
// ======> A <========
};
usedFiltersRepeater.ItemDataBound += (sender, args) =>
{
FilteringProperty dataItem = (FilteringProperty)args.Item.DataItem;
Control template = args.Item.Controls[0];
TextBox control = (TextBox)template.FindControl("conditionControl");
control.Text = dataItem.DisplayName;
// ======> C <========
};
usedFiltersRepeater.ItemTemplate = // item template
usedFiltersRepeater.DataSource = UsedElements;
usedFiltersRepeater.DataBind();
// ======> B <========
Controls.Add(filteringElementsDropDownList);
Controls.Add(usedFiltersRepeater);
}
}
I marked important portions of code with (A), (B) and (C)
The problem is, (A) is executed after DataBinding (B and C), so changes in UsedElements are not visible until next postback.
It is possible to add usedFiltersRepeater.DataBind(); after (A), but than all controls are recreated without data from viewstate (i.e empty)
Is there a way to dynamically change repeater after databinding, such that data of contained controls is preserved?
Tl;dr - i have a DropDownList and I want to add editable items to Repeater on SelectedIndexChanged (without losing viewstate).
I finally solved my problem.
My solution is rather dirty, but it seems to work fine.
Instead of simple databinding:
I get state from all controls in repeater and save it in temporary variable (state for each control includes everything, such as selected index for dropdownlists) using my function GetState()
modify this state in any way i want
restore full state using my function SetState()
For example:
FilterState state = GetState();
state.Conditions.Add(new ConditionState { Item = condition });
SetState(state);

How can, or can you access a group of controls or elements with code behind?

Is there a way to access a group of controls in ASP.NET somehow?
In jQuery you can access multiple elements using a class="somegroup" like
$('.somegroup')...
In ASP.NET I understand I can access an element or control using the ID, but is there a way to access multiple controls or elements at once?
For example, let's say I have this in design view:
<asp:Label ID="label1" CssClass="someclass"></asp:Label>
<asp:Label ID="lbl" CssClass="someclass"></asp:Label>
<asp:Label ID="lb2" CssClass="someclass"></asp:Label>
Now I want to turn the visibility off on all of them.
Instead of doing this:
label1.Visible = false;
lbl.Visible = false;
lb2.Visible = false;
Is there an equivalent to this?
someclass.Visible = false;
Is there possibly a different tag property I could be using?
using asp.net and C#
You may write your own function, pass a string class into it (and optionally a parent control or form) then loop thru Controls collection checking for the CssClass property and making needed modifications for matched controls.
Something like
void hide(Control el, string cssClass) {
foreach (WebControl c in el.Controls)
{
if (c.CssClass == cssClass)
{
c.Visible = false;
}
}
}
and call
hide(this, "someclass");
public void Apply(string selector, Control parent, Action<WebControl> a)
{
if (selector.StartsWith("."))
{
foreach(WebControl wc in parent.Controls)
{
if (wc.CssClass == selector.Substring(1))
{
a(wc);
if (wc.HasControls())
{
Apply(selector,wc,a);
}
}
}
}
if (selector.StartsWith("#"))
{
foreach (WebControl wc in parent.Controls)
{
if (wc.ID == selector.Substring(1))
{
a(wc);
return;//no need to search any further.
}else
{
if (wc.HasControls())
{
Apply(selector, wc, a);
}
}
}
}
}
Maybe this will help?
then you can do this:
Apply(".SomeClass", this, a => a.CssClass="SomethingElse");
There is no baked-in syntax for doing a selector-type operation in C#.
You can however write your own loop to do this, like this:
private void SetAllLabelsWithCssClassValueToInvisible(Control parentControl,
string className)
{
foreach(Control childControl in parentControls.Controls)
{
// Try to cast control to a label, null if it fails
var label = childControl as Label;
// Check to see if we successfully cast to label or not
if(label != null)
{
// Yes, it is a label
// Does it have the correct CssClass property value?
if(label.CssClass == className)
{
// Update the Visible property to false
label.Visible = false;
}
}
}
}
Note: Obviously you can expand/improve upon this notion and make it fit your needs, just a proof of concept that is very specific for Label controls with a particular CssClass value and the Visible property.
Internally, the jQuery Sizzle engine is doing this looping for you.

Reflection to add css class to body tag c#

I have masterpage which has runat="server" & Id set on body tag. see below
<body id="MasterPageBodyTag" runat="server">
code behind on masterpage I've added the following code:
public HtmlGenericControl BodyTag
{
get { return MasterPageBodyTag; }
set { MasterPageBodyTag = value; }
}
now I want to add css class to body tag from Class1.cs file in App_code folder.
On the .aspx am passing the master page control using the following code:
protected void Page_Load(object sender, EventArgs e)
{
backend.FindPage((PageTemp)this.Master);
}
Now on Class1.cs I have the following
public static void FindPage(Control mp)
{
Page pg = (Page)HttpContext.Current.Handler;
PropertyInfo inf = mp.GetType().GetProperty("BodyTag");
}
I want to add the following to found BodyTag
// BodyTag.Attributes.Add("class", "NewStyle");
But can't seem to find a way to add atrribute or cast the inf to HtmlGenericControl.
Any help would be great.
Rather than having a dependency on the Master Page type, I'd simply use FindControl to search for the body element by Id. Assuming the body tag is on your top-level Master page, and also assuming you may be using nested master pages, it would look something like:
private static MasterPage GetTopLevelMasterPage(Page page)
{
MasterPage result = page.Master;
if (page.Master == null) return null;
while(result.Master != null)
{
result = result.Master;
}
return result;
}
private static HtmlGenericControl FindBody(Page page)
{
MasterPage masterPage = GetTopLevelMasterPage(page);
if (masterPage == null) return null;
return masterPage.FindControl("MasterPageBodyTag") as HtmlGenericControl;
}
private void UpdateBodyCss()
{
HtmlGenericControl body = FindBody(this);
if(body != null) body.Attributes.Add(...);
}
You could even remove the dependency on the id by searching for an HtmlGeneric control with a tag name of "body":
private static HtmlGenericControl FindBody(Page page)
{
MasterPage masterPage = GetTopLevelMasterPage(page);
if (masterPage == null) return null;
foreach(Control c in masterPage.Controls)
{
HtmlGenericControl g = c as HtmlGenericControl;
if (g == null) continue;
if (g.TagName.Equals("body", StringComparison.OrdinalIgnoreCase)) return g;
}
return null;
}
You need the add the following on the aspx file:
<%# MasterType VirtualPath="~/Your_Master_Page.Master" %>
and then you can do at the .cs of your page:
Master.BodyTag.Attributes.Add("class", "NewStyle");
What you're doing seems a bit convoluted and there are probably easier ways to deal with this.
To answer your question, you don't need to use reflection. You can simply cast the parameter you're passing to FindPage to the master page type you've created.
You haven't specified the type name of your master page so I'll give it the name MyMasterPage.
So FindPage should look like:
public static void FindPage(Control mp)
{
var masterPage = mp as MyMasterPage;
if (mp != null)
{
mp.BodyTag.Attributes.Add("class", "NewStyle");
}
}

Filling in Text of a VBulliten Private message

Im trying to submit a private message in a webforum through C#.
I can fill in every component except for the message box itself:
nk
The usual method of setting the innertext element of the textarea does nothing at all, it works for the rest of the page but not this?? I have no idea why and i can confirm the code is correctly identifying this area.
I can only imagine something else truly controls the display and submission value.
I have found :
When I set the value to anything but 0, the message will post but the text is missing each time.
Any ideas?
I don't know what is your favorite method to access a web page, but you can create a dummy WebBrowser form, then use a function like it:
void SetText(string attribute, string attName, string value)
{
HtmlElementCollection tagsCollection = webBrowser1.Document.GetElementsByTagName("input");
foreach (HtmlElement currentTag in tagsCollection)
{
if (currentTag.GetAttribute(attribute).Equals(attName))
currentTag.SetAttribute("value", value);
}
}
void CheckBox(string attribute, string attName, string value)
{
// Get a collection of all the tags with name "input";
HtmlElementCollection tagsCollection = webBrowser1.Document.GetElementsByTagName("input");
foreach (HtmlElement currentTag in tagsCollection)
{
if (currentTag.GetAttribute(attribute).Equals(attName))
currentTag.SetAttribute("checked", value);
}
}
void ClickButton(string attribute, string attName)
{
webBrowser1.Document.GetElementsByTagName("input");
HtmlElementCollection col = webBrowser1.Document.GetElementsByTagName("button");
foreach (HtmlElement element in col)
{
if (element.GetAttribute(attribute).Equals(attName))
{
element.InvokeMember("click");
}
}
}

Categories