Dynamically adding a command button to a GridView - c#

I'm having an issue with trying to add a button to my grid. My GridView is first loaded with data in the PageLoad event.
I'm then taking the data in the first cell of each row, and creating a button that will link to a URL. To get the URL, I have to run a query with the data in the first cell as a parameter. I was doing this in the RowDataBound event at first, but hitting that query for every row was making it really slow.
So I decided to add a button that would retrieve the URL only when you clicked the button.
Here's my GridView:
<asp:GridView ID="gvResults" runat="server"
OnRowDataBound="gvResults_RowDataBound"
OnRowCommand="gvResults_RowCommand">
</asp:GridView>
And my code:
protected void gvResults_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.DataItem != null)
{
LinkButton lb = new LinkButton();
lb.CommandArgument = e.Row.Cells[0].Text;
lb.CommandName = "NumClick";
lb.Text = e.Row.Cells[0].Text;
e.Row.Cells[0].Controls.Add((Control)lb);
}
}
protected void gvResults_RowCommand(object sender, CommandEventArgs e)
{
switch (e.CommandName.ToLower())
{
case "numclick":
string url = GetUrl(e.CommandArgument.ToString());
Response.Redirect(url);
break;
default:
break;
}
}
The grid generates fine, the button gets added to the grid for each row. But when I click on it, the RowCommand event doesn't fire, and the page just refreshes.
Does anyone know what the issue is?

Why use a dynamic button at all? You can easily put the linkbutton directly into the markup of the gridview (as long as you don't mind using a template field) and there will be no need to mess around with the RowDataBound event.
Your markup would look something like the following:
<Columns>
<asp:TemplateField HeaderText="SomeHeaderText">
<ItemTemplate>
<asp:LinkButton ID="lnkBtn" runat="server" CommandName="NumClick" CommandArgument= '<%# (string)Eval("dbValue") %>' Text='<%# (string)Eval("dbValue") %>'></asp:LinkButton>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField></asp:BoundField>
<asp:BoundField></asp:BoundField>
<asp:BoundField></asp:BoundField>
</Columns>
Add breakpoints to the RowCommand event and make sure that you can hit the breakpoints.
The problem may lie elsewhere.
Also, make sure that you're not databinding on postback.

You have a big trouble with your code. It's pretty hard for me to explain what's your big mistake, but I can easily tell you how to fix.
The problem is that you generate a new button inside the RowDataBound event, definitely the wrongest choice. The button gets rendered because it exists after that event when page renders, but doesn't exist before data binding. If you bind data everytime you load the page (even during postback) the button still gets rendered because you generate a new button.
But since the button doesn't exist before data binding, it cannot raise events. You must declare the button from markup into a template of GridView, then access it not by using new LinkButton() but by using e.Row.Cells[0].FindControl("buttonId") and set its text. Then, you have to set its markup in order to fire its own Command event (not RowCommand) and handle it as you used (don't forget to set CommandArgument during data binding)
[Edit] I also made a mistake: controls inside data bound controls also don't exist before data binding. But they are initialized not with new Control() (by the private methods of data bound control) but with Page.LoadControl(typeof(Control)). That's the first thing you must fix when you load controls dynamically!!

Because the control is added dynamically on databind and you have to databind the gridview for each postback, the control being "clicked" is different each time. The event doesn't fire because at the time it needs to fire it doesn't exist as it did in the last iteration of the page.
I notice you don't have any logic determine if the button should be there, and it always goes into cell[0].
You should place this button into a TemplateItem so that it exists properly. If you have a need to do it in code-behind, you are probably better served doing it in the RowCreated event.

Related

dynamicly created LinkButton onClick doesn't trigger onclick method

