ASP.Net ImageButton not firing onclick - c#

I'm testing out two ImageButton controls one is set programmatically inside a GridView_RowDataBound function and the other is hard-coded on the client side as follows:
Static ImageButton (Works and fires ImageButton_Click):
<asp:ImageButton OnClick="ImageButton_Click" runat="server" ID="Foo" ImageUrl="images/edit-icon.png" />
Dynamic ImageButton (Does not work, won't fire ImageButton_Click):
protected void gv_RowDataBound(object sender, GridViewRowEventArgs e)
{
.
.
.
ImageButton i = new ImageButton();
i.ImageUrl = "/images/edit-icon.png";
i.ID = "icon_" + testID.ToString();
i.ToolTip = "Click to add a new comment";
i.Click += new ImageClickEventHandler(ImageButton_Click);
e.Row.Cells[5].Controls.Add(i);
.
.
.
}
The Dynamic ImageButton renders as follows
<input type="image" name="GridView1$ctl24$DGV1$ctl02$icon_1234" id="GridView1_ctl24_DGV1_ctl02icon_1234" title="Click to add a new comment" src="/images/edit-icon.png" onclick="ImageButton_Click;WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions("GridView1$ctl24$DGV1$ctl02$icon_1234", "", true, "", "", false, false))" style="border-width:0px;">
I notice the dynamic image button contains "onclick" vs the static imagebutton which has "OnClick", not sure if that makes a difference. I've been stumped on this for the last few hours, I don't know why the dynamic ImageButton is not firing. Please help.
UPDATE:
Thanks for all the feedback, I only need to process the ID of the ImageButton and it doesn't have to be server-side. Is there anyway I can do this from client-side using JavaScript in order to avoid postback? I tried re configuring the ImageButton as follows:
ImageButton i = new ImageButton();
i.ImageUrl = "/images/edit-icon.png";
i.ID = "icon_" + testID.ToString();
i.ToolTip = "Click to add a new comment";
i.OnClientClick = "submitComment(i.ID);return false;";
e.Row.Cells[5].Controls.Add(i);
I specify this function in the header of my web form
<head runat="server">
<script type="text/javascript">
submitComment(i.ID){
//Call WebMethod and pass comment along with this button's testID
}
</script>
</head>
But I get the following error when I click on the edit-icon ImageBtn:
Uncaught Reference Error: icon_12345 is not defined.
How is this so? If the edit-icons are already rendered and I'm just using client-side Javascript, shouldn't they already be defined and able to be properly referenced? This seems very counter intuitive to me. Am I missing something here? Is there some other control, besides and ImageButton, I can use for rendering the image without worrying about a postback, but that can still call the Javascript and pass along it's ID? What about using a regular Image control and just wiring it's onclick property to call the the submitComment function.

The problem is that on postback ASP.NET does not remember that you dynamically added a control on the previous request. The control simply does not exist in the control tree so the event will not fire.
You are asking for pain and suffering when trying to dynamically add controls. The control must be recreated by you before Page_Load if you want the event to fire. See http://forums.asp.net/t/1186195.aspx/1 for more details.
I would just hide the ImageButton for rows that don't need it.

You have to DataBind on each postback to get the control back into the ControlTree.

Maybe you could try something like this? Just make a regular img element with a data binding expression for the onclick attribute. "ID" must be a field in the gridview datasource.
<Columns>
<asp:TemplateField>
<ItemTemplate>
<img src="/images/edit-icon.png" onclick="submitComment('<%# Eval("ID", "icon_{0}") %>');"></img>
</ItemTemplate>
</asp:TemplateField>
</Columns>
Untested, but I've used something like this before. ASP.NET can get picky parsing the databinding expression.

If your page is routed, just add this code to masterpage on_load or on the page where you have the form
form1.Action = Request.Url.PathAndQuery;

Related

Gridview Custom controls and javascript functions c#

I have a custom control which is basically a Gridview and its first column is a TemplateField with a checkbox in the header and a checkbox for each of the rows. Clicking on the header checkbox should call some javascript to toggle each of the checkboxes in every row on display... it's a common requirement as we know.
My problem is (I'll go into more detail later) that when I have 2 of these controls on the same page, and I click the checkbox that selects all the chkboxes, the page calls the wrong bit of javascript (which only appears once on the rendered html page) and checks all the checkboxes on the wrong grid !
Here's my code:
<asp:TemplateField>
<HeaderTemplate>
<input type="checkbox" ID="chkSelectAll" onclick="SelectAllCheckboxes(this)"/>
</HeaderTemplate>
<ItemTemplate>
<asp:CheckBox runat="server" ID="chkSelected"></asp:CheckBox>
</ItemTemplate>
</asp:TemplateField>
Javascript:
<script>
function SelectAllCheckboxes(chk) {
$('#<%=gridPublishers.ClientID%>').find("input:checkbox").each(function ()
{
if (this != chk) { this.checked = chk.checked; }
});
}
</script>
So, the parent page has 2 of these controls on it. The first grid shows All the publishers and the other shows the publishers that have been selected from the first...
<h2>Selected Publishers</h2>
<cac:GridPublishers id="GridSelectedPublishers" runat="server" CssClass="GridSelectedPublishers" BindSource="DynamicFromSession" ShowMultiSelectCol="true" ShowFilterControls="false" NoRecordsMessage="No Publishers have been selected yet." />
<br /><br />
<h2>All Publishers</h2>
<cac:GridPublishers id="GridPublishers" runat="server" ShowMultiSelectCol="true" CssClass="GridPublishers" />
As I said earlier, the javascript only appears once on the rendered html page (and I understand why) but how can I get each instance of the custom control calling it's own javascript (or an alternative method) so that it only toggles its own checkboxes ??
...
I've also tried adding 2 javascript events and on grid bind trying to find the master checkbox and assign it the correct JS function, but I've searched each cell of the header row, and col 0 (where the control should be) holds 0 controls.
...
I've also tried adding a hidden button that on page load, I can assign it the correct javascript function (that will affect the correct gridview) and then the master checkbox fires the hidden button onClientClick event, but as the page reloads, it gets confused and fires the click event twice and from both grids apparently !
Please help !!
So I guess you realise that this line of code is causing the root problem of selecting the checkboxes in the wrong datagrid:
#<%=gridPublishers.ClientID%>').find
It's always going to pickup gridPublishers, and not GridSelectedPublishers.
So this is the area to fix. What you need to do is make this function a bit more abstract:
<input type="checkbox" ID="chkSelectAll" onclick="SelectAllCheckboxes(this)"/>
The onclick event passes 'this', but that's only a reference to the checkbox which isn't much help.
I'd suggest you try and make it something like this:
<input type="checkbox" ID="chkSelectAll" onclick="SelectAllCheckboxes(this,'GridSelectedPublishers')"/>
And then use the 2nd argument in the javascript function to grab the right datagrid.
Your problem now, is how to get that 2nd argument in there....you may be able to think of your own solution for this, but I would be tempted to make that checkbox an ASP checkbox, and find it during datagrid render and assign it the onClick with Attribute.Add
Making sense?
I've already marked "ben_the_builder's" answer as correct because it got me along the right line.
When I bind my grids I call this function:
private void Register_CheckAllControl_JScript()
{
// THIS IS A WORKAROUND FOR WHEN TWO OF THE SAME CUSTOM CONTROL LIVE ON THE SAME PAGE, EACH CONTROL'S JAVASCRIPT MUST SPECIFY THE ID OF THE CONTROL TO AFFECT
if (gridPublishers.HeaderRow != null)
{
CheckBox chkAll = gridPublishers.HeaderRow.FindControl("chkSelectAll") as CheckBox;
if (chkAll != null)
{
if (this.BindSource == Enumerators.BindSource.DynamicFromSession)
{
chkAll.Attributes.Add("onclick", "SelectAllCheckboxes(this,'GridSelectedPublishers');");
}
else
{
chkAll.Attributes.Add("onclick", "SelectAllCheckboxes(this,'GridPublishers');");
}
}
}
}
To access the "master" checkbox from code behind - it had to be an ASP control. an input control just wasn't recognised when iterating through the header cell collection.
My Javascript needed a little tweaking for the IDs to be correct. The control name I'm passing had to be name it was given on the parent page which belongs in the middle of the three tier final html output name (see the example, it'll make sense...)
My Javascript looks like this now:
<script>
function SelectAllCheckboxes(chk, ctrlName) {
//if ctrlName = "
$("#MainContent_" + ctrlName + "_gridPublishers").find("input:checkbox").each(function () {
if (this != chk) { this.checked = chk.checked; }
});
}
</script>
You want to use parameters. Instead of hard coding #<%=gridPublishers.ClientID%> into your javascript function, use the this parameter you pass to the function to determine the name of the grid view that contains the checkbox, then check everything inside that grid view.

Making a Control to be runat=server

In my application I have created a button dynamically & its name is "Dynamic_Button". Is it possible to make the button to be runat=server. I just tried the code but it doesn't work.
Dynamic_Button.Attributes.Add("runat","server");
Is there anyother ways to make it in serverside ?
When you manually instantiate a server control, there's no need to add the runat="server" attribute. This is a special attribute that's used only by the ASP.NET page parser to distinguish server controls from other markup.
The OnClick attribute in markup corresponds to the Click server-side event, which you hook up using the += operator.
So:
LinkButton lb = new LinkButton();
lb.ID = "LinkButton1";
lb.Click += click_event;
lb.Text = "testtext";
And the event handler
protected void click_event(object sender, EventArgs e)
{
}
In this example there is no need to add runat server because it already has it.
Have a look at this answer: https://stackoverflow.com/a/11337397/284240
This and this will guide you in creating and adding buttons at run time as well assigning events(client and server side).

ASP.NET use Hyperlinks instead of Buttons

I would like to add a Logout link to my form so our employees can log out of the job they are working on.
The code behind in my application is simple:
protected void Logout_Click(object sender, EventArgs e) {
MasterPage.Logout();
}
A asp.Button I can code by wiring up the onClick event.
How would I call this method using a asp.Hyperlink control?
You're looking for the LinkButton control. That gets rendered as an a tag, and the page will be posted back to itself so that your OnClick function can be invoked.
The Hyperlink control renders a simple hyperlink, which won't allow you to wire it up to a click handler. Try the LinkButton control instead.
Replace Hyperlink with LinkButton.
Hyperlink has no server side events.
http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.linkbutton.aspx
One thing I might suggest is to simply use CSS to style your actual-fact button to adopt the look of a link, as opposed to imitating a button from something that will likely be styled differently anyway.
When imitating a button, you are relying on the user having script enabled in their browser:
The LinkButton control renders JavaScript to the client browser. The
client browser must have JavaScript enabled for this control to
function properly.
Whereas a button that is a button will submit the form.
EDIT:
As per your comment, here is a quick example you could easily adapt:
CSS:
.hyperLinkButton
{
border:none;
background:none;
color:Navy;
cursor:pointer;
}
.hyperLinkButton:hover
{
text-decoration:underline;
}
Mark-up:
<asp:Button runat="server" CssClass="hyperLinkButton"
Text="Is it a HyperLink? Is it a LinkButton? No, it's a Button!" />

use javascript value and doPostback?

i have a page where on click of a button, a javascript function runs. It then aggregates some data and places the data on a hidden field in this page. It then opens a new window. This new window picks up this aggregated data like so :-
$('#accepted').val(window.opener.$('#accepted').val());
where accepted is the hidden field in both parent and child window (no runat="server" was used). The issue now is that i require this data to databind two grids. Currently I've done a doPostback on both grids, but what i really want to do is doPostback for the form once and handle the databinding the PageLoad event. So two questions :-
1) How do i doPostback the form?
2) How do i do this while still being able to differentiate from the actual form submission?
To post the form you should just be able to add a call to __doPostback in your javascript, after the accepted field is set. You can use the EventTarget and EventArgument parameters of the __doPostback to control the binding in your grid.
So, you could put this in your js:
__doPostback('rebindGrid', '');
and then this in your page load event:
if (Request.Form["__EVENTTARGET"] == "rebindGrid")
{
//....Do so stuff
}
In order to tie it in more directly with the postback model I wrap mine with some C#
C# Extension Method
public static string GetPostBackLink (this Control c, string argument = "") {
return c.Page.ClientScript.GetPostBackEventReference(ctl, argument, true) + ";";
}
ASPX
<asp:LinkButton id="lnkDoThis" runat="server" onclick="lnkDoThis_Click"
style="display: none;"></asp:LinkButton>
<asp:HiddenField id="hdnParamHolder" runat="server" />
JS
function DoSomething(param) {
$("[id$='hdnDealTemp']").val(param);
<%= lnkDoThis.GetPostBackLink() %>
}
CodeBehind
protected void lnkDoThis_Click (object sender, EventArgs e) {
var myParam = hdnParamHolder.Value;
// Do server actions here
}
As for the opening in a second window ... I am not sure I follow when you want this to happen? If it is after the postback you will need to read from the hdnParamHolder control when the page reloads.

