ASP.NET CheckBox does not fire CheckedChanged event when unchecking - asp.net

I have a CheckBox on an ASP.NET Content Form like so:
<asp:CheckBox runat="server" ID="chkTest" AutoPostBack="true" OnCheckedChanged="chkTest_CheckedChanged" />
In my code behind I have the following method:
protected void chkTest_CheckedChanged(object sender, EventArgs e)
{
}
When I load the page in the browser and click the CheckBox it becomes checked, the page posts back, and I can see chkTest_CheckedChanged being called.
When I then click the CheckBox again it becomes unchecked, the page posts back, however chkTest_CheckedChanged is not called.
The process is repeatable, so once the CheckBox is unchecked, checking it will fire the event.
I have View State disabled in the Web.Config, enabling View State causes this issue to disappear. What can I do to have reliable event firing while the View State remains disabled?
Update:
If I set Checked="true" on the server tag the situation becomes reversed with the event firing when un-checking the CheckBox, but not the other way around.
Update 2:
I've overridden OnLoadComplete in my page and from within there I can confirm that Request.Form["__EVENTTARGET"] is set correctly to the ID of my CheckBox.

To fire CheckedChanged event set the following properties for CheckBox, AutoPostBack property should be true and should have a default value either checked false or true.
AutoPostBack="true" Checked="false"

Implementing a custom CheckBox that stores the Checked property in ControlState rather than ViewState will probably solve that problem, even if the check box has AutoPostBack=false
Unlike ViewState, ControlState cannot be disabled and can be used to store data that is essential to the control's behavior.
I don't have a visual studio environnement right now to test, but that should looks like this:
public class MyCheckBox : CheckBox
{
private bool _checked;
public override bool Checked { get { return _checked; } set { _checked = value; } }
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
//You must tell the page that you use ControlState.
Page.RegisterRequiresControlState(this);
}
protected override object SaveControlState()
{
//You save the base's control state, and add your property.
object obj = base.SaveControlState();
return new Pair (obj, _checked);
}
protected override void LoadControlState(object state)
{
if (state != null)
{
//Take the property back.
Pair p = state as Pair;
if (p != null)
{
base.LoadControlState(p.First);
_checked = (bool)p.Second;
}
else
{
base.LoadControlState(state);
}
}
}
}
more info here.

It doesn't fire because with viewstate disabled the server code does not know that the checkbox was previously checked, therefore it doesn't know the state changed. As far as asp.net knows the checkbox control was unchecked before the postback and is still unchecked. This also explains the reverse behavior you see when setting Checked="true".

I'm not sure but I guess that my solution is working only for .NET Framework 4.0:
Use ViewStateMode = "Disabled" to disable view state insted of EnableViewState="false". This will caution the same behavior except that you can save a local view state.
So, on your checkbox, set the attribute ViewStateMode = "Enabled" and the problem is solved, without implementing a custom checkbox.

It's an old post but I had to share my simple solution in order to help others who searched for this problem.
The solution is simple: Turn on AutoPostBack.
<asp:CheckBox id="checkbox1" runat="server"
AutoPostBack="True" //<<<<------
Text="checkbox"
OnCheckedChanged="knowJobCBOX_CheckedChanged"/>

I wanted to tidy things up a bit so I've just spent a bit of time testing a solution for this.
joshb is correct with his explanation for why the CheckBox behaves the way it does.
As I don't know how I got round this last year or even if I did (I can't remember what I was working on at the time to check), I've put together a simple solution/workaround.
public class CheckBox2 : CheckBox
{
protected override bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
{
bool isEventTarget = postCollection["__EVENTTARGET"] == UniqueID;
bool hasChanged = base.LoadPostData(postDataKey, postCollection);
hasChanged = hasChanged || isEventTarget;
return hasChanged;
}
}
If you now register CheckBox2 in your page and use it in place of your standard CheckBoxes, you will get the CheckedChanged event fired as you expect with ViewState disabled and AutoPostBack enabled.
The way this works is allowing the normal CheckBox to do its thing with validation and change checking, but then performs an additional check to see if it was the target of the event that caused the postback. If it was the target, it returns true to tell the framework to raise the CheckedChanged event.
Edit:
Please note that this only solves the problem for AutoPostBack on the CheckBox. If the PostBack is invoked from anything else (a button, for example), the CheckedChanged event still exhibits the observed problem.

