ASP CompositeControl & ScriptManager - asp.net

I'm really new to the WebControl / CompositeControl world, and I have a small test class I am playing with. It's just a LinkButton that updates when clicked. Things work great when I leave it out of UpdatePanel. But when I try to run it inside I still get a full page POST response. How can I make this class work inside a UpdatePanel?
Here's the class:
public class Test2 : CompositeControl
{
private static readonly object testButtonEvent = new object();
public event EventHandler OnTestClick
{
add { Events.AddHandler(testButtonEvent, value); }
remove { Events.RemoveHandler(testButtonEvent, value); }
}
private LinkButton testLinkButton;
public virtual string testLinkButtonText
{
get
{
object o = ViewState["testLinkButtonText"];
return (o == null) ? String.Empty : (string)o;
}
set
{
if (value == null)
ViewState.Remove("testLinkButtonText");
else
ViewState["testLinkButtonText"] = value;
}
}
protected override void OnInit(EventArgs e)
{
/* This stuff makes it ajax friendly but stops the text rendering
EnsureChildControls();
ScriptManager ScMan = ScriptManager.GetCurrent(Page);
if (ScMan != null)
{
ScMan.RegisterAsyncPostBackControl(testLinkButton);
} */
base.OnInit(e);
}
protected override void CreateChildControls()
{
Controls.Clear();
testLinkButton = new LinkButton();
testLinkButton.Command += new CommandEventHandler(testClick);
testLinkButtonText = "Test ViewState Text";
Controls.Add(testLinkButton);
}
void testClick(object sender, CommandEventArgs e)
{
testLinkButtonText = "Updated Text On " + DateTime.Now.ToLongTimeString();
}
protected override void Render(HtmlTextWriter writer)
{
RenderContents(writer);
}
protected override void RenderContents(HtmlTextWriter writer)
{
EnsureChildControls();
testLinkButton.Text = testLinkButtonText;
testLinkButton.RenderControl(writer);
}
}
The code in OnInit() causes the control to post correctly, but I don't get the updated text for the LinkButton. It is still firing off the event - when I debug I can see it being called. What's the proper way to set this control up for use in a UpdatePanel?
Usage, just in case:
<asp:UpdatePanel runat="server" UpdateMode="Conditional">
<ContentTemplate>
<cc:Test2 ID="jqTest02" runat="server" />
</ContentTemplate>
</asp:UpdatePanel>

You have to give the button an ID property...this is used in the client-side javascript that drives the UpdatePanel. More specifically, it's listed in the list of controls to intercept and do async postbacks for.
testLinkButton.ID = "btn";

Related

Dynamic Control creation, event handling and control properties in asp.net

I am trying to solve the following issue:
I have a user control that needs to be dynamically loaded inside another control. This dynamically loaded control raises an event and as per my knowledge the events raised by dynamically loaded control will only be handled correctly if the control is created and loaded during the onload event. There is one more constraint that i have to consider when loading the control dynamically and that is a property in parent control. This property will determine if the control should be loaded or not.
Pseudo Code:
ControlA
Property ShowControl
ControlA has a CheckBox(chkShowControlIfSelected)
OnLoadEvent()
If chkShowControlIfSelected.checked checked and ShowControlProperty is set
{
reate ControlB Dynamically
ControlB.Event += EventHandler()
Add ControlB to ControlCollection
}
The problem i am running into is that if I include the code to load the controlB in prerender event then the property ShowControl is set correctly but the EventHandler() is not called. If I put the code to load the controlB dynamically in pageLoad event then property ShowControl is not yet set but in that case the eventHandler Code is called correctly.
Am i missing something or handling the code in incorrect event handlers?
Following is the working example:
ControlA:
public partial class ControlA : System.Web.UI.UserControl
{
public bool ShowControl
{
get
{
if (this.ViewState["ShowControl"] == null)
return false;
else
return (bool)this.ViewState["ShowControl"];
}
set
{
this.ViewState["ShowControl"] = value;
}
}
protected void Page_Load(object sender, EventArgs e)
{
if (this.ShowControl)
{
var controlB = (ControlB)this.LoadControl("ControlB.ascx");
controlB.FileUploadingComplete += controlB_FileUploadingComplete;
this.pnl1.Controls.Add(controlB);
}
}
void controlB_FileUploadingComplete(object sender, EventArgs e)
{
//throw new NotImplementedException();
Trace.Write("file upload completed");
}
}
ControlB:
public partial class ControlB : System.Web.UI.UserControl
{
public event EventHandler FileUploadingComplete;
protected void OnFileUploadingComplete()
{
if (this.FileUploadingComplete != null)
this.FileUploadingComplete(this, EventArgs.Empty);
}
protected void btn1_Click(object sender, EventArgs e)
{
this.OnFileUploadingComplete();
}
}
Page (has ControlA present):
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
this.ControlA1.ShowControl = true;
}
}