Postback events from within DataView

I'm presenting information from a DataTable on my page and would like to add some sorting functionality which goes a bit beyond a straight forward column sort. As such I have been trying to place LinkButtons in the HeaderItems of my GridView which post-back to functions that change session information before reloading the page.
Clicking my links DOES cause a post-back but they don't seem to generate any OnClick events as my OnClick functions don't get executed. I have AutoEventWireup set to true and if I move the links out of the GridView they work fine.
I've got around the problem by creating regular anchors, appending queries to their hrefs and checking for them at page load but I'd prefer C# to be doing the grunt work. Any ideas?
Update: To clarify the IDs of the controls match their OnClick function names.
You're on the right track but try working with the Command Name/Argument of the LinkButton. Try something like this:
In the HeaderTemplate of the the TemplateField, add a LinkButton and set the CommandName and CommandArgument
<HeaderTemplate>
<asp:LinkButton ID="LinkButton1" runat="server" CommandName="sort" CommandArgument="Products" Text="<%# Bind('ProductName")' />
</HeaderTemplate>
Next, set the RowCommand event of the GridView
protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "sort")
{
//Now sort by e.CommandArgument
}
}
This way, you have a lot of control of your LinkButtons and you don't need to do much work to keep track of them.
Two things to keep in mind when using events on dynamically generated controls in ASP.Net:
Firstly, the controls should ideally be created in the Page.Init event handler. This is to ensure that the controls have already been created before the event handling code is ran.
Secondly, you must assign the same value to the controls ID property, so that the event handler code knows that that was the control that should handle the event.
You can specify the method to call when the link is clicked.
<HeaderTemplate>
<asp:LinkButton
ID="lnkHdr1"
Text="Hdr1"
OnCommand="lnkHdr1_OnCommand"
CommandArgument="Hdr1"
runat="server"></asp:LinkButton>
</HeaderTemplate>
The code-behind:
protected void lnkHdr1_OnCommand(object sender, CommandEventArgs e)
{
// e.CommandArgument
}

Categories