ASP.NET: Access DataItem from an Event Handler - c#

I have a data-bound, templated control, and inside the templated area I create a control with an event handler, like so:
<tnl:DisplayTree ID="DisplayTree1" runat="server" KeyPropertyName="Id"
ParentPropertyName="ParentDemographic" DataSourceID="DemographicObjectSource">
<ItemTemplate>
<asp:CheckBox ID="DemogSelector" runat="server" OnCheckedChanged="DemogSelector_OnCheckedChanged" />
<asp:Label ID="InlineEditLabel" runat="server" Text='<%#DataBinder.Eval(Container.DataItem, "Name") %>'></asp:Label>
</ItemTemplate>
</tnl:DisplayTree>
Within the event handler, I would like to be able to detect the Key of the item for which the control was created. For example:
protected void DemogSelector_OnCheckedChanged(object sender, EventArgs e)
{
CheckBox selector = (CheckBox)sender;
DisplayTree.TreeNode treeNode = (DisplayTree.TreeNode)selector.Parent.Parent.Parent.Parent;
Label1.Text += (int)treeNode.Key + ", ";
}
As you can see, this approach requires close knowledge of the hierarchy within my DisplayTree.TreeNode class (i.e. I have to know that sender.Parent.Parent.Parent.Parent is where I'll find the DisplayTree.TreeNode object). I would like to make it a bit more robust, so that if my TreeNode hierarchy changes or something, I can access the key without difficulty. What's the best way for me to make this Key available from within the event handler?

Best way is to add a custom attribute to your checkbox
<asp:CheckBox ID="DemogSelector" runat="server" oncheckedchanged="DemogSelector_CheckedChanged" AutoPostBack="true" key='<%# Eval("Id") %>'/>
And then access it using
string key = (sender as CheckBox).Attributes["key"];

One potential way is to add an extension method to the checkbox, which finds the TreeNode for you, it could be implemented as some loop which recursively searches the parents until the TreeNode is found.
That way you just call selector.FindTreeNode(), of course this would fail on any checkbox called outside of your structure.

The best way is to create your own event arguments class that inherits from EventArgs and expose it as a property there. Since you're using a custom or third-party control, I don't have any advice on how to set the property on the event args. A custom events args would look like this:
public class TreeNodeCheckedChangedEventArgs : EventArgs
{
TreeNodeCheckedChangedEventArgs(int nodeKey)
{
NodeKey = nodeKey;
}
int NodeKey { get; private set; }
}

You can set the value attribute on the checkbox
<asp:CheckBox ID="DemogSelector" runat="server" oncheckedchanged="DemogSelector_CheckedChanged" AutoPostBack="true" value='<%#DataBinder.Eval(Container.DataItem, "Id") %>' />
And retrieve server side
string value = ((CheckBox)sender).Attributes["value"];

I´m using kind of a similar solution as others, adding my code if it helps:
In markup:
<ItemTemplate>
<input type="checkbox"
value="<%# Container.DataItemIndex %>" name="CheckedItems" />
</ItemTemplate>
And in eventhandler:
string checkedItemRowIds = Request.Form["CheckedItems"];

Related

Repeater datasource Connection not working

Here is my Code Structure.
Repeater Markup:
<asp:Repeater runat="server" ID="RPMenu" DataSource='<%# Menues.GetAllMainMenu() %>'>
<ItemTemplate>
<%# Eval("MenuName") %><br />
<asp:Repeater runat="server" ID="RPMenuUnder" DataSource='<%# Menues.GetAllMainMenu(Convert.ToInt32(Eval("MenuID"))) %>'>
<ItemTemplate>
<%# Eval("MenuName") %><br />
</ItemTemplate>
</asp:Repeater>
</ItemTemplate>
</asp:Repeater>
Menu Class :
public static List<Menu> GetAllMainMenu(int parrentID = 0)
{
using (Scooterfrøen_Entities db = new Scooterfrøen_Entities())
{
return db.Menu.Where(i => i.ParentMenuID == parrentID).ToList();
}
}
Database table:
MenuID | MenuName | MenuDescription | ParrentMenuID | MenuUrl
I have several rows where ParentMenuID IS 0 and NOT NULL.
But for some reason Repeater control does not list anything on the site.
What could be the reason why Repeater control don't show anything?
I think page still requires to bind even though you have provided datasource in markup.
Write Page.DataBind(); in your page load event:
protected void Page_Load(object sender, EventArgs e)
{
Page.DataBind();
}
How to Use nested Repeater:It seems you have to handle ParentRepeater_ItemDataBound event to bind child repeater control.
Checkout this article available here.
You have to set values and bind repeater
RPMenu.DataSource=<value you want to set>;
RPMenu.DataBind();
A combination of the two existing answers is closest to true.
Rather than run Page.DataBind(), which may have unintended side-effects, run RPMenu.DataBind() in the Page_Load event handler. It will use the DataSource which is set in code-front.
The inner Repeater should automatically be bound. You only need to explicitly databind the outermost Repeater.

Reproduce a content of panel dynamically

Is there any way where we can reproduce the content of a panel.For example i have a panel which has two text box .On clicking more button i would like to have another panel below which again has two text box like the first one or add two text box into the existing panel.My end goal is to add controls as the user clicks on more button and get data from those controls .This is the controls inside the panel that i would like to reproduce
any possible way where i can add the controls as shown in the layout through server side ?Please help!
There are plenty ways to solve this. You could add proper DOM elements by yourself using plain JavaScript or jQuery, or use some JS MV* frameworks, like KnockoutJS (example: http://knockoutjs.com/examples/contactsEditor.html) or AngularJS.
you can obviously add dynamic controls from code behind on button click event of 'more button'.
Click Here for more references:
If you want to achieve this on client side using jQuery, then 'closest()' (to find the source element/row to be repeated nearby to the add/remove button etc., especially if it is in a tabular/grid format) in conjunction with 'clone()' function, (to make a copy of the source element/row) and then you can paste the clone inside the target container.
The following link might help you achieve what you want:
jQuery Clone table row
But doing this in Asp.Net WebForms should be much straight forward.
Also, please be noted that, it would always be much helpful to get a quicker answer by specifying more details(eg., MVC, WebForms etc. in the description, what trials you did to find/fix the problem) and that help save other's time as well. For more info: https://stackoverflow.com/questions/how-to-ask
try this
your aspx page add
<asp:TextBox runat="server" ID="TextBox1" />
<asp:TextBox runat="server" ID="TextBox2" />
<asp:Button Text="Add" ID="btnAdd" OnClick="btnAdd_Click" runat="server" />
<asp:Repeater ID="rpt" runat="server">
<ItemTemplate>
<asp:TextBox runat="server" ID="txt1" Text='<%# Eval("str1") %>' />
<asp:TextBox runat="server" ID="txt2" Text='<%# Eval("str2") %>' /><br />
</ItemTemplate>
</asp:Repeater>
and in code behind
protected void btnAdd_Click(object sender, EventArgs e)
{
List<temp> lst = GetItemFromRpt();
lst.Add(new temp
{
str1=TextBox1.Text,
str2 = TextBox2.Text
});
rpt.DataSource = lst;
rpt.DataBind();
}
private List<temp> GetItemFromRpt()
{
List<temp> lst = new List<temp>();
for (int i = 0; i < rpt.Items.Count; i++)
{
temp obj = new temp();
obj.str1 = ((TextBox)rpt.Items[i].FindControl("txt1")).Text;
obj.str2 = ((TextBox)rpt.Items[i].FindControl("txt2")).Text;
lst.Add(obj);
}
return lst;
}
public class temp // instead of temp you can use whatever your entity class you need
{
public string str1 { get; set; }
public string str2 { get; set; }
}

How to get ControlToValidate control inside validation function when a validation function is used for multiple controls

Ok,
This seems straight forward but I am having trouble finding the solution.
I have 10 PeopleEditor controls. Each one of the PeopleEditor controls has a CustomValidator and the ControlToValidate property is set to that specific PeopleEditor control. I assign a function to the control based on criteria.
It is possible that the same validation function is assigned to multiple CustomValidators which in turn means the function needs to know which ControlToValidate control it is validating.
Is this clear?
The question is:
How do I reference the control from the ControlToValidate property in the validation function server side c# code?
Here are similar issues but they reference client side or inline validation:
How to get the 'controlToValidate' property on ClientValidationFunction? and
Extract value from control in ControlToValidate property in a CustomValidator control?
UPDATE:
I have 10 of these in the .aspx page:
<asp:Label ID="lblPeople0" runat="server" />
<SharePoint:PeopleEditor ID="edtPeople0" SelectionSet="User,SecGroup" AutoPostBack="false" CausesValidation="false" PlaceButtonsUnderEntityEditor="false" Rows="3" AllowEmpty="true" ValidatorVisible="true" runat="server" MultiSelect="true" MaximumEntities="100" ShowCreateButtonInActiveDirectoryAccountCreationMode="true" />
<asp:CustomValidator id="vldPeople0" display="Dynamic" runat="server" ErrorMessage="Error Message." ControlToValidate="edtPeople0" />
In the .aspx.cs page, I assign the validating function like this:
vldPeople0.ServerValidate += new System.Web.UI.WebControls.ServerValidateEventHandler(validate_ThisAndThat);
Then, I have this for the function and need to get the ControlToValidate in order get the ResolvedEntities from it.
/// <summary>
/// Validation function.
/// </summary>
private void validate_ThisAndThat(Object source, ServerValidateEventArgs args)
{
foreach (PickerEntity entity in (ControlToValidate).ResolvedEntities)
{
String tmpPrincipalType = (entity.EntityData["PrincipalType"]).ToString();
if (tmpPrincipalType == "User")
{
if ((entity.EntityData["DisplayName"]).ToString().Contains("aString"))
{
args.IsValid = false;
}
}
}
}
Ok, so the key was to assign an attribute to the CustomValidator...
<asp:Label ID="lblPeople0" runat="server" />
<SharePoint:PeopleEditor ID="edtPeople0" SelectionSet="User,SecGroup" AutoPostBack="false" CausesValidation="false" PlaceButtonsUnderEntityEditor="false" Rows="3" AllowEmpty="true" ValidatorVisible="true" runat="server" MultiSelect="true" MaximumEntities="100" ShowCreateButtonInActiveDirectoryAccountCreationMode="true" />
<asp:CustomValidator id="vldPeople0" display="Dynamic" runat="server" ErrorMessage="Error Message." ControlToValidate="edtPeople0" />
In the .aspx.cs page, I assign the validating function like this:
vldPeople0.ServerValidate += new System.Web.UI.WebControls.ServerValidateEventHandler(validate_ThisAndThat);
vldPeople0.Attributes.Add("peopleEditor", "edtPeople0");
Then, I have this for the function.
private void validate_ThisAndThat(Object source, ServerValidateEventArgs args)
{
CustomValidator thisValidator = (CustomValidator) source;
string strPeopleEditor = (string)thisValidator.Attributes["peopleEditor"];
PeopleEditor peopleEditor = (PeopleEditor)panel1.FindControl(strPeopleEditor);
foreach (PickerEntity entity in peopleEditor.ResolvedEntities)
{
String tmpPrincipalType = (entity.EntityData["PrincipalType"]).ToString();
if (tmpPrincipalType == "User")
{
if ((entity.EntityData["DisplayName"]).ToString().Contains("aString"))
{
args.IsValid = false;
}
}
}
}
Yes, it's possible to do. The OnServerValidate definition can point to the same method (handler). You can cast the sender of the ServerValidateEventArgs to find the object type and ID. Alternatively, give each validator its own callback, and define a central set of validation methods called from each of the callbacks. This way, the logic is in common methods is called for the appropriate handler.
HTH.

Accesing a CheckBox that's inside a Repeater

In my repeater's ItemTemplate I've a CheckBox and a disabled TextBox, I need to implement this idea: TextBox only gets enabled if the the CheckBox is checked .. so I set the CheckBox AutoPostBack to true and I tried to put this code in ItemDataBound. but I can't find my control which is weird because I use the same code but in loop "MyRptr.Item[i].FindControl...." and it works! .. I don't want to loop through all the Items, I just wish If I can know the Item number or location in which the CheckBox was created. and I've also tried to create an event handles for the CheckBox's CheckedChanged event but I can't find the CheckBox either!
protected void MyRptr_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
CheckBox ChkBx = e.Item.FindControl("IsSelected_ChkBx") as CheckBox;
if (ChkBx.Checked == true)
{
TextBox TxtBx = e.Item.FindControl("Value_TxtBx") as TextBox;
TxtBx.Enabled = true;
}
}
<asp:Repeater ID="MyRptr" runat="server"
onitemdatabound="MyRptr_ItemDataBound">
<ItemTemplate>
<asp:CheckBox ID="IsSelected_ChkBx" runat="server" Text='<%# Eval("Item") %>' AutoPostBack="True" OnCheckedChanged="IsSelected_ChkBx_CheckedChanged" />
<asp:TextBox ID="Value_TxtBx" runat="server" Enabled="false"></asp:TextBox>
<asp:HiddenField ID="ID_HdnFld" runat="server" Value='<%# Eval("ID") %>' />
</ItemTemplate>
<SeparatorTemplate>
<br></br>
</SeparatorTemplate>
</asp:Repeater>
So basically I need a clean and simple way to implement my logic and If I could get an explanation for what's happening it would be great, so any ideas =) ?
You can find your textbox as follow, but I think its better use the jQuery instead of server-side event
protected void IsSelected_ChkBx_CheckedChanged(object sender, EventArgs e)
{
var ch = (CheckBox)sender;
var txt = ch.Parent.FindControl("Value_TxtBx") as TextBox;
}

Nested Repeaters in ASP.NET

I have a class that contains hierarchical data. I want to present this data in my ASP.net webapp using nested repeaters. How do I do this? I've only ever done one level of nesting, how do I do say five levels?
Each item can have zero or many sub items. I'm basically just indenting at each subleveling using some css stuff. I do not want to use the treeview control, I want to strictly stick with a repeater.
Update:
My data comes from a database. I have an item datatable with some basic properties.
Item
{
ID,
Name,
Description,
...
}
Then I have a many to many table with:
Parent
{
ParentID,
ChildID
}
I'm iterating through each item and displaying its children; and its children's children. I assume this would best be accomplished with nested repeaters, but I could be wrong.
I've found that the simplest way to do nested repeaters without worrying about databinding events is to just set the DataSource using <%# %> syntax.
For example:
<asp:Repeater runat="server" id="Departments">
<ItemTemplate>
Name: <%# Eval("DeptName") %>
Employees:
<asp:Repeater runat="server" DataSource='<%# Eval("Employees") %>'>
<ItemTemplate><%# Eval("Name") %></ItemTemplate>
<SeparatorTemplate>,</SeparatorTemplate>
</asp:Repeater>
</ItemTemplate>
</asp:Repeater>
This is presuming that your Departments class has an Employees property - eg:
public class Department {
public string DeptName {get; set;}
public IEnumerable<Employee> Employees {get; set;}
}
public class Employee {
public string Name {get; set;}
}
If your outer-repeater object doesn't have a property corresponding to the inner-repeater object you can still use this trick, by adding a method in your code-behind that does the calculation. So your inner repeater might become:
<asp:Repeater runat="server" DataSource='<%# GetEmployees(Container.DataItem) %>'>
and then GetEmployees might look something like:
protected IEnumerable<Employee> GetEmployees(object item) {
var dept = (Department) item;
// then do whatever is necessary to get the employees from dept
return employees;
}
It's always cleaner to deal with the datasource than messing about with ItemDataBound, but this is even more the case when nesting Repeaters:
<asp:Repeater DataSource="<%#ColOfCol%>" runat="server">
<ItemTemplate>
<tr>
<asp:Repeater DataSource="<%#Container.DataItem%>" runat="server">
<ItemTemplate>
<td><%#SomeExtractingMethodLikeEval()%></td>
</ItemTemplate>
</asp:Repeater>
</tr>
</ItemTemplate>
</asp:Repeater>
The inner datasource could also be an evaluated property, or a call to a method that returns the enumeration wanted. Just be aware that it will be called with an object. I prefer to write the specific version, and then overload:
protected IEnumerable<string> GetNames(Family fam)
{
foreach(Person p in fam.Members)
yield return p.FirstName + " " + p.Surname;
}
protected IEnumerable<string> GetNames(object famObj)
{
return GetNames((Family)famObj);
}
One thing to be aware of is that if you want to get the current object in the parent repeater than you have to obtain it with:
((RepeaterItem)Container.Parent.Parent).DataItem
You can nest repeaters without a problem. More then 2 levels deep gets nasty though. Here's how:
The html looks something like this:
<asp:Repeater ID="r1" runat="server" OnItemDataBound="r1_ItemDataBound">
<ItemTemplate>
<!-- top level repeater element template here -->
<asp:Repeater ID="r2" runat="server" onitemdatabound="r2_ItemDataBound">
<ItemTemplate>
<!-- child repeater element template here -->
</ItemTemplate>
</asp:Repeater>
</ItemTemplate>
</asp:Repeater>
The codebehind looks like this:
protected void r1_ItemDataBound(object sender, RepeaterItemEventArgs e) {
Repeater r2 = (Repeater)e.Item.FindControl("r2");
r2.DataSource = yourDataSourceHere; // you'll have to query for appropriate data
r2.DataBind();
}
protected void r2_ItemDataBound(object sender, RepeaterItemEventArgs e) {
// do the same thing here for the 3rd nested repeater if you have a third, and so on
}
<asp:Repeater ID="R1" runat="server">
<ItemTemplate>
<asp:Repeater ID="R2" runat="server">
</asp:Repeater>
</ItemTemplate>
</asp:Repeater>
R1.ItemDataBound += (s, e) =>
{
var r2 = e.Item.FindControl("R2") as Repeater;
r2.DataSource = something;
r2.DataBind();
};
Be aware that FindControl is not recursive, it will only get the children.

Categories

Resources