Asp.Net Repeater ItemCommand dataitem is always null - asp.net

In repeater rpt_ItemCommand Event the e.Item.DataItem is always null.
Here is the code behind:
protected void rpt_ItemCommand(Object sender, RepeaterCommandEventArgs e)
{
DataRowView drv = (DataRowView)e.Item.DataItem // here the DataItem is Null.
}
Suggest me any solutions.

The DataItem Property is always null except ItemDataBound... its by design of Microsoft.

Think of using CommandArgument.
<asp:LinkButton ToolTip="Delete" CommandArgument='<%#Eval("Id") %>' ....
and use it in ItemCommand Event as
int id = Convert.ToInt32(e.CommandArgument);

Related

Event Handler OnItemCommand with TextBox control

I have Repeater that contains a TextBox and a LinkButton. When the LinkButton is clicked, I need to grab the TextBox.Text and do stuff …
Using the EVENT Repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e) I am able to get the value of the TextBox using TextBox tx = e.Item.FindControl("txCode") as TextBox
However
Using the EVENT Repeater1_ItemCommand(object sender, RepeaterCommandEventArgs e) I am NOT getting anything back. The TextBox is empty.
How can I get the text/content from the TextBox using 'OnItemCommand'?
<asp:Repeater ID="Repeater1" runat="server" onitemdatabound="Repeater1_ItemDataBound" OnItemCommand="Repeater1_ItemCommand">
<ItemTemplate>
<li>
<asp:TextBox ID="txCode" runat="server"></asp:TextBox>
<asp:LinkButton CommandName="verifyCode" ID="lbCode" runat="server">Submit<asp:LinkButton>
</li>
</ItemTemplate>
</asp:Repeater>
I am able to get the TextBox Value below
protected void Repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
TextBox tx = e.Item.FindControl("txCode") as TextBox;
string myText = tx.Text; '<--- working
}
I am NOT able to get the TextBox Value below
protected void Repeater1_ItemCommand(object sender, RepeaterCommandEventArgs e)
{
if (e.CommandName == "verifyCode")
{
TextBox tx = e.Item.FindControl("txCode") as TextBox;
string myText = tx.Text; '<--- NOT working
}
Do not bind your Repeater to it's DataSource on every postback. Otherwise ViewState cannot be reloaded correctly what causes issues like this.
So always check the IsPostBack property in Page_Load when ViewState is enabled(EnableViewState=true):
if(!IsPostBack)BindRepeaterToDataSource();

Handling the checkedchanged event of inner repeater Checkbox control in asp.net nested repeaters

I have nested repeaters on my aspx page.In the outer repeater I am displaying a list of products and in the inner repeater I am displaying a list of additional options associated with each product.The inner repeater contains a checkbox,textbox,label and other stuff.I would like to find the controls inside the outer repeater when a user selects a checkbox in the inner repeater.In order to handle this I am using the following code.
<asp:Repeater ID="OuterRepeater" runat="server"
onitemdatabound="OuterRepeater_ItemDataBound" >
<ItemTemplate>
<asp:Label ID="CodeLabel" runat="server" Text='<%# Eval("Code") %>'></asp:Label>
<asp:Repeater ID="InnerRepeater" runat="server" OnItemCreated="InnerRepeater_ItemCreated">
<ItemTemplate>
<asp:CheckBox ID="CheckBox1" runat="server" AutoPostBack="true"/>
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
......
.......
</ItemTemplate>
</asp:Repeater>
......
......
</ItemTemplate>
</asp:Repeater>
protected void InnerRepeater_ItemCreated(object sender, RepeaterItemEventArgs e)
{
RepeaterItem ri = (RepeaterItem)e.Item;
if (ri.ItemType == ListItemType.Item || ri.ItemType == ListItemType.AlternatingItem
)
{
CheckBox cb = ri.FindControl("CheckBox1") as CheckBox;
cb.CheckedChanged += new EventHandler(CheckBox1_CheckedChanged);
}
}
private void CheckBox1_CheckedChanged(object sender, EventArgs e)
{
CheckBox cb = (CheckBox)sender;
if (cb.Checked)
{
//do something
}
else
{
//do something
}
}
But the checkedChanged event of the checkbox is not firing for some reason.Also I am not sure how to access the textbox of the outer repeater in the checked changed event of the innter repeater checkbox control.
Could someone please help me with this?
Thanks
It does not fire the CheckedChanged event, since you have declared the event handler as private, You have to make it Protected or Public
Protected void CheckBox1_CheckedChanged(object sender, EventArgs e)
You can access the Textbox control like..
private void CheckBox1_CheckedChanged(object sender, EventArgs e)
{
CheckBox checkBox = (CheckBox)sender;
Textbox textbox1 = (TextBox)checkBox.Parent.FindControl("TextBox1");
String textboxText = textbox1.Text;
}
It doesn't look like you defined an event handler in your markup.
<asp:CheckBox ID="CheckBox1" runat="server" AutoPostBack="true" OnCheckedChanged="CheckBox1_CheckedChanged" />
Muhammad Akhtar's answer helped me a lot today!
I just needed to set a specific ID to my dynamic generated checkboxes inside my reapeater to recover the origin of the event, and do the rest of the processing, and it worked perfectly.
chkAtivo.ID = DataBinder.Eval(e.Item.DataItem, "id").ToString();
Reovered just as the sample.
Cant vote up yet, but thank you.

Repeater in Repeater

I have a repeater inside a repeater. Where the parent repeater is bound to a Datatble which has a column with a Datatable in it.
I would like to bind the child repeater to the datatable column in the parent repeater's datarow
Is this possible? i was thinking i could do this directly in the aspx file like:
DataSource="<%# DataBinder.Eval(Container.DataItem, "Products")%>" but it doesn't seem to work.
In the parent repeater, attach a method to the OnItemDataBound event and in the method, find the nested repeater and data bind it.
Example (.aspx):
<asp:Repeater ID="ParentRepeater" runat="server" OnItemDataBound="ItemBound">
<ItemTemplate>
<!-- Repeated data -->
<asp:Repeater ID="ChildRepeater" runat="server">
<ItemTemplate>
<!-- Nested repeated data -->
</ItemTemplate>
</asp:Repeater>
</ItemTemplate>
</asp:Repeater>
Example (.cs):
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
ParentRepeater.DataSource = ...;
ParentRepeater.DataBind();
}
}
protected void ItemBound(object sender, RepeaterItemEventArgs args)
{
if (args.Item.ItemType == ListItemType.Item || args.Item.ItemType == ListItemType.AlternatingItem)
{
Repeater childRepeater = (Repeater)args.Item.FindControl("ChildRepeater");
childRepeater.DataSource = ...;
childRepeater.DataBind();
}
}
I would add a DataBinding event to the child repeater itself:
<asp:Repeater ID="parentRepeater" runat="server">
<asp:Repeater ID="childRepeater" runat="server"
OnDataBinding="childRepeater_DataBinding" />
</asp:Repeater>
Then just implement it:
protected void childRepeater_DataBinding(object sender, System.EventArgs e)
{
Repeater rep = (Repeater)(sender);
int someIdFromParentDataSource = (int)(Eval("ParentID"));
// Assuming you have a function call `GetSomeData` that will return
// the data you want to bind to your child repeater.
rep.DataSource = GetSomeData(int);
rep.DataBind();
}
I prefer to do it at the control level instead of the ItemDataBound level so that if you ever have to remove controls or items within your templates you don't have to worry about looking for code in the parent controls that use it. It get's all localize witht he control itself. Plus you never have to do a FindControl.
If you want to replace a control in the future you can just delete it and your code will still work since it is all self contained. Using the ItemDataBound would cause your code to still compile but crash or act unexpectedly at runtime because of it's reliance on child controls.
Here is how it's done:
DataSource='<%# ((System.Data.DataRowView)Container.DataItem)[3] %>'
So if you know the column in the parent table that holds the child table/datasource for the nested repeater you can put this directly in the aspx file.
Repeater1 OnItemDataBound event, then FindControl Repeater2. The code-behind will not find the nested Repeater2! You have to use FindControl("Repeater2").
protected void Repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.DataItem != null)
{
MemberView dataRow = (MemberView)e.Item.DataItem;
var cat = MemberPresenter.getMemberID(dataRow.memID);
Repeater rp2 = (Repeater)e.Item.FindControl("Repeater2");
rp2.DataSource = cat;
rp2.DataBind();
}
}
If I need to do that, I usually do it using the ItemDataBound event of the parent repeater to bind the child repeater. If e is your EventArgs parameter, you'll have access to the child repeater via e.Item.FindControl(), and access to the data via e.Item.DataItem.
Here is an example of how to do this:
Article for nested repeater control
protected void MainRepeater_ItemDataBound(object sender, RepeaterItemEventArgs args)
{
if (args.Item.ItemType == ListItemType.Item || args.Item.ItemType == ListItemType.AlternatingItem)
{
Repeater childRepeater = (Repeater)args.Item.FindControl("ChildRepeater");
DataTable innerTable= ((DataRowView)args.Item.DataItem)["InnerTableColumnName"] as DataTable;
childRepeater.DataSource = tasksDetails;
childRepeater.DataBind();
}
}

