I have created a custom server control. It looks great and the rendered HTML is also as it should be. I initially had it extending the ControlContainer and now it extends the WebControl (both behave the same.) It has two properties, ImageUrl and Text. Essentially it will render a generic HTML tag with an and tags within it.
My problem is that the ServerClick event that is exposed (by NamingContainer I beleive) doesn't seem to fire. If I add any of the ASP buttons (Link, Image or regular) and associate to that Click event it fires but of course I have extra rendered content. It successfully runs the javascript and does the __dopostback call. But it must not see the given control ID or something because the event never gets fired.
using System;
using System.ComponentModel;
using System.Drawing.Design;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
namespace PLSO.Info.Web.UI {
[DefaultEvent("Submit")]
[DefaultProperty("Text")]
[ToolboxData("<{0}:ComboButton runat=\"server\"> </{0}:ComboButton>")]
public class ComboButton : WebControl {
private HtmlImage imageControl;
private HtmlGenericControl spanControl;
private static readonly object EventSubmitKey = new object();
[Bindable(true)]
[Category("Appearance")]
[DefaultValue("")]
[Description("The text to display on the button.")]
public string Text {
get { return ViewState["NewText"] as string; }
set { ViewState["NewText"] = value; }
}
[DefaultValue("")]
[Bindable(true)]
[Category("Appearance")]
[UrlProperty()]
[Editor("System.Web.UI.Design.ImageUrlEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
public string ImageUrl {
get {
EnsureChildControls();
return this.imageControl.Src;
}
set {
EnsureChildControls();
this.imageControl.Src = value;
}
} // ImageUrl - Property
public override string CssClass {
get { return ViewState["CssClass"] as string; }
set { ViewState["CssClass"] = value; }
}
[Category("Action")]
[Description("Raised when the user clicks the button.")]
public event EventHandler Submit {
add { Events.AddHandler(EventSubmitKey, value); }
remove { Events.RemoveHandler(EventSubmitKey, value); }
}
protected virtual void OnSubmit(EventArgs e) {
EventHandler SubmitHandler = (EventHandler)Events[EventSubmitKey];
if (SubmitHandler != null)
SubmitHandler(this, e);
}
void ComboButton_Submit(object sender, EventArgs e) {
OnSubmit(EventArgs.Empty);
}
protected override void CreateChildControls() {
Controls.Clear();
imageControl = new HtmlImage();
imageControl.Src = this.ImageUrl;
imageControl.Alt = this.Text;
this.Controls.Add(imageControl);
spanControl = new HtmlGenericControl("span");
spanControl.InnerText = this.Text;
this.Controls.Add(spanControl);
this.Submit += new EventHandler(ComboButton_Submit);
ChildControlsCreated = true;
}
protected override void Render(HtmlTextWriter writer) {
PostBackOptions pbo = new PostBackOptions(this);
AddAttributesToRender(writer);
writer.AddAttribute(HtmlTextWriterAttribute.Class, this.CssClass);
writer.AddAttribute("onclick", string.Format("javascript:{0}", Page.ClientScript.GetPostBackEventReference(pbo)));
writer.RenderBeginTag(HtmlTextWriterTag.Button);
imageControl.RenderControl(writer);
spanControl.RenderControl(writer);
writer.RenderEndTag();
}
}
}
Here is my markup. I put in this control and then a regular ASP:Button. That regular button's event gets hit! Not mine.
<ucs:ComboButton ID="btnT4" runat="server" Text="Please" CssClass="PButtonCombo" ImageUrl="~/Styles/icons/edit-find.png" OnSubmit="btnT4_Submit" />
<asp:Button ID="btnT5" runat="server" Text="TEST" onclick="btnT5_Click" UseSubmitBehavior="False" />
And here is the rendered HTML:
<button id="MainContent_btnT4" class="PButtonCombo" onclick="javascript:__doPostBack('ctl00$MainContent$btnT4','')"><img src="../Styles/icons/edit-find.png" alt="Please" /><span>Please</span></button>
<input type="button" name="ctl00$MainContent$btnT5" value="TEST" onclick="javascript:__doPostBack('ctl00$MainContent$btnT5','')" id="MainContent_btnT5" />
I have to believe I am close but just missing something. Been tweaking it for hours today, PLEASE HELP!
EDIT:
Thanks to @James answer, all I did was add the following to the top of the above example. It did the trick but now fires twice. Not sure why? So that is my current question:
public class ComboButton : WebControl, IPostBackEventHandler {
public void RaisePostBackEvent(string eventArgument) {
OnClick(new EventArgs());
}
[Category("Action")]
[Description("Raised when the user clicks the button.")]
public event EventHandler Click;
protected virtual void OnClick(EventArgs e) {
if (Click != null)
Click(this, e);
}
EDIT 2 == SOLUTION
using System.ComponentModel;
using System.Drawing.Design;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
namespace PLSO.Info.Web.UI {
[DefaultEvent("Submit")]
[DefaultProperty("Text")]
[ToolboxData("<{0}:ComboButton runat=\"server\"> </{0}:ComboButton>")]
public class ComboButton : Button {
private HtmlImage imageControl;
private HtmlGenericControl spanControl;
[DefaultValue("")]
[Bindable(true)]
[Category("Appearance")]
[UrlProperty()]
[Editor("System.Web.UI.Design.ImageUrlEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
public string ImageUrl {
get {
EnsureChildControls();
return this.imageControl.Src;
}
set {
EnsureChildControls();
this.imageControl.Src = value;
}
} // ImageUrl - Property
protected override void CreateChildControls() {
Controls.Clear();
imageControl = new HtmlImage();
imageControl.Src = this.ImageUrl;
imageControl.Alt = this.Text;
this.Controls.Add(imageControl);
spanControl = new HtmlGenericControl("span");
spanControl.InnerText = this.Text;
this.Controls.Add(spanControl);
ChildControlsCreated = true;
} // CreateChildControls - Method - Override
protected override void Render(HtmlTextWriter writer) {
PostBackOptions pbo = new PostBackOptions(this);
AddAttributesToRender(writer);
writer.RenderBeginTag(HtmlTextWriterTag.Button);
imageControl.RenderControl(writer);
spanControl.RenderControl(writer);
writer.RenderEndTag();
} // Render - Event - Override
}
}
Try implementing the
IPostBackEventHandler
interface:Here's an article that explains the implementation of the
IPostBackEventHandler
interface:http://msdn.microsoft.com/en-us/library/system.web.ui.ipostbackeventhandler.aspx
EDIT
If your events are in some way dependent on data, you need to implement the
IPostBackDataHandler
interface. For example, you would use theIPostBackDataHandler
interface to fire theOnTextChanged
event of a TextBox:Here's an article that explains the implementation of the
IPostBackDataHandler
interface:http://msdn.microsoft.com/en-us/library/system.web.ui.ipostbackdatahandler.aspx