Set Focus to Any Control After PostBack - c#

First I have 3 TextBoxes, a DropDownList, a CheckBox, and 2 LinkButtons in a table. The TextBoxes and DropDownList must do an AutoPostBack because there are some calculations being done on the server side which called by OnTextChanged.
My issue here is that after the content of any Control (which requires a PostBack) changes and you attempt to move focus to any other Control on the page (whether it be tabbing or using the mouse to click on any other Control) the focus does not move to the next Control or the Control you clicked.
I tried using a method which finds the current Control and based on the TabIndex, adds 1 which moves the focus to the next control. The problem here is that it does not allow for focus to be set on any other Control other than that one with the next TabIndex. To better explain here is an example of the situation.
Example: I have TextBox1, TextBox2, and TextBox3. I change the content in TextBox2 then want to move backwards to TextBox1. When I click in TextBox1 the focus is set to TextBox3 since the code is using the order of the TabIndex. ** A key factor here is that TextBox1 DOES get focus before the server side code fires but then loses focus and sets it on TextBox3 **
To me, it seems like the only way this can be accomplished would be to add a function on the client side which finds the Control with focus before the PostBack occurs, then reset the focus on that Control after the server side code completes. It does not seem like anything I do on the server side will work since the focus has already been lost when this occurs.
I have searched and searched but have not been able to find any solution to this particular issue, everything I have found deals with setting focus to the NEXT Control instead of the Control you want to have focus. Any help would be greatly appreciated. Thank you in advance.
Here is the code I am using to move to the next Control based on the TabIndex but this does not work in my situation.
protected void Page_Load(object sender, EventArgs e)
{
WebControl ctrl = GetPostBackControl() as WebControl;
if (ctrl != null && !SetNextFocus(Controls, ctrl.TabIndex + 1)) { ctrl.Focus(); }
}
public Control GetPostBackControl()
{
Control control = null;
string ctrlname = Request.Params.Get("__EVENTTARGET");
if (ctrlname != null && ctrlname != string.Empty)
{
control = FindControl(ctrlname);
control.Focus();
}
else
{
foreach (string ctl in Request.Form)
{
Control c = FindControl(ctl);
if (c is Button)
{
control = c;
break;
}
}
}
return control;
}
private bool SetNextFocus(ControlCollection controls, int tabIndex)
{
foreach (Control control in controls)
{
if (control.HasControls())
{
bool found = SetNextFocus(control.Controls, tabIndex);
if (found) { return true; }
}
WebControl webControl = control as WebControl;
if (webControl == null) { continue; }
if (webControl.TabIndex != tabIndex) { continue; }
webControl.Focus();
return true;
}
return false;
}

Try doing something like this to retain focus after postback:
/// <summary>
/// Overrides the OnLoad event moves the cursor to the appropriate control.
/// </summary>
/// <param name="e"></param>
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
int currentTabIndex = 1;
WebControl postBackCtrl = (WebControl)GetControlThatCausedPostBack(Page);
foreach (WebControl ctrl in Panel1.Controls.OfType<WebControl>())
{
ctrl.TabIndex = (short)currentTabIndex;
if (postBackCtrl != null)
{
if (ctrl.TabIndex == postBackCtrl.TabIndex + 1)
ctrl.Focus();
}
currentTabIndex++;
}
}
/// <summary>
/// Retrieves the control that caused the postback.
/// </summary>
/// <param name="page"></param>
/// <returns></returns>
private Control GetControlThatCausedPostBack(Page page)
{
//initialize a control and set it to null
Control ctrl = null;
//get the event target name and find the control
string ctrlName = Page.Request.Params.Get("__EVENTTARGET");
if (!String.IsNullOrEmpty(ctrlName))
ctrl = page.FindControl(ctrlName);
//return the control to the calling method
return ctrl;
}