Why won't my LinkButton inside a GridView raise its OnClick event?

I have a LinkButton inside a GridView (via an TemplateField). No matter what I try, the LinkButton will not invoke its event handler. I have tried both:
A traditional event handler ("OnClick")
A OnRowCommand event handler at the GridView level.
In both cases, I've debugged and it doesn't even catch the event handler.
If I move the LinkButton out on the page (so it's not in the GridView), it works fine, so I know the syntax is right.
Here is the "traditional" method:
<asp:TemplateField>
<ItemTemplate>
<asp:LinkButton Text="Cancel" ID="DeleteButton" CausesValidation="false" OnClick="CancelThis" runat="server" />
</ItemTemplate>
<asp:TemplateField>
What's interesting is if I remove the "CancelThis" method from the code behind, it throws an error. So I know it's aware of its event handler, because it looks for it when it compiles.
Here is the RowCommand method:
<asp:TemplateField>
<ItemTemplate>
<asp:LinkButton Text="Cancel" ID="DeleteButton" CausesValidation="false" CommandName="CancelThis" runat="server" />
</ItemTemplate>
<asp:TemplateField>
In this case, the GridView has:
OnRowCommand="GridView_RowCommand"
It postsback, but never hints at raising the event.
Any idea what I'm missing here?
How are you binding your GridView? Are you using a datasource control? If you are binding manually during Page_Load, it's possible that since the grid is binding every round trip, the event handler isn't catching properly. If this is the case, you may want to try something like:
protected void Page_Load(object sender, EventArgs e)
{
if(!Page.IsPostBack)
{
//do binding
}
}
Can you post sample binding code to go with your markup?
If you really want to force the issue, you could hook into the RowDataBound event on the Grid, find the button manually and add the handler in the code behind. Something like:
markup snippet:
<asp:GridView ID="gvTest" runat="server" OnRowDataBound="gvTest_RowDataBound" />
code behind:
protected void gvTest_RowDataBound(object sender, GridViewRowEventArgs e)
{
if(e.Row.RowType == DataControlRowType.DataRow)
{
//find button in this row
LinkButton button = e.Row.FindControl("DeleteButton") as button;
if(button != null)
{
button.Click += new EventHandler("DeleteButton_Click");
}
}
}
protected void DeleteButton_Click(object sender, EventArgs e)
{
LinkButton button = (LinkButton)sender;
// do as needed based on button.
}
I'm not sure what the purpose of the button is, but assuming it is a row delete button, you may not want to take this approach as in the event handler, you don't have direct access to the row in question, like you would using the RowCommand event.
Is there a reason you're using the Template field? Vs say a ButtonField? If you use a ButtonField, then you can hook into the RowCommand event.
markup snippet:
<asp:GridView ID="gvTest" runat="server" OnRowCommand="gvTest_RowCommand">
<columns>
<asp:buttonfield buttontype="Link" commandname="Delete" text="Delete"/>
....
</columns>
</asp:GridView>
code behind:
protected void gvTest_RowCommand(object sender, GridViewCommandEventArgs e)
{
if(e.CommandName == "Delete")
{
//take action as needed on this row, for example
int rowIndex = Convert.ToInt32(e.CommandArgument);
GridViewRow currentRow = (sender as GridView).Rows[rowIndex];
//do something against the row...
}
}
You might want to consult MSDN docs on some of these topics:
RowCommandEvent
ButtonField class
EDIT:
To answer your question on the ButtonField - yes I don't see why you couldn't still deal with a buttonfield. Here's a snippet to find the buttonfield during row data bound and hide it (untested but I think would work...)
protected void gvTest_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
//let's assume your buttonfield is in column 1
// (you'd know this based on your markup...)
DataControlFieldCell cell = e.Row.Cells[1] as DataControlFieldCell;
if(cell != null)
{
ButtonField field = cell.ContainingField as ButtonField;
//based on your criteria, show or hide the button
field.Visible = false;
//or
field.Visible = true;
}
}
}
Is viewstate turned on on your GridView? This has caught me out numerous times.
<button onclick="window.open('<%#Eval("ReportLinks")%>', '_blank');" title='<%#Eval("ReportLinks")%>'> Link</button>

