Is it possible to grab the cell data from Repeater items property such as
Dim test As String = myRepeater.Items(index).DataItem(2)
where myRepeater has x rows and 10 columns and was bound from the DataTable
Seems like it should be trivial, but looks like individual cells CAN NOT be accessed. Please shed some light on this.
Not directly -- remember, repeaters are very, very simple webcontrols and the repeater itself really has a limited idea of what is underneath it.
But you can easily get at items within. The best method is to use a control within the item to help you find stuff. EG, given this repeater "row":
<ItemTemplate>
<asp:Button runat="server" ID="theButton" text="Click Me!" OnClick="doIt" /> <asp:TextBox id="theTextBox" value="Whateeavs" />
</ItemTemplate>
You can use the button's click handler to find other items in the row:
protected void doIt(object s, EventArgs e) {
var c = (Control)s;
var tbRef = c.NamingContainer.FindControl("theTextBox");
var tb = (ITextControl)tbRef;
doSomethingWith(tb.Text);
}
Typically much cleaner than finding things in rows using absolute positioning -- you don't have to worry about indexes and such.
PS: it has been a long time since I laid down significant web forms code, no garuntees I got the class names, etc, exactly right.
Related
I'm trying to implement Nick Wesselman's Dynamic Place Holder technique in Sitecore. I'm using sitecore 6.5 and asp.net.
http://www.techphoria414.com/Blog/2011/August/Dynamic_Placeholder_Keys_Prototype
http://www.techphoria414.com/Blog/2012/May/Sitecore_Page_Editor_Unleashed
I used the sourcecode I Found in the Sitecore_Page_Editor_Unleashed BLOG
All the pipelines seem to be in place and working. But in the Dynamic Placeholder control the following bit of code returns 0 (zero) although there are five dynamic placeholders on the control
Stack<Placeholder> stack = Switcher<Placeholder, PlaceholderSwitcher>.GetStack(false);
To Isolate the problem I created a very simple sitecore instance. 1 layout and 1 sublayout.
In the codebehind sublayout I have the following code for demo purposes:
var list = new List<int>();
for (int i = 0; i < 5; i++)
{
list.Add(i);
}
Repeater.DataSource = list;
DataBind();
This is the source of the ascx/sublayout
<asp:Repeater runat="server" ID="Repeater">
<ItemTemplate>
<mi:DynamicKeyPlaceholder runat="server" ID="pl" Key="place"></mi:DynamicKeyPlaceholder>
</ItemTemplate>
</asp:Repeater>
Result is that all five placeholders still have the same key.
What to do?
I've never tested this solution with multiple dynamically created placeholder controls inside the same sublayout. It's designed to solve the problem of the same sublayout (with a placeholder) being placed on a page, multiple times. It works by grabbing the ID of the containing rendering and appending it to the placeholder key. Steps I would take:
Ensure that the sublayout containing your repeater is placed in the layout dynamically, through a placeholder. You should not be using <sc:Sublayout/> to put the sublayout on the page. This may explain why the stack is empty.
You will also need to databind the "Key" field on the DynamicKeyPlaceholder. Within the same sublayout, all the DynamicKeyPlaceholder controls need to have a unique Key.
Of course, the danger here is that if the data driving your repeater changes, the Key could change. You might consider re-evaluating your architecture and driving it based on placing the same sublayout multiple times (with datasources), instead of a repeater.
What I have done is similar what you are requesting...I created a reference of another sublayout (which implements dynamic placeholders) from the calling control (which want to have dynamic ph).
So suppose my calling control is ControlA and implementing control is ControlB.
This is the markup of ControlA:
<ControlB ID="SectionItems" runat="server" CurrentItem="<%# Container.DataItem %>" />
And this is the code behind for it:
private static void SetIndexForControlBItems(RepeaterItemEventArgs e)
{
var controlBItems = e.Item.FindControl("ControlB") as ControlB;
if (controlBItems != null)
controlBItems.PropertyName = e.Item.ItemIndex.ToString();
}
When you assign the property name (which is going to be present in ControlB) then you will get the value of which index you are assigning dynamic PH to. And in ControlB you can do something like this:
private void SetPlaceholderKeys(RepeaterItemEventArgs e)
{
Placeholder phPreColumnContentTopLeft = e.Item.FindControl("phColumnContentTopLeft") as Placeholder;
if (phPreColumnContentTopLeft != null)
phPreColumnContentTopLeft.Key = "topLeftColumnSectionItems" + e.Item.ItemIndex + PropertyName;
Hope this helps!
I have a set of categories that a user can choose from.
Each category has a different set of properties that a user may want to filter on.
The items in each category are displayed in a grid view. Each category has its own web page for the grid view.
When the grid view is displayed I would like a side bar to display properties that pertain to the category. The user should be able to select a property to filter. And filter by min/max values on the property.
I'm trying to determine what controls should go in the sidebar and also how to dynamically populate the set of controls (assuming each one is a distinct property filter).
For example looking at Amazon books the sidebar has a dynamically generated list of filters that pertains to the category of books.
Other nice features would be:
Change the list of properties so that only properties/filters that will produce a result will be displayed.
Have each property/filter show the number of results that will be displayed if selected.
I don't really know how you're going to load the gridview, but this is how I was able to do something similar.
Let's say you're getting your data to the GridView via SQL query:
select property1, property2, property3, ...., from categoryA
Whatever is on the side view should factor into your SQL query somehow, each of them with auto post back.
<asp:TextBox runat="server" AutoPostBack="true" ID="Property1" />
So when it posts back to the server, in your page load method:
protected void Page_Load(object sender, EventArgs e)
{
if(IsPostBack)
{
UpdateCategoryQuery();
}
}
And on your UpdateCategoryQuery() method:
private void UpdateCategoryQuery()
{
if(Property1.Text != "")
{
string sql = "where property1 = '" + Property1.Text + "'";
}
//... and go on down the list.
}
Finally, you will want to read this query and bind the data to the GridView using .DataSource and .DataBind();
It's an extremely simple example, but I didn't really know exactly what you were looking for, so I hope this helps you along.
Edit: The query here can get fairly complicated depending on how many properties you have to filter the data, so you may have to spend some time building it out to make sure it runs correctly.
I'm new to ASP.NET so I'm stuck finding the controls to accomplish
this.
There aren't any controls out of the box, that accomplish this. You have to build your own.
This might not the way you envision, but hopefully is an idea that you can extrapolate:
Since you know the type of objects you are binding to the gridview, create a dropdown box with the list of properties the user can filter on. Next to the drop down, put a min and a max text field so that the user can type in whatever value he needs in those fields. Something like this:
<asp:dropdownlist id="properties" runat="server">
<ListItem Text="Color" Value="Color" />
<ListItem Text="Size" Value="Size"/>
<ListItem Text="Price" Value="Price"/>
</asp:dropdownlist>
<asp:Textbox id="min" runat="server" />
<asp:Textbox id="max" runat="server" />
<asp:Button id="btnFilter" Click="Filter" Text="Filter" />
protected void Filter(object sender, EventArgs e)
{
string minVal = min.Text;
string maxVal = max.Text;
string filterProperty = properties.SelectedValue;
//Now filter your data using the property name and the min and max values
//you can use Linq to do this quickly.
//If binding to a DataTable, use DataTable.Select method and rebind again
}
TL;DR
The Control inside my Repeater requires special instantiation before it can be useful. How do I perform this special instantiation for each control as they are constructed by the repeater?
The Setup
I have three classes:
ComplexData.
Contains many public members of varying types, such as:
int favorite_integer
Food what_you_had_for_breakfast_this_morning
string capital_of_alaska
PhaseOfMoon when_magic_runes_will_reveal_themselves_on_the_mysterious_artifact
etc, etc, etc
ComplexDataDisplay
A control used to render an instance of ComplexData in an aesthetically pleasing way. Exposes a single public member, ComplexMember DataToDisplay. When the user sets DataToDisplay, the labels within the control will populate themselves with the appropriate data.
MultiComplexDataDisplay
A control that uses a Repeater to display multiple ComplexDataDisplays in a row. It contains a private List<ComplexData> datasToDisplay, which is the data source for the repeater. It provides two public methods, addComplexData(ComplexData) and emptyAllData(), which let the user manipulate datasToDisplay.
The Problem
During the data bind process, I don't know how to set each ComplexDataDisplay's DataToDisplay member.
It appears that controls within repeaters are normally populated from the front end, for example
<asp:Repeater runat="server" id="linkRepeater">
<%# getDescription(Container.DataItem) %>
</asp:Repeater>
As I understand it, during data bind, the repeater instantiates each anchor element, using whatever the data source is to set the href and description.
My attempt to replicate this behavior only led to a blank page:
<asp:Repeater runat="server" id="displayRepeater">
<ComplexDataDisplay id="dataDisplay" DataToDisplay="<%# Container.DataItem %>" runat="server"
</asp:Repeater>
The only other thing I can think to try is to modify ComplexDataDisplay so it has a public member for each member of ComplexData. Then within the repeater I can do:
<asp:Repeater runat="server" id="displayRepeater">
<ComplexDataDisplay id="dataDisplay" runat="server"
favorite_integer="<%# get_favorite_integer(Container.DataItem) %>"
what_you_had_for_breakfast_this_morning="<# get_what_you_had_for_breakfast_this_morning(Container.DataItem) %>"
<%--etc etc etc--%>
/>
</asp:Repeater>
This seems highly undesirable because for every member of ComplexData, I'll have to write a corresponding public member in ComplexDataDisplay, and a get_whatever(DataItem) method in MultiComplexDataDisplay. On top of that, I don't even know if it will work, because half of ComplexData's members are complex data types, which may or may not be settable in this way.
What I'm looking for in an answer
One of the following:
A direct answer to the question posed in the TLDR section - a way to perform special instantiation for each control in the repeater during databind, or a way to iterate through them shortly after the fact.
A recommendation on how to best restructure the code, such so that special instantiation is no longer necessary to display my many Complex Datas. I am willing to add/update/delete any class besides ComplexData, as I am obligated to maintain its interface as-is.
best practices/references/documentation related to my problem, which will guide me in finding a solution myself.
You can bind you child controls inside of ItemDataBound, I couldn't follow which objects get bound when so replace where needed:
<asp:Repeater runat="server" id="displayRepeater" OnItemDataBound="displayRepeater_ItemDataBound">
<uc1:ComplexDataDisplay id="dataDisplay" runat="server" />
</asp:Repeater>
protected void displayRepeater_ItemDataBound(Object Sender, RepeaterItemEventArgs e)
{
// Execute the following logic for Items and Alternating Items.
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
//get object bound to row
ComplexData c = (ComplexData)e.Item.DataItem;
//find the child control to bind
ComplexDataDisplay cds = (ComplexDataDisplay)e.Item.FindControl("dataDisplay");
//set properties or any other complex things you need to do
cds.DataToDisplay = c.ComplexMember;
cds.DataBind(); // if this control has repeaters, it's ItemDataBound will fire, repeat this process untill all your controls are bound properly
}
}
I have a gridview for which I am binding data from a Generic List collection. Currently not connected to DB. All the columns in the GridView are defined as properties(get;set.
I want to have tooltip on one of the columns. The column has a very big description. I want to show only 3 words in the column and the rest of the description should appear in tooltip.
Currently my gridview has only asp:gridview id and runat server
There are no columns, headertemplate and itemtemplate.
Could anyone suggest some idea on this.
Thanks in advance
Well, I don't know how you are getting your columns in place...but I suppose you could put a Label webcontrol in the itemtemplate for the header of each column, and give that label's tooltip property the value you want it to have.
Seems to me like that would be a pretty viable solution for this, if I'm understanding you correctly..
You can use an itme template with a label as mentioned. Bind the label the full description:
<ItemTemplate>
<asp:Label id="l1" runat="server" Text='<%# Eval("Description")' />
</ItemTemplate>
In code-behind, use RowDataBound to process each row accordingly:
protected void RowDB(..)
{
Label l = e.Row.Cells[4].Controls[1] as Label;
if (l == null) return;
string description = l.Text;
l.Text = //Partial text here
}
And the grid will bind each row, the full text is passed to a variable, and you can insert a substring of the text (3 words or such) by assigning the new value to the text property.
What kind of tooltip are you looking for? You could use the tooltip property, or consider using the ACT HoverMenuExtender: http://www.asp.net/AJAX/AjaxControlToolkit/Samples/HoverMenu/HoverMenu.aspx
I am building a pop-out menu, and the client wants it to be able to pop out continously based on a heirarchy.
For example, the first pane is a list of options. When they are hovered over, another pane should pop up next to it with the next level of options, and so on until the last level of options is reached.
I can handle all the javascript and stuff, but I can't think of a way to continously embed repeaters inside repeaters. I know I could do it once by putting a repeater inside another, but then I would only have two layers.
I need to be able to continously embed repeaters for each layer of options, or achieve this with a similar technique using a different control.
Any help is great, thanks!
You won't be able to build this in mark up. You'll have to add the controls dynamically in your code behind by building the Repeater for each level and adding it to the previous Repeater's template. It will require a full postback for each option selected because the nested Repeater could potentially be of different depth depending on which option is chosen.
You might be better off doing this all client-side, though, using AJAX and javascript. When an option is chosen, fire off an AJAX request to see if that option has sub-options. If it does (return them), then dynamically build the new options control using javascript and add it to the page. When a different option is chosen, you'll remove the elements from the DOM holding the previously chosen options sub-options.
If you can get your menu out in form of a list of MenuItem objects, each of which has a (sometimes empty) list of sub items (and I really mean a List<MenuItem> here... we're going to use this collection as a datasource for a sub-repeater, so it needs to implement IEnumerable<T>) as a property MenuItem.SubItems, you could probably make use of a UserControl that loops out one menu level, and calls upon itself for the next.
In the UserControl you'd have something like this:
<li><a href='<%= this.MenuItem.Url %>'><%= this.MenuItem.LinkText %></a></li>
<asp:Repeater ID="UCRepeater" runat="server">
<HeaderTemplate>
<ul>
<ItemTemplate>
<menu:MenuItem ID="MenuItemUC" runat="server" />
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
The UserControl in the ItemTemplate is the same one, so for each item template the same thing will be rendered.
Below is the Code Behind for this user control, and this is where the magic happens:
public partial class MenuItemUserControl : UserControl
{
// A property we'll use as the data source
public MenuItem MenuItem { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
// If the current menu item has sub items, we bind the repeater to them
// And by the way, there is no use doing this on every postback. First
// page load is good enough...
if(!Page.IsPostBack) {
{
if(MenuItem.SubItems.Count > 0)
{
UCRepeater.DataSource = MenuItem.SubItems;
UCRepeater.DataBind();
}
}
}
protected void UCRepeater_OnItemDataBound(object sender,
RepeaterDataBoundEventArgs e)
{
// Every time an Item is bound to the repeater, we take the current
// item which will be contained in e.DataItem, and set it as the
// MenuItem on the UserControl
// We only want to do this for the <ItemTemplate> and
// <AlternatingItemTemplate>
if(e.Item.ItemType == ListItemType.Item ||
e.Item.ItemType == ListItemType.AlternatingItem)
{
var uc = (MenuItemUserControl)e.Item.FindControl("MenuItemUC");
if(uc != null)
{
// This is the magic. Abrakadabra!
uc.MenuItem = (MenuItem)e.DataItem;
}
}
}
}
So in order to get this to work, the only thing missing is really a nice way of getting your data out as a hierarchical list of MenuItems. This I'll leave to your data access layer (and it would be cheap easy using LINQ to SQL or Entity Framework... ;) )
DISCLAIMER: This code is provided as is, and I wrote it off the top of my head. I have not tested it, but I think it will work - and if it doesn't, it could at least give you an idea of how to solve the problem. If you have problems, please post them in comments and I'll try to help out - but there are no promises of success here. Just a willingness to help out! =)