Embedded code in repeater - asp.net

I am using a databound repeater component with some click-sensitive panel inside.
<ItemTemplate>
<asp:Panel ID="PanelContent" runat="server">
<asp:Panel ID="PanelMenuTitle" runat="server"
ondblclick="EditMenu(<%# Eval("ID") %>)">
As you can see I want to pass the ID of the current data item to a javascript function called EditMenu().
However, this code breaks due to "The server tag is not well formed.". I also tried everything I can think of: Using <%= instead of <%#, Bind() instead of Eval(), ' instead of " without success.

Use single quotes around the ondblclick function. That should fix it. See below.
ondblclick='EditMenu(<%# Eval("ID") %>)'
My second suggestion would be to use another control, like a ListView or a DataList, so that you can assign data keys to hold the ID. Then you can assign the ondblclick event in the ItemDataBound event like this.
protected void ListView1_ItemDataBound(object sender, ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem)
{
Panel pnlCtrl = (Panel)e.Item.FindControl("Panel1");
if (pnlCtrl != null)
{
pnlCtrl.Attributes["ondblclick"] = String.Format("EditMenu({0})", ListView1.DataKeys[((ListViewDataItem)e.Item).DisplayIndex]["ID"]);
}
}
}

Alright, I got it working. Not the original approach, but in the end it's what I wanted:
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
Panel pnlCtrl = (Panel)e.Item.FindControl("PanelMenuTitle");
if (pnlCtrl != null)
{
myMenu menu = (e.Item.DataItem as myMenu);
pnlCtrl.Attributes["ondblclick"] = String.Format("EditMenu('{0}')", menu.ID);
}
}

Related

How do I find an AjaxToolkit TabContainer in the code behind?

I have a tabcontainer in a repeater. When the page builds I want to set the active tab. How do I find it in the code behind?
This doesn't work:
AjaxControlToolkit.TabContainer tc = FindControl("projTabContainer");
because I'm trying to cast a control to a tabcontainer.
If the TabContainer is in an ItemTemplate of a Repeater, you have to set the ActiveTab in ItemDataBound. That's also the only place where you can find it in this way since the RepeaterItem is it's NamingContainer.
void R1_ItemDataBound(Object Sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
var tc = (AjaxControlToolkit.TabContainer)e.Item.FindControl("projTabContainer");
tc.ActiveTab = ...
}
}
Try the following.
AjaxControlToolkit.TabContainer tc = e.Item.FindControl("projTabContainer") as AjaxControlToolkit.TabContainer;

Managing images in asp.net repeater (add class to each picture)

This is my code:
Codebehind:
public class data
....
...
public List<dataImages> Images { get; set; }
...
var data= GarageBLL.LoadData(Convert.ToInt32(DataId), Convert.ToInt32(MemberId));
rptImages.DataSource = data.Images.Take(3);
rptImages.DataBind();
aspx:
<asp:Repeater runat="server" ID="rptImages">
<ItemTemplate>
<asp:Image runat="server" CssClass="img1" ImageUrl='<%# String.Format("/images/{0}/{1}.{2}", DataId, Eval("ImageId"), Eval("Extension")) %>' />
</ItemTemplate>
</asp:Repeater>
So, this works at the moment, but i would like to add different classes to each image.
Anyone got any idea how i can do that?
You can use repeater's ItemDataBound event to set the CssClass:
protected void rptImages_ItemDataBound(Object Sender, RepeaterItemEventArgs e) {
// This event is raised for the header, the footer, separators, and items.
// Execute the following logic for Items and Alternating Items.
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) {
dataImages img = (dataImages) e.Item.DataItem;
Image img1 = (Image) e.Item.FindControl("img1");
// add your correct logic here according to the dataImages properties
img.CssClass = "YourCssClass";
// assuming you just want different classes for your three images, use ItemIndex with remainder:
string class = "img" + (e.Item.ItemIndex % 3 + 1).ToString() + "class";
img.CssClass = class;
}
}

How do I change the image on a <asp:buttonField type="Image" /> in code behind?

I have a <asp:GridView > with a <asp:ButtonField ButtonType="Image"> as one of the columns.
Here's the problem: I have to dynamically change the image of this ButtonField during the gridView_RowDataBound(...) event based on the data found in that particular gridview row.
The real question is, is how to access that particular ButtonField inside the gridView_RowDataBound(...) event so I can change its image in C# code?
I can't use
Image imgCtrl = (Image)args.Row.FindControl("ctrlID");
because the <asp:ButtonField> won't allow an ID to be set (get a parser error when I try to run the webPage). And I can't use
args.Row.Cells[0].Controls[0];
because the zeroth index of the .Controls[0] doesn't exist (I get a boundry overflow error).
There's got to be a simple, slick, easy way to do this!
Quick Example :
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
DataRowView drv = (DataRowView)e.Row.DataItem;
if (e.Row.RowType == DataControlRowType.DataRow)
{
TableCell tableCell = e.Row.Cells[3]; // Column 3 in the grid have the Image Button
foreach (var control in tableCell.Controls)
{
if (control.GetType() == typeof(System.Web.UI.WebControls.ImageButton)) ;
{
ImageButton iButton = control as ImageButton;
iButton.ImageUrl = "/Logo.jpg";
}
}
}
}