I'm creating X linkbuttons in code behind using the following code:
HTML:
<div runat="server" id="div_tables">
</div>
Backend:
LinkButton lnkB = new LinkButton();
lnkB.ID ="LB" + row.ItemArray[1].ToString() + row.ItemArray[2].ToString();
lnkB.Text = "Link to episode";
lnkB.Attributes.Add("runat", "server");
lnkB.Click += new EventHandler(lb_Clicked);
div_tables.Controls.Add(lnkB);
OnClick method:
protected void lb_Clicked(object sender, EventArgs e)
{
LinkButton b = sender as LinkButton;
b.Text = "ASD";
}
On linkbutton click the entire page reloads instead of only the lb_Clicked method. I'm sure this is a basic linkbutton question, but I really hope someone can help me.
Thank you in advance
#
EDIT, Found this other post which gives a solution to the problem:
Can't call Click Event on dynamic button
As Mark correctly says, the data is lost at the pageload. This post suggest to use viewstate to only recreate the desired button.
The entire page will always reload. Keep in mind, after the page is served there is no page anymore. When you click a button it then transmits everything back to the server. The server then spins up the page, goes through it's events, and then processes the appropriate postback such as the linkbutton's click event.
If you want to ensure certain other code isn't run, such as code in your OnLoad, you must enclose it in an if statement such as:
if(!Page.IsPostBack)
{
// only do things the first time the page is laoded.
}
Also, when creating dynamic controls you have to re-create them each time the page posts back, and early enough in the page's lifecycle that they actually exist in time to receive a postback event. OnLoad or OnPreLoad are the latest events you should do this in (don't use Page_Load as it occurs after OnLoad and is generally too late).
For easier processing, such as if you are having the linkbutton do something with a record, you may want to look at the Command Event. This behaves as the click does, but gives you access to the CommandName and CommendArgument which you can set on the linkbutton and use to pass information such as the ID of a selected record.

LinkButton Text property not changing after dynamic change

I have tried for so long to understand why my LinkButton text toggles to say "on" when its clicked, however when I click the button again the text still says "on" even though in my code behind file I set LinkButton.text = "off". I have run the debugger and when I set the text to "off" its like
the setter method doesnt actually update that specific LinkButton object, as the next click its like it never got updated.
I am using this to test why my panel inside a gridview template field wont toggle its visibility on and off, as the same thing happens with that where I click a button to make the panel visible which it does, then when I press the button again the panel should disappear but it stays on the screen.
My goal is to have each row in a gridrow have a toggle link button that displays a panel that drops down below each row.
So the essence to my problem is, why can I toggle a control on, but cannot toggle it off?.
My markup is essentially the same as below. Note all the actual proper server attributes are left
out for sake of clarity.
I've tried setting viewstate to false but that doesn't help.
The funny part is, I place the LinkButton and Panel outside of the
gridview and it does exactly what I expect it to do which is toggle
on and off or set the panel to visible or hide.
I ready some other posts but I don't fully understand the issue.
I have read that some people say its about nesting controls inside other controls but I am just not certain how this is causing an issue.
UpdatePanel.Visible = true has no effect
http://forums.asp.net/t/1773859.aspx?C+Visible+true+not+working
My other thought is that the button is rendered at a stage that is too late for me to make a change that will be placed on the final html?
Can anyone give me any pointers?
<asp:GridView>
<Columns>
<asp:TemplateField>
<ItemTemplate>
<div id="wrapper">
<asp:LinkButton ID="LinkButton1" runat="server"
CommandArgument='<%# ((GridViewRow)Container).RowIndex %>'
Text="off" oncommand="changeName"></asp:LinkButton>
</div>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
code behind
protected void changeName(object sender, CommandEventArgs e)
{
GridViewRow row = MyGridView.Rows[Convert.ToInt32(e.CommandArgument)];
LinkButton button = (LinkButton)row.FindControl("LinkButton1");
if (button.Text.Equals("off"))
{
button.Text = "on";
}
else
{
button.Text = "off";
}
}
I solved the issue by removing my 'EnableViewState = "False"` on the gridview.
After reading this article
http://forums.asp.net/t/1590532.aspx?Visible+invisible+panel+inside+Gridview
It gave me some ideas on the whole postback process.
I am not sure if this is correct but it kind of makes some sense to me?
If my gridview is disabling viewstate and its the parent to all nested controls such as 'Panel' and 'LinkButton' then the state of those nested controls will be lost across postback. So any
dynamically made changes I modified in the code behind file would be lost if the changes were not
being retained in the viewstate of the gridview.
There is probably a better way of achieving my design but for the moment leaving viewstate
on the parent control seemed to fix it.