I had the same problem. I have spent a lot of time on it and finally have solved it.
In my case the Checkbox was disabled by default:
<asp:CheckBox ID="chkActive" runat="server" Enabled="false"/>
It turns ViewState isn't loaded for disabled or invisible controls.
So remove Enabled="false" or Visible="false" and it will work as expeceted. And of course ViewState shouldn't be disabled.

Additionally: Check for any errors in the JavaScript console.
I experienced the same exact issue described by OP except that it only happened in Safari (checkbox worked fine in Chrome and Firefox). Upon inspecting the JavaScript console, I found an error that was being thrown by a malformed jQuery selector.
In my case, I had $('a[id*=lbView') which was missing a closing ]. This threw an error in Safari but, surprisingly, not in Chrome nor in Firefox.

The super easy answer is to set the ViewState on for that one control.
Just add the EnableViewState="true" to the AutoPostBack="true" property in the checkbox tag.

The default value of "Checked" Property of CheckBox is false (which means Uncheck). So, when you Check/Uncheck the CheckBox it compares with Checked Property.
If it does not match with Checked Property, ASP fires OnCheckedChanged Event.
If it matches with the Checked Property it will not fire the OnCheckedChanged Event.
So, in order to make ASP to fire OnCheckedChanged event when unchecking, it has to know Original value, so that it compares with the value from user and fires the event.
By adding data-originalValue='<%# Eval("ValueFromCodeBehind") %>' property in asp:CheckBox will be able to fire the OnCheckChanged Event as ASP can now able to know its previous value. So that when we change it, ASP fires OnCheckChanged Event even if we do Uncheck.

Related

Getting posted-back TextBox.Text in Page_Load?

I've a asp:TextBox and a submit button on my asp.net page. Once the button was clicked, the TextBos's value is posted back. I'm going to keep the the posted-back text value into session, so that other child controls can access to the value during their Page_Load. However, I always get NOTHING ("") in the Page_Load method, and I can read the text out in the button click handler. I know that the "button click event" happens after the Page_Load. So, I'm asking how can I "pre-fetch" the TextBox.text during Page_Load?
public partial class form_staffinfo : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e){
string s = staff_id.Text; //Reach this line first, but GET empty value. However, I want to keep it in the session during this moment.
}
protected void btn_submit_Click(object sender, EventArgs e) {
string s = staff_id.Text; //Reach this line afterward, value got.
}
}
-- EDITED --
<%# Control Language="C#" AutoEventWireup="true" CodeFile="form_staffinfo.ascx.cs" Inherits="form_staffinfo" %>
<asp:Label ID="Label1" runat="server" Text="Staff ID: "></asp:Label>
<asp:TextBox ID="staff_id" runat="server" ></asp:TextBox>
<asp:Button ID="btn_submit" runat="server" Text="Query" OnClick="btn_submit_Click" />
Since I can't get the TextBox's text in the Page_Load, so, I didn't include any code related to session for clear presentation.
Thank you!
William
None of the values of your server controls are available for consumption in the Page_Load. Those controls are assigned after the form is validated (which is after the form is loaded) and before the form's control's events fire (like button clicks, in your example). The values posted are in the Request.Form Collection. Look in the AllKeys property and you should see a key that ends in $staff_id if you use your example posted. There may be other characters in from of the key, depending upon if the control is nested in a master page or other control.
If you absolutely must have that value at page load, grab it from the Request.Form collection instead of the user control, but I would question the wisdom of capturing the value that early in the page lifecycle. You could conceivably capture the textbox's OnTextChanged Event if you needed to preserve the value in Session.
EDIT - Additional Explanation
if you were going to create a custom event for your user control, there are only a couple of steps to it.
Create a delegate. This is will be the common object for inter-control messaging.
public delegate void StaffIdChangedEvent(object sender, string staffId);
Declare an event using that delegate in the user control that is going to broadcast.
public event StaffIdChangedEvent StaffIdChanged;
In your user control, when you are ready to broadcast (say from the Staff_id textbox's OnTextChanged event), you just invoke the event [Its generally a best practice to check to see if the event is null]
this.StaffIdChangedEvent(this, "staff-id-value-here");
The final step is to wire the user control event up to an event handler (this prevents the null situation I mentioned above when trying to invoke the event). You could wire a handler into the hosting page.
this.form_staffinfo.StaffIdChangedEvent += this.some_method_on_page;
Just make sure the method on the page has the same method signature as the delegate used to declare the event.
Events also could be wired into each control that needs to know about them (look up multicast delegates), so you could do something like:
this.form_staffinfo.StaffIdChangedEvent += this.some_method_on_page;
this.form_staffinfo.StaffIdChangedEvent += this.some_control_on_the_page;
this.form_staffinfo.StaffIdChangedEvent += this.some_other_control_on_the_page;
In any event, I generally preferred to do this type of wiring in the page's OnInit method.
override protected void OnInit(EventArgs e)
{
base.OnInit(e);
InitializeComponent();
}
and just write your own InitializeComponent method to centralize any of this wiring you have to do.
There is something else that is setting the textbox value. Could you please check if you are overriding other event that occurs before Page_Load and modifying the textbox text property. Even, posting the code where you update session variable would be handy. From the code you have posted, it should work.
Do you have autoeventwireup disabled? I could be mistaken, but I think if it is disabled your Page_Load will not fire. If you want to leave it disabled, you can always override the OnLoad event...
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
// do stuff
}

