This question already has answers here:
Better way to find control in ASP.NET
(9 answers)
Closed 9 years ago.
Let's say i have this markup structure
<asp:Repeater id="rptItems" datasource="getItemsList()" runat="server" OnItemDataBound="rpAereos_ItemDataBound">
<ItemTemplate>
<asp:Panel id="tableHolder" runat="server">
<asp:table ID="TableHolded" runat="server">
<asp:TableRow>
<asp:TableCell>
<asp:Panel runat="server" ID="panelToFind">Test</asp:Panel>
</asp:TableCell>
</asp:TableRow>
</asp:table>
</asp:Panel>
</ItemTemplate>
</asp:Repeater>
Now on ItemDataBound event I want to find the element panelToFind, but I don't want to go through all the elements to find this element like e.Item.FindControl("tableHolder").FindControl("tableHolded").AReallyLongCallChainUntilMyItem ... , I want to find anything under the tableHolder panel that has the id panelToFind, How would my ItemDataBound event look?
I would like to know if something like: e.Item.FindControl("tableHolder").FindAny("panelToFind")
Declare an extension method like this:
public static class ControlExtensions
{
public static IEnumerable<Control> GetEnumerableChildren(this Control control)
{
return control.Controls.Cast<Control>();
}
public static Control FindAny(this Control control, string id)
{
var result = control.GetEnumerableChildren().FirstOrDefault(c => c.ID == id);
if (result != null)
return result;
return control.GetEnumerableChildren().Select(child => child.FindAny(id)).FirstOrDefault();
}
}
Then do:
var foundControl = e.Item.FindControl("tableHolder").FindAny("panelToFind");
Note will return null if no control exists with that id.
Related
Ok I have a many to many relationship like this:
Walk = {WalkID, title, ...., (Navigation Properties) Features}
Feature = {FeatureID, featureName, description, (Navigation Properties) DogWalks}
I do of course have a junction table, but EF assumes this thus it is not shown in my edmx diagram:
WalkFeatures = {WalkID, FeatureID} //junction, both FK
So using LINQ with EF, I am now trying to grab the features for the Walk at WalkID=xx.
This is my formview:
<asp:FormView ID="FormView1" runat="server" ItemType="Walks.DAL.Walk" SelectMethod="FormView1_GetItem">
<ItemTemplate>
<h1><%# Item.Title %></h1>
...
</ItemTemplate>
</asp:FormView
<asp:Label ID="lbFeatures" runat="server" Text="Label"></asp:Label>
And selectMethod:
public Walks.DAL.Walk FormView1_GetItem([QueryString("WalkID")] int? WalkID)
{
using (WalkContext db = new WalkContext())
{
var walk = (from n in db.Walks.Include("Features")
where n.WalkID == WalkID
select n).SingleOrDefault();
foreach(var f in walk.Features){
lbFeatures.Text += f.FeatureName + "<br/>";
}
return walk;
}
}
The code runs fine, but is there a way that I can display the Walk.Features directly inside the <ItemTemplate> of the formview rather than using a label and a loop? Can the attribute be directly binded like the other properties in the .aspx page?
I have also used this new feature not that extensively but just gave it a try for this particular scenario and this is what I have:-
Simply return walk from FormView1_GetItem method and don't manipulate your label control there. Now, you can use a Repeater control to display the lbFeatures control (since it is going to repeat dynamically) like this:-
<ItemTemplate>
<h1><%# Item.Title %></h1>
<asp:Repeater ID="lbFeatures" runat="server" DataSource='<%# Item.Features%>'>
<ItemTemplate>
<asp:Label ID="lblTest" runat="server"
Text='<%# Eval("FeatureName") %>'></asp:Label>
<br />
</ItemTemplate>
</asp:Repeater>
</ItemTemplate>
As you can see I am able to assign the datasouce of repater control as Item.Features, then use the conventional approach to bind the label. This looks clean and simple :)
How can i use it in Repeater ItemTemplate ?
foreach(string tag in Eval("etiketler"))
{
Response.Write("<a href='#'>"+tag+"</a");
}
there is no way to do it in this format. you should send Dataitem to a static method and then you should return string that contains your markup..
public static string GetMarkup(object dataItem)
{
var tags = DataBinder.Eval(dataItem, "etiketler"); // depend on etiketler type
StringBuilder sb = new StringBuilder();
foreach(string tag in tags)
{
sb.Append("<a href='#'>"+tag+"</a");
}
return sb.ToString();
}
use this method like this:
<%# GetMarkup(Container.DataItem) %>
<%# is not made to use foreach with it, neither if or other language keywords. It's mainly done to databinding.
But you can :
Do it in codebehind with the _ItemDataBound event of the repeater (hugely recommanded for complex processings/formatting).
See http://msdn.microsoft.com/fr-fr/library/system.web.ui.webcontrols.repeater.itemdatabound%28v=vs.110%29.aspx
Call a method of the page which takes in parameter the DataItem and creates the data you want. See How do you pass a Container.DataItem as a parameter? for example
Hope this helps
You can also use nested repeaters in ASP.NET
<asp:Repeater runat="server" ID="rpt1">
<ItemTemplate>
<asp:Repeater runat="server" ID ="rpt2" DataSource='<%# Eval("etiketler") %>'>
<ItemTemplate>
<%# Eval("tag") %>
</ItemTemplate>
</asp:Repeater>
</ItemTemplate>
</asp:Repeater>
I have the next structure for a menu item:
class MenuItem
{
public string Path, Title;
}
I want to be able to Iterate an object of MenuItem[], creating a new object of asp:HyperLink on each iteration, and to add it to a <ul> list.
One thing that must happen is that each HyperLink will be inside a <li> tag.
How can I do that?
You can use a repeater. In the aspx:
<asp:Repeater ID="repMenuItems" runat="server">
<HeaderTemplate>
<ul>
</HeaderTemplate>
<ItemTemplate>
<li><asp:HyperLink ID="lnkMenuItem" runat="server" Text='<%# Eval("Title") %>' NavigateUrl='<%# Eval("Path")%>'/></li>
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
And in the codebehind:
repMenuItems.DataSource = arrMenuItem; // your MenuItem array
repMenuItems.DataBind();
Additionaly you should change your class code for using Public Properties instead of Public Members, like this:
public class MenuItem
{
public string Title {get;set;}
public string Path {get;set;}
}
I recommend you to read more about Properties in .NET, a nice feature for object encapsulation http://msdn.microsoft.com/en-us/library/65zdfbdt(v=vs.71).aspx
Hope this helps you
ASP.NET 2.0 Web forms
So how can you iterate through all the controls in user control and find a certain type of control and append a event to it?
I have a similar question How do I add a event to an ASP.NET control when the page loads? that deals with adding an event - but this is different if I wanted to find a control.
SCENARIO
The control is a custom control:
<asp:Repeater runat="server" ID="options" OnItemDataBound="options_OnItemDataBound">
<HeaderTemplate>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
</HeaderTemplate>
<ItemTemplate>
<td>
<span>
<asp:Label runat="server" ID="optionName">
</asp:Label>
<asp:DropDownList runat="server" ID="optionValues" CssClass="PartOption">
</asp:DropDownList>
</span>
</td>
</ItemTemplate>
<FooterTemplate>
</tr>
</table>
</FooterTemplate>
</asp:Repeater>
The custom control declaration on the user control:
<td><def:CustomControl id="somePartOptions" runat="server"></td>
In the code behind of the user control, I tried the following in the Page_Load event:
foreach(Control control in partOptions.Controls) {
FindDropDownControl(control);
}
protected void FindDropDownControl(Control controlContainer) {
bool isRepeater = false;
if (controlContainer is Repeater) {
isRepeater = true;
}
if (controlContainer.HasControls()) {
foreach (Control subControl in controlContainer.Controls) {
FindDropDownControl(subControl);
}
}
}
However, the boolean flag is always false. So what am I doing? I am eventually wanting to find the dropdownlist control inside the itemTemplate of the repeater, but I can't even find the repeater.
thanks,
I'm using this method to get list of control in container (on each nesting level):
public static List<Control> GetControlsByType(Control ctl, Type type)
{
List<Control> controls = new List<Control>();
foreach (Control childCtl in ctl.Controls)
{
if (childCtl.GetType() == type)
{
controls.Add(childCtl);
}
List<Control> childControls = GetControlsByType(childCtl, type);
foreach (Control childControl in childControls)
{
controls.Add(childControl);
}
}
return controls;
}
You can use t in this way:
List<Control> repeaters = GetControlsByType(containerControl, typeof (Repeater));
I like to handle and compare a lot of date times in my repeater even I have to work more than one time with the same.
It's a bit ugly, to cast everywhere the Eval("MyDate") like ((DateTime)Eval("MyDate")) to substract 2 datetimes or to compare it, even if you have to do this more than in one operation.
I thought of saving all the evals in a var at start of the repeater?
DateTime mydt1 = Eval("myDate");
DateTime mydt2 = Eval("mydate");
after that, it's easy to do any operations in the whole repeater. Hope you understand my idea. Is this possible? I tried short but everytime errors.
mydt1 - mydt2....
Thank you and best regards.
You could call a method on the code behind page from the repeater using the DateTimes as arguments. The casting logic can be done in the code behind if the goal it to create a cleaner looking aspx page.
Example ASPX:
<asp:Repeater ID="Repeater1" runat="server">
<ItemTemplate>
<asp:Literal
ID="Literal1"
runat="server"
Text='<%# DateFoo(Eval("myDate1"), Eval("myDate2")) %>' />
</ItemTemplate>
</asp:Repeater>
Example C# code behind:
protected string DateFoo(Object o1, Object o2)
{
DateTime? dt1 = o1 as DateTime?;
DateTime? dt2 = o2 as DateTime?;
// Do logic with DateTimes
return "string";
}
If you want to add more logic to your repeater I would suggest you move the binding logic to the code behind:
ASPX:
<asp:Repeater id="myRepeater" runat="server">
<ItemTemplate>
<asp:Literal id="myLiteral" runat="server" />
</ItemTemplate>
</asp:Repater>
CS:
protected override void OnInit(EventArgs e)
{
myRepeater.ItemDataBound += myRepeater_ItemDataBound;
base.OnInit(e);
}
void myRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
// this method will be invoked once for every item that is data bound
// this check makes sure you're not in a header or a footer
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
// this is the single item being data bound, for instance, a user, if
// the data source is a List<User>
User user = (User) e.Item.DataItem;
// e.Item is your item, here you can find the controls in your template
Literal myLiteral = (Literal) e.Item.FindControl("myLiteral");
myLiteral.Text = user.Username + ", " + user.LastLoginDate.ToShortDateString();
// you can add any amount of logic here
// if you need to use it, e.Item.ItemIndex will tell you what index you're at
}
}
I hate evals with a passion.
This is why I use this code to be rid of them forever and go back to strong typing:
public static class DataItemExtensions
{
public static T As<T>(this IDataItemContainer repeater) where T : class
{
return (T)repeater.DataItem;
}
public static dynamic AsDynamic(this IDataItemContainer repeater)
{
return repeater.DataItem;
}
}
Then use it like this:
<asp:Repeater runat="server" DataSource="<%# this.MyObjectCollection %>">
<ItemTemplate>
<%# Container.As<MyObject>().DateTime %>
</ItemTemplate>
</asp:Repeater>
Note that if you use the Datasource like I did, you need to use this.DataBind() on the page.