GridView not updating from SelectedIndexChange event of a DropDownList within an UpdatePanel

My problem:
An UpdatePanel holds a DropDownList and a GridView. The GridView populate baseds on a value that is stored in a Label and a value that is selected from a DropDownList. It does NOT populate on PageLoad; it only populates when a query string is present or when a button is pressed, so there is no code in the !IsPostBack. The GridView is populated with the initial SelectedValue of the DropDownList, and I want the GridView to repopulate when the SelectedValue of the DropDownList changes.
The thing is... IT DOES CHANGE! But only once. The GridView initially populates with data using the value of the default SelectedValue of the DropDownList. When the SelectedValue is changed, the data in the GridView changes. But only once. After changing the DropDownList value the second time or more, nothing happens.
<asp:UpdatePanel runat="server" ID="theUpdatePanel" UpdateMode="Conditional" ChildrenAsTriggers="True">
<ContentTemplate>
<asp:DropDownList runat="server" ID="theDropDownList" OnSelectedIndexChanged="theDropDownList_OnSelectedIndexChanged" EnableViewState="true" AutoPostBack="true" />
<asp:GridView ID="theGridView" runat="server" AutoGenerateColumns="False">
<Columns>
...
</Columns>
</asp:GridView>
</ContentTemplate>
</asp:UpdatePanel>
theUpdatePanel's UpdateMode is Conditional, so it updates when its children PostBack. ChildrenAsTriggers is true, so children cause it to update.
theDropDownList's AutoPostBack is true, so it will postback on change. EnableViewState is true (false makes it not even load). SelectedIndexChange links to the right function, and I know that it's getting called by putting a break inside it during debug mode.
Here is the SelectedIndexChanged method:
public void theDropDownList_OnSelectedIndexChanged(object sender, EventArgs e)
{
//get value that is stored in theLabel - I know this value is correct every time
int theValueFromTheLabel = Int32.Parse(theLabel.Text);
//populate theGridView with data from the DB
theGridView_Populate(theValueFromTheLabel);
//update theUpdatePanel (it works for the first change whether this line is here or not)
theUpdatePanel.Update();
}
I've tried both with and without the updatepanel.Update(); method, and it's the same result: the first change of theDropDownList's value works, and every following change does nothing.
The function to actually populate the GridView is pretty simple:
protected void theGridView_Populate(int theValueFromTheLabel)
{
//get value from theDropDownList - this value does change when theDropDownList's value changes
int theValueFromTheDropDownList = Int32.Parse(theDropDownList.SelectedValue);
//get data from DB - the data here does change every time theDropDownList's value changed
ComplexClassController controller = new ComplexClassController();
List<ComplexClass> data = controller.GetData(theValueFromTheLabel, theValueFromTheDropDownList);
//load theGridView - this changes the data, but doesn't refresh theGridView to be able to see it
theGridView.DataSource = data;
theGridView.DataBind();
}
Any ideas? I must be misunderstanding the events behind UpdatePanel.
what is valueLabel? It doesn't look like it ever gets changed?
Why not just do ((DropDownList)sender).selectedValue to get your value to redatabind the gridview with?
Kev
It does not seems to be any problem of getting value from dropdown list the reason might be in your giving data source of your combo
place load of combo in !postback of page load event some thing like this
protected void Page_Load(object sender, EventArgs e)
{
if(!ispostback){
Loadcomobo();
}
}
if its not the case so can you place your page load event code here
thanks...
I'm still not sure why it updated once and then never again, but the problem has been fixed by placing the DropDownList and the GridView in their own UpdatePanels instead of the same one.
Try adding a trigger to the update panel
<triggers>
<asyncpostback ControlID="theDropDownList" />
</triggers>
Or something like that before the
</asp:UpdatePanel>