ASP HiddenField ValueChanged handler fires on every postback in custom control

I'm having problems with event handlers always firing on HiddenFields despite no changes to the field that I'm aware of.
I have an ASCX user control where I statically declare some elements:
MyControl.ascx:
<div id="AnItem" runat="server">
<asp:Textbox id="TextBox1" runat="server" />
<asp:HiddenField id="HiddenField1" runat="server" />
</div>
(There's obviously a lot more there, but this is the markup of note)
MyControl.ascx.cs:
protected override void OnInit(EventArgs e){
base.OnInit(e);
if (SelectedValue != null){
TextBox1.Text = SelectedValue.Text;
HiddenField1.Value = SelectedValue.ID.ToString();
}
}
protected override void OnLoad(EventArgs e){
base.OnLoad(e);
if (String.IsNullOrEmpty(TextBox1.Text))
TextBox1.Attributes["style"] = "display:none";
TextBox1.TextChanged += ItemTouched;
HiddenField1.ValueChanged += ItemTouched;
}
protected void ItemTouched(object sender, EventArgs e){
// process changed values
}
The code seems unconventional because I've omitted items unrelated (I assume) to my question.
My control is actually created dynamically using a wrapper class that I can serialize:
[Serializable]
public class ListControl{
public void GenerateControl(TemplateControl parent){
var control = parent.LoadControl("~/MyControl.ascx") as MyControl;
control.Options = _options;
control.SelectedValue = _selectedValue;
return control;
}
private IList<SelectableOption> _options;
private SelectionOption _selectedValue;
}
(The reasons for this wrapper are due to a large legacy code base that is too slow in creating the list of selectable values. The _options list is generated once and then kept in the session to speed up postback execution.)
ItemTouched is attached to every element that may be touched by the user (or manipulated by JavaScript). My problem is that it's being fired on every postback - even when HiddenField1 wasn't modified on the client side (I've confirmed this by removing all JavaScript that touched it).
I'm at a loss as to why the handler is being fired is the value isn't being touched. It does not fire when the control values aren't set (e.g. in my OnInit method), but always does if they are "pre-set". I don't expect the change handlers to fire if I attach the event handlers after setting the default values, but this doesn't seem to be the case. Am I making a fundamentally bad assumptions about ASP.NET events?
It's been a while, but if I recall correctly HiddenField or Textbox controls fire ValueChanged based on comparing their current value with the value stored in ViewState. Since you're dynamically creating these controls, I'm guessing their viewstate isn't getting rehydrated properly (or early enough) - which then triggers the ValueChanged event handler to fire (since they have a value that differs from that stored in ViewState).
Your best shot at really understanding what's going on is to enable debugging on the .NET Framework: http://msdn.microsoft.com/en-us/library/cc667410.aspx and set some breakpoints around the area where ValueChanged is fired. Then you can see what condition is causing it, and that should hopefully help you figure out how to work around it.

Set Web User Control Property to GridView Selected Row

I have a web user control (ascx) that exposes an "ID" property. What I want to do is set this property when the SelectedIndexChanged event fires in a gridview in the containing page. However, I can't seem to do it.... Here's my code:
protected void grdPhysicians_SelectedIndexChanged(object sender, EventArgs e)
{
physicians_certif1.mdID = grdPhysicians.SelectedDataKey.ToString();
mvData.SetActiveView(viewEdit);
panAdditional.Visible = true;
}
Physicians_certif1 is the user control. It seems the user control is loading before the SelectedIndexChanged event has a chance to set it's property.
Any ideas folks?
ASP.Net page lifecycles can be hard to understand especially with ascx user controls which also have their own lifecycle. If you are setting the mdID property in Page_Load of either the page or the ASCX control or have hardcoded a default value into it in the XHTML, it is probably being reset after SelectedIndexChanged fires.
Set a breakpoint in grdPhysicians_SelectedIndexChanged, set a watch on physicians_certif1.mdID and step through the code using the debugger.
Yes, that is exactly what is happening. You should look at (and be familiar with) the following resource:
ASP.Net Page Life Cycle
The page will load, then the control will load, then your events will begin to fire. If you have configuration needs based on event triggers, it is best either to place those configurations in the Page_LoadComplete or Page_PreRender events of the user control in question or apply "Rebinding" instructions in the Set method of your property:
public MyValue MyProperty()
{
get
{
return _myProperty;
}
set
{
RebindMyControls();
_myProperty = value;
}
}

ASP:Checkbox how to autopostback only on checked?

I've got a checkbox that's set up as below:
<asp:CheckBox ID="myCheckbox" runat="Server" OnClick="showLoadingScreen(this.checked);" AutoPostBack="true" Text="Check me for more data!" />
The function showLoadingScreen is as below:
function showLoadingScreen(isChecked) {
if (isChecked)
{
document.getElementById('form1').style.display='none';
document.getElementById('img_loading').style.display='block';
}
else { return false; }
}
I've added the else clause in hopes that I can get it to only post back when the checkbox is checked, but it's posting back in either case.
I've got a grid on the page (inside form1) that has a set of data loaded into it on page load, but in order to add some extra data to it I've added this checkbox (its a longer running process, so I only want to load it on demand, not upfront). When it's checked I want to show the loading gif, postback, grab the data, and return. If the box gets unchecked I don't want to do anything, since leaving more than enough data on the page is perfectly fine (that is to say, the data displayed upfront is a subset of the data displayed when the checkbox is checked).
Is there any way to make it so the checkbox auto posts back on checked, but not on unchecked?
Edit: Using Dark Falcon's suggestion, I've modified the checkbox to look like:
<asp:CheckBox ID="myCheckbox" runat="Server" OnClick="return showLoadingScreen(this.checked);" AutoPostBack="true" Text="Include HQ Values" />
And the javascript to be:
function showLoadingScreen(checked) {
alert(checked);
if (checked)
{
document.getElementById('form1').style.display='none';
document.getElementById('img_loading').style.display='block';
document.form1.submit(); //my own addition, to get it to post back
}
else { return false; }
}
Now, it posts back on checked, but the box is not able to be unchecked anymore. As you can see I've added an alert to show the value being passed in. It's passing in the correct value when you uncheck the box (false), but then it somehow gets checked again.
It's not a huge issue, since there's really no reason to ever uncheck the box (since as I stated before, the dataset when checked is a superset of the unchecked dataset), but I'd still like to know why it's doing that. Any ideas?
Do not set AutoPostBack in this case. "AutoPostBack" means post back to the server any time the value of this control changes... which is NOT what you want.
Instead, use GetPostBackEventReference(myCheckbox,"") to get the appropriate postback script and call this from your showLoadingScreen method if the checkbox is checked.
For your onclick handler, you need to do:
return showLoadingScreen(this.checked);
Try to avoid using _doPostback as it is a hack which you will have to know what control ID is posting back and other parameters for that Javascript function from Microsoft ASP.NET. To understand what's happening behind the scene, you have to know why there is a postback and how to prevent the postback from happening.
Here's what's happening with an ASP.NET checkbox (ASP:Checkbox) when auto-postback is set:
<ASP:Checkbox runat="server" id="chkCheckbox" AutoPostback="true" onclick="return isDoPostback(this.checked);" ClientIdMode="static" ... />
generated HTML code is:
<input type="checkbox" ... id="..." onclick="return isDoPostback(this.checked);_doPostback(...);" .../>
The custom onclick event is appended to the beginning of the onclick event of the checkbox. No matter what you do, that prepended function call will execute. Worst off, if you have a return value, the _doPostback will never get executed.
This is what you really want to do (I use a mix of jQuery and native Javascript here):
var checkbox = $("#chkCheckbox");
...
checkbox .on("change", function(e)
{ if(this.checked)
{
var isConfirmedToContinue = confirm("Continue with Postback?");
if(!isConfirmedToContinue)
{ this.checked = false; //Uncheck the checkbox since the user canceled out
var onClickDelegate = this.onclick;
if(onClickDelegate)
{ var me = this;
this.removeEventListener("click", onClickDelegate); //Remove the onclick event so that auto-postback no longer happens
setTimeout(function()
{ //Add back the onclick delegate after 250ms
me.addEventListener("click", onClickDelegate);
}, 250);
this.onclick = null; //Remove the current onclick event by nulling it out
}
}
}
});
Try using a JS routine for checking whether it is checked, and if it is set to true, try doing:
_doPostBack(checkElementReference.name, "");
_doPostBack is responsible for performing posts to the server for controls that don't normally postback. You have to pass the name of the element, which on the server happens to be the UniqueID property for the server-side checkbox control.

Help with DataGrid/CheckBoxes/double submit

We have a simple datagrid. Each row has a checkbox. The checkbox is set to autopostback, and the code-behind has an event handler for the checkbox check-changed event. This all works as expected, nothing complicated.
However, we want to disable the checkboxes as soon as one is checked to prevent a double submit i.e. check box checked, all checkboxes are disabled via client side javascript, form submitted.
To achieve this I we are injecting some code into the onclick event as follows (note that the alert is just for testing!):
Protected Sub DgAccounts_ItemCreated(ByVal sender As System.Object, ByVal e As System.Web.UI.WebControls.DataGridItemEventArgs) Handles DgAccounts.ItemCreated
If e.Item.ItemType = ListItemType.Item Or e.Item.ItemType = ListItemType.AlternatingItem Then
Dim chk As CheckBox = CType(e.Item.FindControl("chkItemChecked"), CheckBox)
chk.Attributes.Add("onclick", "alert('fired ...');DisableAllDataGridCheckBoxes();")
End If
End Sub
When inspecting the source of the rendered page we get the following:
<input id="DgAccounts__ctl2_chkItemChecked" type="checkbox" name="DgAccounts:_ctl2:chkItemChecked" onclick="alert('fired ...');DisableAllDataGridCheckBoxes();setTimeout('__doPostBack(\'DgAccounts$_ctl2$chkItemChecked\',\'\')', 0)" language="javascript" />
It all appears in order, however the server side event does not fire – I believe this is due to the checkbox being disabled, as if we just leave the alert in and remove the call to disable the checkbox it all works fine.
Can I force the check-changed event to fire even though the check box is disabled?
I haven't tested this myself but when you call your 'DisableAllDataGridCheckBoxes' method, do you need to disable the checkbox that has been checked?
If not, you could pass the calling checkbox's id and then only disable all the other checkboxes.
chk.Attributes.Add("onclick", "alert('fired ...');DisableAllDataGridCheckBoxes(" + chk.ClientId + ");")
I would think this would then allow the server side code to fire.
The reason the server side event is not firing is that disabled checkboxes do not get submited with the rest of the form.
The workaround I have used is to set the onclick event of the checkboxes to null rather than disable them - while this gives no visual clue to the user that subsequent clicks are ignored, it does prevent the double submit, and the check boxes are set top the correct state when the response is rendered.
Instead of disabling the checkboxes (which then do not get submitted), just "prevent" them from being selected. Do this by changing their onclick handler to reset the checked state. You coul dset all of the other checkboxes to disabled, and do this just for the one that is processing. Something like:
function stopChanges(cbCurrent) {
$('INPUT[type=checkbox]').disable(); // disable all checkboxes
$(cbCurrent)
.disable(false) // enable this checkbox, so it's part of form submit
.click(function() {
// reset to previous checked - effectively preventing change
$(this).checked = !$(this).checked;
}
);
}
A possible workaround, would be to do all this in the server side code of the RowCommand of the data grid.
(posting the sample in C# but won't be much different in VB I hope)
protected void myGrid_RowCommand(object sender, GridViewCommandEventArgs e)
{
if(((CheckBox)e.Item.FindControl("chkItemChecked")).Checked)
{
foreach(GridViewRow dvRow in myGrid.Rows)
((CheckBox)dvRow.FindControl("chkItemChecked")).Enabled = false;
}
}
or you could use
((CheckBox)e.Item.FindControl("chkItemChecked")).Enabled = false;
if you only want to disable the specific item.
However if you only want to implement this in client side code, it won't help you.

Resources