radio button list in repeater control

I have a repeater control in my page. I need to have a radio button in all the rows(Item template) on checking an radio button the remaining radio buttons must unchecked.
How to do this?
Thanks in advance,
Tor
Unfortunately, it's a known bug ( http://support.microsoft.com/kb/316495 ) that the GroupName property doesn't work as expected when used in a Repeater. The problem is that the Repeater implements the INamingContainer interface which requires all nested controls to have a unique name when rendered out to HTML. This causes the radio buttons to break because in order for them to work properly they must have identical names.
There are 2 work-arounds that I've come across:
1 - The first is a client-side javascript solution. It was provided by Microsoft support. Or an easier to read version here.
The instructions are as follows. Include the following javascript in the HEAD:
function SetUniqueRadioButton(nameregex, current)
{
re = new RegExp(nameregex);
for(i = 0; i < document.forms[0].elements.length; i++)
{
elm = document.forms[0].elements[i]
if (elm.type == 'radio')
{
if (re.test(elm.name))
{
elm.checked = false;
}
}
}
current.checked = true;
}
Now the function needs to be linked to the Radio Buttons in the OnDataItemBound event of the repeater. Replace "RadioButton" with the name of your RadioButton control and "RadioGroup" with the GroupName you have chosen:
protected void Repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType != ListItemType.Item && e.Item.ItemType != ListItemType.AlternatingItem) return;
RadioButton rb = (RadioButton) e.Item.FindControl("RadioButton");
string script = "SetUniqueRadioButton('Repeater1.*RadioGroup',this)";
rb.Attributes.Add("onclick", script);
}
2 - The second solution is a server-side solution using a custom usercontrol that inherits from RadioButton. The tutorial and source code can be downloaded here: http://www.codeproject.com/KB/webforms/How_group_RButtons.aspx
Just add a OnCheckedChanged event to the radio button, loop all the radiobuttons in the repeater to uncheck them. You can use UpatePanel if you do not want postback.
.aspx
<asp:Repeater ID="Repeater1" runat="server" >
<ItemTemplate>
<asp:RadioButton ID="RadioButton1" runat="server" OnCheckedChanged="RadioButton1_OnCheckedChanged" AutoPostBack="true" />
</ItemTemplate>
</asp:Repeater>
.cs
protected void RadioButton1_OnCheckedChanged(object sender, EventArgs e)
{
foreach (RepeaterItem item in Repeater1.Items)
{
RadioButton rbtn = (RadioButton)item.FindControl("RadioButton1");
rbtn.Checked = false;
}
((RadioButton)sender).Checked = true;
}
I know this is old, but the reason for the buttons not working is because the name attribute is being overwritten. If using jQuery, you could assign the radio button to a class then set the name attribute to override the new name.
$('.MyRadioClass').attr("name","MyFixedName");
Another simpler alternative, where no fancy formatting is required, is to use a RadioButtonList:
<asp:RadioButtonList ... />

How to programmatically create and use a list of checkboxes from ASP.NET?