DropDownList events don't fire

I’m having trouble with my DropDownList. The events don’t fire! I've tested it on a separate project, with a DropDownList and a literal. Every time the selected value would change, I would add a little star “*” to the literal. No problems what so ever. But every time I try it on my webpage in the project, it fails.
Here is an image.
protected void ddlConsole_SelectedIndexChanged(object sender, EventArgs e)
{
ltlTesting.Text += "*";
}
UPDATE:
I've tried some things but still with no succes. I hope someone can tell me what i'm doing wrong. I'm wiring the events in the code behind now, but i've added a linkbutton next to the dropdownlist to see if it works.
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
ddlConsole.SelectedIndexChanged += new EventHandler(ddlConsole_SelectedIndexChanged);
lnkGet.Click += new EventHandler(ddlConsole_SelectedIndexChanged);
}
Here is an image to see what's going on. The stripe in the literal at the beginning is added in pageload with the same code the star is added. Just to be sure it doesn't load twice. The "GET" linkbutton works fine. The dropdownlist doesn't...
Have you Set
AutoPostBack="true"
in control properties??
EDIT:
Remove
OnSelectedIndexChanged="ddlConsole_SelectedIndexChanged"
from the markup in the ASPX page and try again only with AutoPostback true and the
event defined in codebehind. The aspx page should look like this:
<asp:DropDownList runat="server" ID="ddlConsole" AutoPostBack="True"></asp:DropDownList>
Is the AutoPostBack of the dropdownlist true ?
Check AutopostBack property of Dropdownlist set it to true :
If picture is right and the AutoPostBack="True", is there any code that sets the value of ltlTesting when page loads?
Add the AutoPostback="True" and OnSelectedIndexChanged="ddlConsole_SelectedIndexChanged" to the ddlConsole attributes. You can delete the OnInit method, since you bound the SeletedIndexChanged event at design time.

BulletedList onClick not firing

Ugh, this is driving me mad
Im trying to build up a dynamic menu from a bulletedList, most menu items are plain links however the log out button needs to perform some cleanup code.
I cant for the life of me get the BullettedLists onclick event to fire.
The BulletedList is inside a user control (if that makes a difference)
Any ideas?
Or - any ideas for an alternative, better solution?
Code below
BulletedList
<asp:BulletedList OnClick="menu_Click" runat="server" CssClass="MainMenu" ID="loggedInMenu" DisplayMode="HyperLink" />
Adding an element
loggedInMenu.Items.Add(new ListItem("Logout", ""));
Click handler
protected void menu_Click(object sender, BulletedListEventArgs e)
{
user.logout();
Response.Redirect("Default.aspx");
}
You're using the wrong DisplayMode for your BulletedList control. You should use a DisplayMode of LinkButton. When you use DisplayMode.HyperLink:
Users can click links to move to
another page. You must provide a
target URL as the Value property of
individual items.
This is from the MSDN docs for this control. (It's about 3/4 of the way down the page.)
When you use a BulletedList control in HyperLink mode, the value of your ListItem is the URL that you're navigating to. So your static page HTML controls would use ListItem.Value as the href attribute of the <a> tag.
Here's what the HTML markup looks like when you use a DisplayMode of HyperLink (it's a plain old HTML anchor tag w/ a href):
<li>One</li>
But since you want to postback, you should set the DisplayMode of your BulletedList control to LinkButton. When you do that, you'll enable a postback back to your page and your event handler will trap the event. You can then process the click appropriately then. The event argument that's passed in (of type BulletedListEventArgs) will have an Index property, and that will tell you what item in your list was clicked.
Here's the updated .aspx code that I used:
<asp:BulletedList ID="bullet" runat="server" DisplayMode="LinkButton"
onclick="bullet_Click">
<asp:ListItem Text="One" Value="1">One</asp:ListItem>
</asp:BulletedList>
Everything else is the same except the DisplayMode, which is set to LinkButton. When I use that, then my bullet_Click event handler is fired when I click a list item.
I hope this helps!!

Categories

Resources