ImageButton PostBack Help

  • Thread starter Anthony Merante
  • Start date
A

Anthony Merante

I cant figure out why this ImageButton doesn't post back the way a Button
does. Can anyone explain to me why the RaisePostBackEvent() method doesn't
get called here..

public class MyControl : CompositeControl, IPostBackEventHandler

{

ImageButton img;

protected override void CreateChildControls()

{

this.CreateControls();

}

private void CreateControls()

{

img = new ImageButton();

img.ID = this.UniqueID;

}



protected override void Render(HtmlTextWriter writer)

{

img.RenderControl(writer);

}



#region IPostBackEventHandler Members



public void RaisePostBackEvent(string eventArgument)

{

throw new Exception("The method or operation is not
implemented.");

}



#endregion

}



If I change the ImageButton to a Button, the RaisePostBackEvent is called,
but not with a ImageButton. Also, I'd like to write up a Command Event
instead of the whole RaisePostbackEvent but that wouldn't fire either.



What silliness is happening here?



Thanks
 
T

Teemu Keiski

In CreateControls, you also need to add the ImageButton to Controls
collection, so that postbacking works

private void CreateControls()
{
img = new ImageButton();
img.ID = this.UniqueID;
Controls.Add(img);
}
 
A

Anthony Merante

Thanks for responding Teemu,

That doesnt seem to help. I've done that as well. I must have deleted that
line when i cleaned up the example to show.

I'm still at a loss to understand why this doesnt work. Any other ideas?
 
T

Teemu Keiski

Hi,

sorry, I took a deeper look into your control. Reason is that ImageButton
handles its own Click event (it itself implements IPostBackEventHandler) and
therefore it won't be raised in your own custom control. In order to call
RaisePostBackEvent in your custom control instead of on ImageButton, you
would need have your own custom control as source for the postbacking (e.g
it should cause the postback). Now it's the ImageButton, since it will be
clicked, and it handles its own events.

Simple way is to wire event handler for the ImageButton's Click event, that
way you'd get notified when it is clicked and remove IPostBackEventHandler
totally from your custom control.
 
A

Anthony Merante

Hmm, interesting.

I think i understand what you're saying but both the Button and ImageButton
implement IPostBackEventHandler. Why would the Button work and the
ImageButton not work. I have gotten the example to work by doing just as you
suggested, ie having the custom control handling the postback. One thing i
noticed while looking at Object Browser is that Button inherits from
WebControl and the ImageButton Inherits from Image ( which inherits from
WebControl ). Do you think that this inheritance hierarchy is not bubbling
up the event properly ?

Also, I HAVE tried to wire up a click event for my imageButton and that
didnt work either (though i haven't tried it without IPostBackEventHandler,
I'll try this tonight.)

Thanks again for responding,
t
 
S

sam

What you are doing is seting the ID of the button to the UniqueID of
your composite control, so that when the button is rendered is name
attribute will be rendered in such a way so as your composite control
will receive the postback event. Not really my cup of tea, but thats
just my opinion.

Anyway, if you download Reflector:

(In Page class)
private void ProcessPostData(NameValueCollection postData, bool
fBeforeLoad)
{
if (this._changedPostDataConsumers == null)
{
this._changedPostDataConsumers = new ArrayList();
}
if (postData != null)
{
foreach (string text1 in postData)
{
if ((text1 != null) &&
!Page.IsSystemPostField(text1))
{
Control control1 = this.FindControl(text1);
if (control1 == null)
{
if (fBeforeLoad)
{
if (this._leftoverPostData == null)
{
this._leftoverPostData = new
NameValueCollection();
}
this._leftoverPostData.Add(text1,
null);
}
}
else
{
IPostBackDataHandler handler1 =
control1.PostBackDataHandler;
if (handler1 == null)
{
if (control1.PostBackEventHandler
!= null)
{

this.RegisterRequiresRaiseEvent(control1.PostBackEventHandler);
}
}
.....

The above code is sniffing the form data for name values that match the
uniqueID of controls that implement IPostBackEventHandler and
automatically registering them for RaisePostBackEvent (which it what
fires your RaisePostBackEvent and incidentally the OnClick event in the
standard MS controls). This sniffing is successful for the button
because it submits just its name in the form collection. The
ImageButton instead submits name.x and name.y, and so the sniffing will
not be successful

Also, the ImageButton implements both IPostBackEventHandler *and*
IPostBackDataHandler, and so it wouldnt be successful anyway (as you
can see by a close inspection of the code above)

Here is the code I'm talking about for
Page.RegisterRequiresRaiseEvent():

public virtual void RegisterRequiresRaiseEvent(IPostBackEventHandler
control)
{
this._registeredControlThatRequireRaiseEvent = control;
}

which in Page.RaisePostBackEvent() finally calls your
RaisePostBackEvent:

private void RaisePostBackEvent(NameValueCollection postData)
{
if (this._registeredControlThatRequireRaiseEvent != null)
{

this.RaisePostBackEvent(this._registeredControlThatRequireRaiseEvent,
null);
}
}
.....

It seems that if you simply called
Page.RegisterRequiresRaiseEvent(this) sometime in your composite
control that would work fine.

I'm not sure why they did it this way. Its also possible my analysis
may be wrong. Can Teemu shed some more light on this?

-Sam
 
T

Teemu Keiski

Inline:
Also, the ImageButton implements both IPostBackEventHandler *and*
IPostBackDataHandler, and so it wouldnt be successful anyway (as you
can see by a close inspection of the code above)

It was with v1.x that if control implemented both IPostBAckEventHandler and
IPostBackDataHandler, IPostBackDataHandler implementation was the one
getting called and IPostBackEventHandler wasn't. Therefore this type of
control would need to call Page.RegisterRequiresRaiseEvent like ImageButton
does in its LoadPostData method (IPostBackDataHandler implementation).
However, I think it should work with v2.x already, at least with some of my
experimenting it did but based on reflected code it seems to be like in
v.1.x. And ImageButton's implementation supports that view.
Here is the code I'm talking about for
Page.RegisterRequiresRaiseEvent():
...
It seems that if you simply called
Page.RegisterRequiresRaiseEvent(this) sometime in your composite
control that would work fine.

I'm not sure why they did it this way. Its also possible my analysis
may be wrong. Can Teemu shed some more light on this?

Exaclty because RegisterRaiseEvent is used when control itself cannot raise
the event (or better said page framework cannot) one reason being dual
implementation of interfaces or when the control itself isn't source of the
event. If you have CompositeControl with ImageButton as child control and
when you click the ImageButton, your composite control's
IPostBackEventHandler implementation won't be called. ImageButton's will be,
because it was the control responsible for the postback.

Implementing IPostBackEventHandler in custom control would make sense only
when control itself will be the source of the event. Basically you could
just wire handler for the Click event internally in the control and then
raise a new top-level event with no interface implementation.

Based on previous discussion I wrote a couple of implementations with Button
and ImageButton as child control.

'ButtonCompositeControl.cs

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

/// <summary>
/// Summary description for ButtonCompositeControl
/// </summary>
namespace Samples
{
public class ButtonCompositeControl : CompositeControl
{

protected override void CreateChildControls()
{
Button btn = new Button();
btn.ID = "Button1";
btn.Text = "Click me, I'm a Button";
btn.Click += new EventHandler(btn_Click);
Controls.Add(btn);
}

void btn_Click(object sender, EventArgs e)
{
OnButtonClick(e);
}

#region "Event implementation"
private static readonly object EventButtonClick = new object();

public event EventHandler ButtonClick
{
add
{
Events.AddHandler(EventButtonClick, value);
}
remove
{
Events.RemoveHandler(EventButtonClick, value);
}
}

protected virtual void OnButtonClick(EventArgs e)
{
EventHandler eh = Events[EventButtonClick] as EventHandler;
if (eh != null)
{
eh(this, e);
}
}
#endregion





}
}

'ImageButtonCompositeControl.cs

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

/// <summary>
/// Summary description for ButtonCompositeControl
/// </summary>
namespace Samples
{
public class ImageButtonCompositeControl : CompositeControl
{

protected override void CreateChildControls()
{
ImageButton imgbtn = new ImageButton();
imgbtn.ID = "ImageButton1";
imgbtn.AlternateText = "Click me, I'm an ImageButton";
imgbtn.Click += new ImageClickEventHandler(imgbtn_Click);
Controls.Add(imgbtn);
}

void imgbtn_Click(object sender, ImageClickEventArgs e)
{
OnImageButtonClick(e);
}

#region "Event implementation"
private static readonly object EventImageButtonClick = new object();

public event ImageClickEventHandler ImageButtonClick
{
add
{
Events.AddHandler(EventImageButtonClick, value);
}
remove
{
Events.RemoveHandler(EventImageButtonClick, value);
}
}

protected virtual void OnImageButtonClick(ImageClickEventArgs e)
{
ImageClickEventHandler iceh = Events[EventImageButtonClick] as
ImageClickEventHandler;
if (iceh != null)
{
iceh(this, e);
}
}
#endregion





}
}

'AND HERE's THE TEST PAGE FOR THEM (CODE FILES IN APP_CODE)

<%@ Page Language="C#" %>
<%@ Register Namespace="Samples" TagPrefix="ss" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

protected void Button1_ButtonClick(object sender, EventArgs e)
{
Response.Write("Button1 was clicked");
}


protected void Button2_ImageButtonClick(object sender,
ImageClickEventArgs e)
{
Response.Write("Button2 was clicked");
}
</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<ss:ButtonCompositeControl runat="server" ID="Button1"
OnButtonClick="Button1_ButtonClick" />
<ss:ImageButtonCompositeControl runat="server" ID="Button2"
OnImageButtonClick="Button2_ImageButtonClick" />
</div>
</form>
</body>
</html>
 
T

Tony Merante

Teemu and Sam,

Thanks for the help. I think i have seen my errors. I think i figured out a
few things with your help.

this line was my problem
img.ID = this.UniqueId;

I should not have set my consituent control's ID the same as the
CustomControl. I did that because of examples i've seen that implement the
IPostbackDataHandler. In hindsight, I see that I dont need those Methods in
that interface to capture an event that my constiuent control already
handles for me. Once i set the ID of my IMageButton to anything other than
this.UniqueID, the img button's Click event fired.

Now, to my next question.. I'll start another thread on that.

Thanks a lot for your help!!
Tony Merante




Teemu Keiski said:
Inline:
Also, the ImageButton implements both IPostBackEventHandler *and*
IPostBackDataHandler, and so it wouldnt be successful anyway (as you
can see by a close inspection of the code above)

It was with v1.x that if control implemented both IPostBAckEventHandler
and IPostBackDataHandler, IPostBackDataHandler implementation was the one
getting called and IPostBackEventHandler wasn't. Therefore this type of
control would need to call Page.RegisterRequiresRaiseEvent like
ImageButton does in its LoadPostData method (IPostBackDataHandler
implementation). However, I think it should work with v2.x already, at
least with some of my experimenting it did but based on reflected code it
seems to be like in v.1.x. And ImageButton's implementation supports that
view.
Here is the code I'm talking about for
Page.RegisterRequiresRaiseEvent():
...
It seems that if you simply called
Page.RegisterRequiresRaiseEvent(this) sometime in your composite
control that would work fine.

I'm not sure why they did it this way. Its also possible my analysis
may be wrong. Can Teemu shed some more light on this?

Exaclty because RegisterRaiseEvent is used when control itself cannot
raise the event (or better said page framework cannot) one reason being
dual implementation of interfaces or when the control itself isn't source
of the event. If you have CompositeControl with ImageButton as child
control and when you click the ImageButton, your composite control's
IPostBackEventHandler implementation won't be called. ImageButton's will
be, because it was the control responsible for the postback.

Implementing IPostBackEventHandler in custom control would make sense only
when control itself will be the source of the event. Basically you could
just wire handler for the Click event internally in the control and then
raise a new top-level event with no interface implementation.

Based on previous discussion I wrote a couple of implementations with
Button and ImageButton as child control.

'ButtonCompositeControl.cs

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

/// <summary>
/// Summary description for ButtonCompositeControl
/// </summary>
namespace Samples
{
public class ButtonCompositeControl : CompositeControl
{

protected override void CreateChildControls()
{
Button btn = new Button();
btn.ID = "Button1";
btn.Text = "Click me, I'm a Button";
btn.Click += new EventHandler(btn_Click);
Controls.Add(btn);
}

void btn_Click(object sender, EventArgs e)
{
OnButtonClick(e);
}

#region "Event implementation"
private static readonly object EventButtonClick = new object();

public event EventHandler ButtonClick
{
add
{
Events.AddHandler(EventButtonClick, value);
}
remove
{
Events.RemoveHandler(EventButtonClick, value);
}
}

protected virtual void OnButtonClick(EventArgs e)
{
EventHandler eh = Events[EventButtonClick] as EventHandler;
if (eh != null)
{
eh(this, e);
}
}
#endregion





}
}

'ImageButtonCompositeControl.cs

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

/// <summary>
/// Summary description for ButtonCompositeControl
/// </summary>
namespace Samples
{
public class ImageButtonCompositeControl : CompositeControl
{

protected override void CreateChildControls()
{
ImageButton imgbtn = new ImageButton();
imgbtn.ID = "ImageButton1";
imgbtn.AlternateText = "Click me, I'm an ImageButton";
imgbtn.Click += new ImageClickEventHandler(imgbtn_Click);
Controls.Add(imgbtn);
}

void imgbtn_Click(object sender, ImageClickEventArgs e)
{
OnImageButtonClick(e);
}

#region "Event implementation"
private static readonly object EventImageButtonClick = new
object();

public event ImageClickEventHandler ImageButtonClick
{
add
{
Events.AddHandler(EventImageButtonClick, value);
}
remove
{
Events.RemoveHandler(EventImageButtonClick, value);
}
}

protected virtual void OnImageButtonClick(ImageClickEventArgs e)
{
ImageClickEventHandler iceh = Events[EventImageButtonClick] as
ImageClickEventHandler;
if (iceh != null)
{
iceh(this, e);
}
}
#endregion





}
}

'AND HERE's THE TEST PAGE FOR THEM (CODE FILES IN APP_CODE)

<%@ Page Language="C#" %>
<%@ Register Namespace="Samples" TagPrefix="ss" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

protected void Button1_ButtonClick(object sender, EventArgs e)
{
Response.Write("Button1 was clicked");
}


protected void Button2_ImageButtonClick(object sender,
ImageClickEventArgs e)
{
Response.Write("Button2 was clicked");
}
</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<ss:ButtonCompositeControl runat="server" ID="Button1"
OnButtonClick="Button1_ButtonClick" />
<ss:ImageButtonCompositeControl runat="server" ID="Button2"
OnImageButtonClick="Button2_ImageButtonClick" />
</div>
</form>
</body>
</html>


--
Teemu Keiski
ASP.NET MVP, AspInsider
Finland, EU
http://blogs.aspadvice.com/joteke
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top