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.
Related
This question already has answers here:
Method to reset members inside groupBox
(3 answers)
Closed 6 years ago.
I wanna clean my groupbox items after i click an button.
I tried some code blocks, but they don´t work for reset controls.
I don´t wanna remove or delete it, I just wanna reset the items in the groupbox.
This is working for remove groupbox items.
public void ClearPanels(GroupBox control)
{
control.Controls.Clear();
}
or this
groupBox2.Controls.Clear();
It looks like this, before click.
And when I click the button, as you can see on the right side.
It is removed, but I want to reset it.
Any ideas how I can do this ?
I'm going to asume that clear means to leave it all by default. You need to llop all the controls inside the groupbox and depending on what control they are do something or something else.
foreach (Control ctr in GB.Controls)
{
if (ctr is TextBox)
{
ctr.Text = "";
}
else if (ctr is CheckedListBox)
{
CheckedListBox clb = (CheckedListBox)ctr;
foreach (int checkedItemIndex in clb.CheckedIndices)
{
clb.SetItemChecked(checkedItemIndex, false);
}
}
else if (ctr is CheckBox)
{
((CheckBox)ctr).Checked = false;
}
else if (ctr is ComboBox)
{
((ComboBox)ctr).SelectedIndex = 0;
}
}
I dont know what Deneyim and Not are, but i guess you get the idea of checking what it is and asigning the value you want
I think the term clear depends a little on the kind of controls you use. While for a TextBox you probably want to empty the Text property, for a ListBox you probably want to remove all items. So there is no common Clear() method for controls.
What you can do is something like that:
public void ClearPanels(GroupBox control)
{
foreach(Control childControl in control.Controls)
childControl.ResetText();
}
This iterates through all child Controls in your GroupBox and resets the Text property of them. You may wish to add special treatment for certain types of Control.
If your GroupBox contains nested controls (such as another GroupBox with further controls on it), you may need to make this method recursive:
public void ClearPanels(Control control)
{
foreach(Control childControl in control.Controls)
{
childControl.ResetText();
ClearPanels(childControl); // recursive call
}
}
To really "clear" your controls, you have to check their specific type. So a little advanced method could be this:
public void ClearPanels(GroupBox control)
{
foreach(ListBox listBox in control.Controls.OfType<ListBox>())
{
listBox.Items.Clear();
// do more ListBox cleanup
}
foreach(CheckedListBox listBox in control.Controls.OfType<CheckedListBox>())
{
listBox.Items.Clear();
// do more CheckedListBox cleanup
}
foreach(ListView listView in control.Controls.OfType<ListView>())
{
listView.Items.Clear();
// do more ListView cleanup
}
foreach(CheckBox checkBox in control.Controls.OfType<CheckBox>())
{
checkBox.Checked = false;
// do more CheckBox cleanup
}
// etc...
}
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 can we change the ReadOnly property of all textBoxes in a winform that is true to false i'm trying using this code but this prompt me object null reference error...
private void TextBoxesReadOnlyTrue(Control.ControlCollection cc)
{
foreach (Control ctrl in cc)
{
TextBox tb = ctrl as TextBox;
if (tb.ReadOnly)
{
tb.ReadOnly = false;
}
}
}
That's because not all the controls in cc are TextBoxes. So when you try converting them to a TextBox, the variable is null. When a variable is null, you cannot access any properties on that variable, or you'll get an error. So anytime a variable can be null, you MUST first test whether it is null.
Here's the modified if command that you'll want to use to fix your problem:
if (tb != null && tb.ReadOnly) { tb.ReadOnly = false; }
So i appologize that i overlooked that your TextBoxes can be contained in other container controls. Yes, that means you need to do 1 of 2 things: 1: You can move the TextBoxes outside the GroupBox. haha. I'm just joking. Yes, that can solve that problem but then you have worse problems. The correct way is to recursively call your method for every control that has controls in its Controls property. Every control has this property but it seems it is empty (but not null) in controls that are not containers. (I just learned today that every control has this Controls property, so i've updated my code to reflect this.)
So for this real solution, i suggest something similar to this:
private void TextBoxesReadOnlyTrue(Control.ControlCollection cc)
{
foreach (Control ctrl in cc)
{
TextBox tb = ctrl as TextBox;
if (tb != null && tb.ReadOnly)
{ tb.ReadOnly = false; continue; }
if (ctrl.Controls != null && ctrl.Controls.Count > 0)
{ TextBoxesReadOnlyTrue(ctrl.Controls); }
// this recursively calls this same method for every control ...
// that is a container control that contains more controls, ...
// such as GroupBoxes, Panels, etc.
}
}
first you would like to use a function like this:
Recursive get controls
then you do the following
private IEnumerable<T> GetControls<T>(Control.ControlCollection ctrls)
{
foreach (object ctrl in ctrls)
{
foreach (var item in GetControls<T>(((Control)ctrl).Controls))
{
yield return item;
}
if (ctrl is T)
yield return (T)ctrl;
}
}
foreach(var txtbox in GetControls<TextBox>(form.Controls)
{
txtbox.ReadOnly = false;
}
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!
This is the IF -Else ladder which I have created to focus first visible control on my form.According to the requirement any control can be hidden on the form.So i had to find first visible control and focus it.
if (ddlTranscriptionMethod.Visible)
{
ddlTranscriptionMethod.Focus();
}
else if (ddlSpeechRecognition.Visible)
{
ddlSpeechRecognition.Focus();
}
else if (!SliderControl1.SliderDisable)
{
SliderControl1.Focus();
}
else if (ddlESignature.Visible)
{
ddlESignature.Focus();
}
else
{
if (tblDistributionMethods.Visible)
{
if (chkViaFax.Visible)
{
chkViaFax.Focus();
}
else if (chkViaInterface.Visible)
{
chkViaInterface.Focus();
}
else if (chkViaPrint.Visible)
{
chkViaPrint.Focus();
}
else
{
chkViaSelfService.Focus();
}
}
}
Is there any other way of doing this. I thought using LINQ will hog the performance as i have to tranverse the whole page collection. I am deep on page which has masterpages.Please suggest.
I think your tree is good. This certainly looks like a logic tree that can be simplified, and you have a good sense of smell to be suspicious of it. However, it seems to be that the logic tree reflects what you need. The logic really is this convoluted, and this is the conditional framework that C# gives you to handle this situation. I don't think it can be improved.
If you had a simple list of controls that should have the focus, and you wanted to give focus to the first visible control in the list, you could do this:
(From c in ListOfControls
Where c.visible = true
Select c).First.Focus();
But, it appears you have some additional criteria, so that wouldn't work.
Two approaches:
Iterate controls and set focus if visible
Use TabIndex and set focus to first. Then focus should fall to first visible control
If you're just trying to focus the first visible control on the form, then I would replace the entire ladder with a single loop:
foreach (Control c in Controls)
{
if (c.Visible)
{
c.Focus();
break;
}
}
If you need to focus an inner control, use a recursive method:
bool FocusFirst(ControlCollection controls)
{
foreach (Control c in controls)
{
if (c.Visible)
{
c.Focus();
FocusFirst(c.Controls);
break;
}
}
}
You could return after you meet your criteria, for example:
if (ddlTranscriptionMethod.Visible)
{
ddlTranscriptionMethod.Focus();
return;
}
if (ddlSpeechRecognition.Visible)
{
ddlSpeechRecognition.Focus();
return;
}
etc..
You can iterate controls and set focus if visible. But I would suggest you to use state pattern for better code readability.
All your doing is setting the focus which is client-side functionality. I would personally do this in javascript (using jQuery). ASP.NET controls that aren't set to visible aren't rendered in the HTML, so you could look for the existence of those elements or there might be an easier way if you're just looking for the first visible, enabled control.
What about a jumpto seems like you could use that here.
When the list of items to evaluate is large (and sometimes it's very large), I try to separate the order of evaluation from the conditional logic, something like this:
List<WebControl> wcBasics = new List<WebControl>();
wcBasics.Add(ddlTranscriptionMethod);
wcBasics.Add(ddlSpeechRecognition);
wcBasics.Add(ddlESignature);
List<CheckBox> checks = new List<CheckBox>();
checks.Add(chkViaFax);
checks.Add(chkViaInterface);
checks.Add(chkViaPrint);
private void Focus()
{
foreach (WebControl c in wcBasics)
if (c.Visible) {
c.Focus();
return;
}
if (!tblDistributionMethods.Visible) return;
foreach (CheckBox chk in checks)
if (chk.Visible) {
chk.Focus();
return;
}
}
chkViaSelfService.Focus();
}
Here is a slightly different take on the problem. First, define an interface to represent controls which may or may not be focused:
public interface IFormControl
{
bool Focus();
}
Then create an implementation which handles the easy cases:
public class FormControl : IFormControl
{
private readonly Control _control;
public FormControl(Control control)
{
_control = control;
}
public bool Focus()
{
if(_control.Visible)
{
_control.Focus();
}
return _control.Visible;
}
}
And create another which handles the more difficult cases:
public class DependentFormControl : IFormControl
{
private readonly Control _control;
private readonly Func<bool> _prerequisite;
public DependentFormControl(Control control, Func<bool> prerequisite)
{
_control = control;
_prerequisite = prerequisite;
}
public bool Focus()
{
var focused = _prerequisite() && _control.Visible;
if(focused)
{
_control.Focus();
}
return focused;
}
}
Then, create an extension method which sets the focus on the first control in a sequence:
public static void FocusFirst(this IEnumerable<IFormControl> formControls)
{
var focused = false;
foreach(var formControl in formControls)
{
if(formControl.Focus())
{
break;
}
}
}
And finally, create the set of controls to be focused:
var controls = new FormControl[]
{
new FormControl(ddlTranscriptionMethod),
new FormControl(ddlSpeechRecognition),
new DependentFormControl(SliderControl1, () => !SliderControl1.SliderDisable),
new FormControl(ddlESignature),
new DependentFormControl(chkViaFax, () => tblDistributionMethods.Visible),
new DependentFormControl(chkViaInterface, () => tblDistributionMethods.Visible),
new DependentFormControl(chkViaPrint, () => tblDistributionMethods.Visible),
new DependentFormControl(chkViaSelfService, () => tblDistributionMethods.Visible)
};
controls.FocusFirst();
You can create the set of controls when your page loads and just call .FocusFirst() whenever necessary.
This is a classic state machine. By setting up a machine it can make the code easier to understand and maintain, though it may add to the total lines.
I won't get into the specifics of your code. By determining what state the user is operating in, you can programmatically change the differing values in an understandable specific state fashion. (Pseudo code below)
enum FormStates
{
Initial_View
Working_View
Edit_View
Shutdown_View
};
{ // Somewhere in code
switch (theCurrentState)
{
case Initial_View :
Control1.Enabled = true;
Control2.Enabled = true;
theCurrentState = Working_View;
break;
case Working_View
if (string.empty(Contro1.Text) == false)
{
Control2.Enabled = false;
Speachcontrol.Focus();
theCurrentState = Edit_view;
}
else // Control 2 is operational
{
Control1.Enabled = false;
SliderControl.Focus();
}
case Edit_View:
...
break;
break;
}
By organizing the code in logical steps, it makes it easier to add more states without jeopardizing an huge if/else.