Thank you for the help, it did get me a little closer. The only thing that was preventing it from working correctly is that my page has some variables which are being carried over from other pages. When the page loads, the override is causing the page to break since it is unable to locate those controls. I was able to fix the issue by adding a HiddenField on the .aspx page and adding the following code to the onload() function:
if (document.getElementById("HiddenField1").value != "") {
var contr = document.getElementById(document.getElementById"HiddenField1").value);
contr.focus();
document.getElementById("HiddenField1").value = "";
}
as well as adding a new function which is called on each of the TextBoxes:
function tabFocus(e) {
document.getElementById("HiddenField1").value = e.id;
}
I ended up not having to make any changes in the code behind. Once again, thank you for the help!

Related

How to find first editable control to focus on in a page

My current function only finds first Textbox which is not totally correct. Control class doesn't have IsEditable property.
private static Control FindFocusableControl(Control CurrentControl)
{
if (CurrentControl.Visible)
{
if (CurrentControl is TextBox)
{
return CurrentControl;
}
if (CurrentControl.HasControls())
{
foreach (Control CurrentChildControl in CurrentControl.Controls)
{
Control focusableControl = FindFocusableControl(CurrentChildControl);
if (focusableControl != null)
{
return focusableControl;
}
}
}
}
return null;
}
On the first recursion for the first child control, the child control is probably not visible so the routine exits.
if (CurrentControl.Visible)
You might need a different kind of check at this point, perhaps check for textbox first, then if visible.

WinForms & Infragistics controls readonly property does not work for clicked control

I wrote this code to make all controls readonly.
makeReadOnly(Control control, bool bIsReadOnly)
{
for(int i=0; i< control.Controls.Count; i++)
{
if(control.Controls[i].Controls.Count > 0)
{
makeReadOnly(control.Controls[i], bIsReadOnly);
}
else if(control.Controls[i].GetType() == typeof(UltraTextEditor))
{
(control.Controls[i] as UltraTextEditor).ReadOnly = bIsReadOnly;
}
else if(control.Controls[i].GetType() == typeof(UltraNumericEditor))
{
(control.Controls[i] as UltraNumericEditor).ReadOnly = bIsReadOnly;
}
else if(control.Controls[i].GetType() == typeof(ListBox))
{
(control.Controls[i] as ListBox).Enable = !bIsReadOnly;
}
}
}
It works but if user clicks one control such as a textbox, then click edit button(call makeReadOnly function). Function does not work for the clicked textbox. (It works for the other controls only).
Private void EditButton_Click()
{
foreach(Controls control in this.controls)
{
makeReadOnly(control, false);
}
}
"this" represents parent form."EditButton_Click()" is button event.
Why it is not working for the clicked ones?
How can I solve this problem?
I think, I found the answer. The problem is in Infragistics controls. When a user clicks an UltraTextEditor or UltraComboEditor controls, Infragistics changes its type to something like "EmbeddableUIText..." and its parent type becomes UltraTextEditor or UltraComboEditor. So, I have to check this types or controls' parent types to solve it.

How do I find out which button triggered the postback?