Implementing a functional link in a Repeater control

I am implementing a Repeater in my web application to display data. I want to add functional action links in a column similar to the built-in functionality in a GridView. Can anybody give me the steps required? I assume I will add a LinkButton control to each row, somehow set the OnClick event handler to point to the same method, and somehow pass in the unique identifier on the row as a parameter.
Thanks!
I'm guessing this is what you want.
<asp:Repeater ID="rpt" runat="server">
<ItemTemplate>
<asp:LinkButton ID="lbtn" runat="server" OnCommand="lbtn_Command"
CommandArgument='<%# DataBinder.Eval(Container.DataItem, "KeyIDColumn") %>' ></asp:LinkButton>
</ItemTemplate>
</asp:Repeater>
Then in your code behind
protected void lbtn_Command(object sender, CommandEventArgs e)
{
int id = Convert.ToInt32(e.CommandArgument);
}
Use LinkButtons. That way, you can handle the OnClick event in the code behind.
First you would set the onclick of the linkbutton in markup. You'll then want to implement the ItemDataBound event for the repeater.
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
SomeObject obj = e.Item.DataItem as SomeObject; // w/e type of item you are bound to
var linkButton = e.Item.FindControl("linkButtonId") as LinkButton;
if(linkButton != null)
{
//either set a custom attribute or maybe append it on to the linkButton's ID
linkButton.Attributes["someUniqueId"] = obj.SomeID;
}
}
Then in the click event
void lb_Click(object sender, EventArgs e)
{
LinkButton lb = sender as LinkButton;
if (lb != null)
{
// obviously do some checking to ensure the attribute isn't null
// and make it the correct datatype.
DoSomething(lb.Attributes["someUniqueId"]);
}
}

Resources