How to add a Requiredfieldvalidator to a Custom Dropdownlist

I've tried to create a custom control which inherits from DropDownList. In this control I want to add a RequiredFieldValidator.
If I delete the marked line, the page will be rendered, but the Validator doesn't work. With the marked line the following error occurred:
System.Web.HttpException: TEST lässt keine untergeordneten Steuerelemente zu.
public class TEST: DropDownList
{
private RequiredFieldValidator rfv;
protected override void OnInit(EventArgs e)
{
rfv = new RequiredFieldValidator();
rfv.ID = this.ClientID;
rfv.ControlToValidate = ID;
rfv.Display = ValidatorDisplay.Static;
rfv.SetFocusOnError = true;
rfv.InitialValue = "";
rfv.CssClass = "validator";
rfv.ValidationGroup = ValidationGroup;
--> Controls.Add(rfv); <--
}
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
rfv.RenderControl(writer);
}
}
I developed a similar control which inherits from TextBox, and there it works fine (with the marked line).

How to add event to custom control that fired when click a button in it?

I want to build custom control consist of html code which shows table, and in the footer of this table I need to add three buttons.
What I did is inherit from WebControl class:
public class MyCustomControl : WebControl {
protected override void RenderContents(HtmlTextWriter output)
{
// using output.Write I write the table in html code
// and I write the three buttons using <input> tag in html not <asp:button> tag
}
}
What I want here is to add three events one to each button I wrote, and those events would be used in the user interface and fired when the proper button clicked:
<asp:MyCustomControl runat="server" id="myCtrl" onButton1Click="Button1_Click" onButton2Click="Button2_Click" />
How can I do this ?
Thanx
**UPDATE1:
The render code in my custom control would be like this:
protected override void RenderContents(HtmlTextWriter output)
{
output.Write("<table> ......");
output.Write("<input id='button1' type='button'>");
output.Write("<input id='button2' type='button'>");
output.Write("<input id='button3' type='button'>");
output.Write(".........</table>");
}
So how would I make 'button1' fire the event on server side?
**UPDATE2:
This is what the code looks like:
public class MyCustomControl : WebControl
{
public Button Button1 = new Button {Text = "Button1"};
public Button Button2 = new Button {Text = "Button2"};
public event EventHandler Button1_Click;
public event EventHandler Button2_Click;
protected override void OnPreRender(EventArgs e)
{
Button1.Click += Button1Click;
Button2.Click += Button2Click;
base.OnPreRender(e);
}
protected override void RenderContents(HtmlTextWriter output)
{
using (var plh = new PlaceHolder())
{
var htmlCode = new StringBuilder();
htmlCode.Append("....html code for table...");
var container = new HtmlGenericControl { InnerHtml = htmlCode.ToString() };
plh.Controls.Add(container);
plh.Controls.Add(Button1);
plh.Controls.Add(Button2);
plh.RenderControl(output);
htmlCode.Append("..../html code for table...");
}
}
private void Button1Click(object sender, EventArgs e)
{
if (Button1_Click != null)
Button1_Click(this, e);
}
private void Button2Click(object sender, EventArgs e)
{
if (Button2_Click != null)
Button2_Click(this, e);
}
And in the page.aspx:
<cc1:MyCustomControl ID="myCtrl" runat="server" onbutton1_click="MyCustomControl_Button1_Click" />
But even with this the button1's click method 'MyCustomControl_Button1_Click' not called.
declare your new events like this:
public event EventHandler OnButton1Click;
public event EventHandler OnButton2Click;
public event EventHandler OnButton3Click;
this in the click method on each button do something like this:
public void Button1_Click(object sender, EventArgs e)
{
if (OnButton1Click != null)
OnButton1Click(this, null);
}
Within MyCustomControl class, define events that take (object sender, EventArgs e) and return void.
Then add (still within MyCustomControl) the event handlers for the click events of the internal buttons.
Inside these methods, do CustomClickEvent.Invoke().
Then, in the page containing your custom control, do myControl.CustomClickEvent += new CustomClickEvent(name_of_method_within_page);
To render an asp control from your Custom Control do that from Render base method:
protected override void Render(HtmlTextWriter writer)
Prepare before your controls with this method
protected override void CreateChildControls()

Viewstate null on postback

So I have a listbox on my page and some textfields. Through the textfields I can add an item to my listbox (click the button, it adds it to a private List which is then set as a ViewState and the list is databound again). My listbox is also in an updatepanel which gets triggered on the button's Click event. Problem: My Viewstate remains null on a postback so it gets reset each time.
Some code:
private const string VIEW_INGREDIENTS = "IngredientsList";
private const string VIEW_LANGUAGE = "CurrentLanguage";
private List<IngredientData> _ingredientsList;
protected void Page_PreInit(object sender, EventArgs e)
{
if (Page.IsPostBack)
{
if (ViewState[VIEW_INGREDIENTS] != null)
{
_ingredientsList = (List<IngredientData>) ViewState[VIEW_INGREDIENTS];
}
}
else
{
// prepare ingredient lists
_ingredientsList = new List<IngredientData>();
}
}
protected void Page_Load(object sender, EventArgs e)
{
lstIngredients.DataSource = _ingredientsList;
lstIngredients.DataTextField = "Text";
lstIngredients.DataValueField = "Name";
lstIngredients.DataBind();
}
protected void btnAddIngredient_Click(object sender, EventArgs e)
{
_ingredientsList.Add(new IngredientData { Name = txtIngredientName.Text, Quantity = txtUnitQuantity.Text, Unit = lstUnits.SelectedValue });
ViewState[VIEW_INGREDIENTS] = _ingredientsList;
lstIngredients.DataSource = _ingredientsList;
lstIngredients.DataBind();
}
You're using vewstate during PreInit ? Try to check that a bit later during PreLoad.
Check if the page has EnableViewState="true":
<%# Page Language="C#" EnableViewState="true" ...
And verify the site-wide setting in web.config:
<pages enableViewState="true" enableViewStateMac="true" ... />
Now ASP.NET has built-in viewstate for list controls, so I wonder why you're writing custom code for it. The default viewstate should work well for what you're trying to accomplish.

Custom TextBox with built-in Validator: server side validation not firing

I have a class that looks like this:
public class TextField : TextBox
{
public bool Required { get; set; }
RequiredFieldValidator _validator;
protected override void CreateChildControls()
{
base.CreateChildControls();
_validator = new RequiredFieldValidator();
_validator.ControlToValidate = this.ID;
if(Required)
Controls.Add(_validator);
}
public override void Render(HtmlTextWriter tw)
{
base.Render(tw);
if(Required)
_validator.RenderControl(tw);
}
}
This has been working for a while in a internal application where javascript is always enabled. I recently noticed that an upstream javascript error can prevent the validators from firing, so the server side validation should kick in... right? right?
So the Page.IsValid property always returns true (I even tried explicitly calling Page.Validate() before-hand).
After some digging, I found that the validator init method should add the validator to the page, but due to the way I'm building it up, I don't think this ever happens. Thus, client side validation works, but server side validation does not.
I've tried this:
protected override OnInit()
{
base.OnInit();
Page.Validators.Add(_validator); // <-- validator is null here
}
But of course the validator is null here (and sometimes it's not required so it shouldn't be added)... but OnInit() is really early for me to make those decisions (the Required property won't have been loaded from ViewState for example).
Ideas?
The CreateChildControls is basically for the controls that have childs. RequiredFieldValidator is like a sibling to TextBox.
Here is the code that works for me:
public class RequiredTextBox : TextBox
{
private RequiredFieldValidator _req;
private string _errorMessage;
public string ErrorMessage
{
get { return _errorMessage; }
set { _errorMessage = value; }
}
protected override void OnInit(EventArgs e)
{
_req = new RequiredFieldValidator();
_req.ControlToValidate = this.ID;
_req.ErrorMessage = _errorMessage;
Controls.Add(_req);
base.OnInit(e);
}
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
base.Render(writer);
_req.RenderControl(writer);
}
}
And here it the ASP.NET page behind:
protected void SubmitClick(object sender, EventArgs e)
{
if(Page.IsValid)
{
// do something
}
}
And here is the ASPX code:
<MyControl:RequiredTextBox runat="server" ErrorMessage="Name is required!" ID="txtName"></MyControl:RequiredTextBox>
<asp:Button ID="Btn_Submit" runat="server" Text="Submit" OnClick="SubmitClick" />
Validators have to inherit from BaseValidator.

Resources