I have a page with a table of stuff and I need to allow the user to select rows to process. I've figured out how to add a column of check boxes to the table but I can't seem to figure out how to test if they are checked when the form is submitted. If they were static elements, I'd be able to just check do this.theCheckBox but they are programaticly generated.
Also I'm not very happy with how I'm attaching my data to them (by stuffing it in there ID property).
I'm not sure if it's relevant but I'm looking at a bit of a catch-22 as I need to known which of the checkboxes that were created last time around were checked before I can re-run the code that created them.
Edit:
I've found an almost solution. By setting the AutoPostBack property and the CheckedChanged event:
checkbox.AutoPostBack = false;
checkbox.CheckedChanged += new EventHandler(checkbox_CheckedChanged);
I can get code to be called on a post back for any check box that has changed. However this has two problems:
The call back is processed after (or during, I'm not sure) Page_Load where I need to use this information
The call back is not called for check boxes that were checked when the page loaded and still are.
Edit 2:
What I ended up doing was tagging all my ID's with a know prefix and stuffing this at the top of Form_Load:
foreach (string v in this.Request.Form.AllKeys)
{
if (v.StartsWith(Prefix))
{
var data = v.Substring(Prefix.Length);
}
}
everything else seems to run to late.
I'm going to assume you're using a DataList but this should work with and Control that can be templated. I'm also going to assume you're using DataBinding.
Code Front:
<asp:DataList ID="List" OnItemDataBound="List_ItemDataBound" runat="server">
<ItemTemplate>
<asp:CheckBox ID="DeleteMe" runat="server"/>
<a href="<%# DataBinder.Eval(Container, "DataItem.Url")%>" target="_blank">
<%# DataBinder.Eval(Container, "DataItem.Title")%></a>
</ItemTemplate>
</asp:DataList>
<asp:Button ID="DeleteListItem" runat="server" OnClick="DeleteListItem_Click" ></asp:Button>
Code Behind:
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadList();
}
protected void DeleteListItem_Click(object sender, EventArgs e)
{
foreach (DataListItem li in List.Items)
{
CheckBox delMe = (CheckBox)li.FindControl("DeleteMe");
if (delMe != null && delMe.Checked)
//Do Something
}
}
LoadList();
}
protected void LoadList()
{
DataTable dt = //Something...
List.DataSource = dt;
List.DataBind();
}
protected void List_ItemDataBound(object sender, DataListItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.AlternatingItem || e.Item.ItemType == ListItemType.Item)
{
string id = DataBinder.Eval(e.Item.DataItem, "ID").ToString();
CheckBox delMe = (CheckBox)e.Item.FindControl("DeleteMe");
if (delMe != null)
delMe.Attributes.Add("value", id);
}
}
}
First, make sure that each Checkbox has an ID and that it's got the 'runat="server"' in the tag.
then use the FindControl() function to find it.
For example, if you're looping through all rows in a GridView..
foreach(GridViewRow r in Gridview1.Rows)
{
object cb = r.FindControl("MyCheckBoxId");
if(r != null)
{
CheckBox chk = (CheckBox)cb;
bool IsChecked = chk.Checked;
}
}
Postback data is restored between the InitComplete event and the PreLoad event. If your checkboxes are not created until later then the checkboxes will play "catch up" with their events and the data will be loaded into the control shortly after it is created.
If this is to late for you then you will have to do something like what you are already doing. That is you will have to access the post data before it is given to the control.
If you can save the UniqueId of each CheckBox that you create then can directly access the post data without having to given them a special prefix. You could do this by creating a list of strings which you save the ids in as you generate them and then saving them in the view state. Of course that requires the view state to be enabled and takes up more space in the viewstate.
foreach (string uniqueId in UniqueIds)
{
bool data = Convert.ToBoolean(Request.Form[uniqueId]);
//...
}
Your post is a little vague. It would help to see how you're adding controls to the table. Is it an ASP:Table or a regular HTML table (presumably with a runat="server" attribute since you've successfully added items to it)?
If you intend to let the user make a bunch of selections, then hit a "Submit" button, whereupon you'll process each row based on which row is checked, then you should not be handling the CheckChanged event. Otherwise, as you've noticed, you'll be causing a postback each time and it won't process any of the other checkboxes. So when you create the CheckBox do not set the eventhandler so it doesn't cause a postback.
In your submit button's eventhandler you would loop through each table row, cell, then determine whether the cell's children control contained a checkbox.
I would suggest not using a table. From what you're describing perhaps a GridView or DataList is a better option.
EDIT: here's a simple example to demonstrate. You should be able to get this working in a new project to test out.
Markup
<form id="form1" runat="server">
<div>
<table id="tbl" runat="server"></table>
<asp:Button ID="btnSubmit" runat="server" Text="Submit"
onclick="btnSubmit_Click" />
</div>
</form>
Code-behind
protected void Page_Load(object sender, EventArgs e)
{
for (int i = 0; i < 10; i++)
{
var row = new HtmlTableRow();
var cell = new HtmlTableCell();
cell.InnerText = "Row: " + i.ToString();
row.Cells.Add(cell);
cell = new HtmlTableCell();
CheckBox chk = new CheckBox() { ID = "chk" + i.ToString() };
cell.Controls.Add(chk);
row.Cells.Add(cell);
tbl.Rows.Add(row);
}
}
protected void btnSubmit_Click(object sender, EventArgs e)
{
foreach (HtmlTableRow row in tbl.Rows)
{
foreach (HtmlTableCell cell in row.Cells)
{
foreach (Control c in cell.Controls)
{
if (c is CheckBox)
{
// do your processing here
CheckBox chk = c as CheckBox;
if (chk.Checked)
{
Response.Write(chk.ID + " was checked <br />");
}
}
}
}
}
}
What about using the CheckBoxList control? I have no Visual Studio open now, but as far as I remember it is a DataBound control, providing DataSource and DataBind() where you can provide a list at runtime. When the page does a postback you can traverse the list by calling something like myCheckBoxList.Items and check whether the current item is selected by calling ListItem.Selected method. This should work.
Add them in an override of the CreateChildControls method of the Page. Be sure to give them an ID! This way they get added to the control tree at the correct time.
IMHO The best way would be to use DataBound Templated Control though, i.e. something like a ListView (in .NET 3.5). then in pageload after postback traverse all items in the databound control and use item.FindControl to get at the actual checkbox.
What I ended up doing was tagging all my ID's with a know prefix and stuffing this at the top of Form_Load:
foreach (string v in this.Request.Form.AllKeys)
{
if (v.StartsWith(Prefix))
{
var data = v.Substring(Prefix.Length);
}
}
everything else seems to run to late.

Resources