In asp.net (c#) how can I find out which asp:button triggered the postback?
I am using it for dynamic controls and want to do a different process on pageload for different buttons. I have tried looking at __EVENTARGUMENTS etc. but they didnt work.
I am looking to do something like this:
Page_load
Case:
Button 1 clicked
//Do something
Button 2 clicked
//Do something
Use the below code.
public static string GetPostBackControlId(this Page page)
{
if (!page.IsPostBack)
return string.Empty;
Control control = null;
// first we will check the "__EVENTTARGET" because if post back made by the controls
// which used "_doPostBack" function also available in Request.Form collection.
string controlName = page.Request.Params["__EVENTTARGET"];
if (!String.IsNullOrEmpty(controlName))
{
control = page.FindControl(controlName);
}
else
{
// if __EVENTTARGET is null, the control is a button type and we need to
// iterate over the form collection to find it
// ReSharper disable TooWideLocalVariableScope
string controlId;
Control foundControl;
// ReSharper restore TooWideLocalVariableScope
foreach (string ctl in page.Request.Form)
{
// handle ImageButton they having an additional "quasi-property"
// in their Id which identifies mouse x and y coordinates
if (ctl.EndsWith(".x") || ctl.EndsWith(".y"))
{
controlId = ctl.Substring(0, ctl.Length - 2);
foundControl = page.FindControl(controlId);
}
else
{
foundControl = page.FindControl(ctl);
}
if (!(foundControl is Button || foundControl is ImageButton)) continue;
control = foundControl;
break;
}
}
return control == null ? String.Empty : control.ID;
}
Calling this function:
I have included the above function in a static class UtilityClass.
String postBackControlId = UtilityClass.GetPostBackControlId(this);
The code has been referenced from Mahesh's Blog.

Change value of all controls of a specific type without causing indefinite loop

I have a situation where I have several (let's say 10 for now) DevExpress LookUpEdit controls (sort of like DropDown Controls for Windows) on several tabs on my form. All 10 of these controls are the same in that they use the same data source to get their information from. What I'm trying to do is if a value changes in ANY of the 10 controls, then I want it to change in the other 9 controls as well. Because I have other controls of this type on my form that I do not want to be changed, I simply added a string value to the Tag property and have a method search for all controls of this type with the Tag property set to a specific string.
I initially thought I might be able to create a generic method that would change the text for all other controls and assign it to the TextChanged event of each control but I soon found out that they cancel each other out once I assign the value of one control to another (because in essence, once I change the value of one control, it then calls the same method and tries to change the rest).
Sorry if it's confusing, but here's some code as to what I'm trying to do...for now, let's say I have just 2 controls...lookupWeight and lookupBicycleWeight. On the TextChanged event for each, I have this:
private void OnLookupWeight_TextChanged(object sender, EventArgs e)
{
OnLookupWeight_TextChanged<LookUpEdit>(sender, e);
}
which calls this:
private void OnLookupWeight_TextChanged<T>(object sender, EventArgs e)
{
var controls = GetAll(tabPageSpecifications, typeof(T));
foreach (var control in controls)
{
if (control.Tag != null)
if (control.Tag.ToString() == "Weight")
if(control.Name != (sender as LookUpEdit).Name)
(control as LookUpEdit).EditValue = (sender as LookUpEdit).Text;
}
}
GetAll is a simple method that returns all controls for a given Control, including sub-controls:
/// <summary>
/// method to get all child & sub-child controls within a control by type
/// </summary>
/// <param name="control">the control we're searching in (use this for the form)</param>
/// <param name="type">The control type we're looking for (i.e; TextBox)</param>
/// <returns></returns>
public IEnumerable<Control> GetAll(Control control, Type type = null)
{
var controls = control.Controls.Cast<Control>();
//check the all value, if true then get all the controls
//otherwise get the controls of the specified type
if (type == null)
return controls.SelectMany(ctrl => GetAll(ctrl, type)).Concat(controls);
else
return controls.SelectMany(ctrl => GetAll(ctrl, type)).Concat(controls).Where(c => c.GetType() == type);
}
I know my OnLookupWeight_TextChanged method isn't entirely generic because I cast to the type LookupEdit but I am just trying to get this to work at this point before going back and changing things.
As you can see, the line if(control.Name != (sender as LookUpEdit).Name) is where the OnLookupWeight_TextChanged gets fired again and basically cancels itself out.
Any help or diretion as to how to accomplish this would be great.
Just change the ones that don't already have the same Text value:
foreach (var control in controls)
{
if (control.Tag != null)
if (control.Tag.ToString() == "Weight")
if((control as LookUpEdit).EditValue != (sender as LookUpEdit).Text)
(control as LookUpEdit).EditValue = (sender as LookUpEdit).Text;
}
It sounds like the code in OnLookupWeight_TextChanged shouldn't be run when the text of the control is changed from within that routine. You can use an instance variable to disable running it except when the change comes from outside. For example:
private Boolean InProgress;
private void OnLookupWeight_TextChanged<T>(object sender, EventArgs e)
{
if (!InProgress)
{
InProgress=true;
var controls = GetAll(tabPageSpecifications, typeof(T));
foreach (var control in controls)
{
if (control.Tag != null)
if (control.Tag.ToString() == "Weight")
if(control.Name != (sender as LookUpEdit).Name)
(control as LookUpEdit).EditValue = (sender as LookUpEdit).Text;
}
InProgress = false;
}
}
Why don't you use the same bindingsource for all the 10 controls?
When you change the selected value in one of them all would change.

On postback, how can I check which control cause postback in Page_Init event

On postback, how can I check which control cause postback in Page_Init event.
protected void Page_Init(object sender, EventArgs e)
{
//need to check here which control cause postback?
}
Thanks
I see that there is already some great advice and methods suggest for how to get the post back control. However I found another web page (Mahesh blog) with a method to retrieve post back control ID.
I will post it here with a little modification, including making it an extension class. Hopefully it is more useful in that way.
/// <summary>
/// Gets the ID of the post back control.
///
/// See: http://geekswithblogs.net/mahesh/archive/2006/06/27/83264.aspx
/// </summary>
/// <param name = "page">The page.</param>
/// <returns></returns>
public static string GetPostBackControlId(this Page page)
{
if (!page.IsPostBack)
return string.Empty;
Control control = null;
// first we will check the "__EVENTTARGET" because if post back made by the controls
// which used "_doPostBack" function also available in Request.Form collection.
string controlName = page.Request.Params["__EVENTTARGET"];
if (!String.IsNullOrEmpty(controlName))
{
control = page.FindControl(controlName);
}
else
{
// if __EVENTTARGET is null, the control is a button type and we need to
// iterate over the form collection to find it
// ReSharper disable TooWideLocalVariableScope
string controlId;
Control foundControl;
// ReSharper restore TooWideLocalVariableScope
foreach (string ctl in page.Request.Form)
{
// handle ImageButton they having an additional "quasi-property"
// in their Id which identifies mouse x and y coordinates
if (ctl.EndsWith(".x") || ctl.EndsWith(".y"))
{
controlId = ctl.Substring(0, ctl.Length - 2);
foundControl = page.FindControl(controlId);
}
else
{
foundControl = page.FindControl(ctl);
}
if (!(foundControl is IButtonControl)) continue;
control = foundControl;
break;
}
}
return control == null ? String.Empty : control.ID;
}
Update (2016-07-22): Type check for Button and ImageButton changed to look for IButtonControl to allow postbacks from third party controls to be recognized.
Here's some code that might do the trick for you (taken from Ryan Farley's blog)
public static Control GetPostBackControl(Page page)
{
Control control = null;
string ctrlname = page.Request.Params.Get("__EVENTTARGET");
if (ctrlname != null && ctrlname != string.Empty)
{
control = page.FindControl(ctrlname);
}
else
{
foreach (string ctl in page.Request.Form)
{
Control c = page.FindControl(ctl);
if (c is System.Web.UI.WebControls.Button)
{
control = c;
break;
}
}
}
return control;
}
If you need to check which control caused the postback, then you could just directly compare ["__EVENTTARGET"] to the control you are interested in:
if (specialControl.UniqueID == Page.Request.Params["__EVENTTARGET"])
{
/*do special stuff*/
}
This assumes you're just going to be comparing the result from any GetPostBackControl(...) extension method anyway. It may not handle EVERY situation, but if it works it is simpler. Plus, you won't scour the page looking for a control you didn't care about to begin with.
Either directly in form parameters or
string controlName = this.Request.Params.Get("__EVENTTARGET");
Edit: To check if a control caused a postback (manually):
// input Image with name="imageName"
if (this.Request["imageName"+".x"] != null) ...;//caused postBack
// Other input with name="name"
if (this.Request["name"] != null) ...;//caused postBack
You could also iterate through all the controls and check if one of them caused a postBack using the above code.
if (Request.Params["__EVENTTARGET"] != null)
{
if (Request.Params["__EVENTTARGET"].ToString().Contains("myControlID"))
{
DoWhateverYouWant();
}
}
Assuming it's a server control, you can use Request["ButtonName"]
To see if a specific button was clicked: if (Request["ButtonName"] != null)
An addition to previous answers, to use Request.Params["__EVENTTARGET"] you have to set the option:
buttonName.UseSubmitBehavior = false;
To get exact name of control, use:
string controlName = Page.FindControl(Page.Request.Params["__EVENTTARGET"]).ID;

Categories

Resources