detect keypress event for all textboxes inside a nested TableLayoutPanel - c#

I have a series of nested TableLayoutPanelcontrols which each of them contains lots of TextBox controls.
I think it is insane to make a keypress event for each of the textboxes, So what I am trying to do is to have a common event method and then apply the event for all textboxes on FormLoad event. What I want to do is to see if the user has pressed Enter key in any of those textboxes.
This is my common method (I hope nothing is wrong with it!):
private void ApplyFiltersOnEnterKey(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)13)
{
tsApplyFilters_Click(this, null);
}
}
And I have the following code in Load event of my form:
//Applying common event for all textboxes in filter options!
foreach (var control in tableCriterias.Controls)
{
var textBox = control as TextBox;
if (textBox != null)
textBox.KeyPress += new KeyPressEventHandler(this.ApplyFiltersOnEnterKey);
}
Well, maybe you can guess already, the codes above does not work! I can list the problems I can think of:
tableCriterias which is the parent TableLayoutPanel and all the other layout panels are inside it, is itself inside a series of Panel SplitContainer and....Do I need to point this in my loop?
Or do I recursively loop over each layoutpanel inside the main layoutpanel?
Or the whole idea is wrong?!!?
Thanks.

private void Recursive(TableLayoutPanel tableCriterias)
{
foreach (var control in tableCriterias.Controls)
{
var textBox = control as TextBox;
if (textBox != null)
textBox.KeyPress += new KeyPressEventHandler(this.ApplyFiltersOnEnterKey);
else if(control is TableLayoutPanel)
Recursive(control as TableLayoutPanel);
}
}
And call this method for parent TableLayoutPanel

Related

How to get the name of the textbox which the user types in, in windows form c#

I have over 160 textboxes, each corresponds to the value inside registers in a chip. I want to write the values typed into the textboxes to the corresponding register each time enter is pressed from that particular textbox. Since there are lot of textboxes keydown event is not possible to every textboxes. What should i do here?. How can i get the name of the textbox the user types the data and then pressed the enter , so that i can take the value from that particular box and copy it.
Surely you can hook into the keypress event in the form constructor.
public Form1()
{
InitializeComponent();
foreach (Control control in this.Controls)
if (control is TextBox)
((TextBox)control).KeyPress += new KeyPressEventHandler(TextBox_Keypress);
}
private void TextBox_Keypress(object sender, KeyPressEventArgs e)
{
var textbox = sender as TextBox;
MessageBox.Show(textbox.Name + "has typed a key");
}
Digging Deeper into Children
If you need to fetch textboxes within children PsychoCoders answer can help you.
public IEnumerable<Control> GetAll(Control control, Type type)
{
var controls = control.Controls.Cast<Control>();
return controls.SelectMany(ctrl => GetAll(ctrl, type))
.Concat(controls)
.Where(c => c.GetType() == type);
}
and change your foreach like so;
foreach (Control control in GetAll(this, typeof(TextBox)))
{
...
}

How call the Text_Changed Event for multiple text box controls on a form in C# winforms

I have form that has about 20 TextBox controls and I would like to fire the Text_Changed event with out adding the event for each individual text box. Is there a way to loop through the text boxes to fire this event? What I am trying to do is clear a label control when the text changes. Instead of displaying a message box, for error descriptions, I use a label control to display the message. I also set it up where if a text box has invalid data, I select all text and give focus to that TextBox so when user re-enters information the label control clears the message.
Edit:
To clear up some confusion, here is some of my code from my validation method
if (txtBentLeadsQty.Text == "")
{
//ValidData = true;
BentLeadsCount = 0;
}
else
{
if (int.TryParse(txtBentLeadsQty.Text.Trim(), out BentLeadsCount))
ValidData = true;
else
{
ValidData = false;
lblError.Text = "Bent Lead Qty must be a numeric value";
txtBentLeadsQty.SelectAll();
txtBentLeadsQty.Focus();
}
}
I already have a way to check for numeric values, and I put code in to select all text entered and gave focus if the values are not numeric, I just want to have a way to clear the Label control when the the text is changes like if the user hit backspace or starts typing that why if the error occurs, I highlight all the text in that TextBox if it is not valid. I can do this if I put code in every text boxes TextChanged event, but to save coding I was wondering if there is way to clear the label control if any of the text boxes throws an error from my validation method instead of adding individual events for 20 text boxes.
Note: Not all text boxes will have data entered, these are quantity text boxes I put code in to assign a 0 to the variable if the TextBox in null.
You can use the following code:
private void Form1_Load(object sender, EventArgs e)
{
foreach (Control ctrl in this.Controls)
{
if ((ctrl as TextBox) != null)
{
(ctrl as TextBox).TextChanged += Form1_TextChanged;
}
}
}
private void Form1_TextChanged(object sender, EventArgs e)
{
MessageBox.Show((sender as TextBox).Name);
}
I'm assuming you want to add the same handler to all textboxes in the form dynamically, i.e. without having to add them for each text box in the visual editor (or code).
If so, this might be what you need:
// Iterate over all controls in the current form:
foreach (var ctl in Controls)
{
// Check if the current control is a textbox
// (will be null if it is of another type)
var txtBox = ctl as TextBox;
if (txtBox != null)
{
txtBox.TextChanged += YourMethod();
}
}
Sounds like you want to programmatically fire the method on each text box.
First, create an array of around 20 text boxes.
var textBoxes = new []{textBox0, textBox1, textBox2};
Loop through the each box and call the text changed method
foreach(var textBox in textBoxes)
{
TextChangedMethod(textBox);
}
If the method you are calling was generated by Visual Studio, it will take a second parameter for EventArgs. You can simply pass a null value for that.
TextChangedMethod(textBox, null);
Create a method, something like this:
public void TextChanged(object sender, EventArgs e)
{
//Text-changed code here.
}
At this point you can click on each text box on your form and add your new method to the event you want to occur. Click a text box on your form, and click the thunderbolt icon in the properties menu and scroll down to "TextChanged" event. Change that drop down on that event to your new method.
OR
Add the event at run time after you initialize the forms components:
textBox1.TextChanged += new EventHandler(TextChanged);
textBox2.TextChanged += new EventHandler(TextChanged);
textBox3.TextChanged += new EventHandler(TextChanged);
This could be done easier if you add all the text boxes to an array, and loop through them with a foreach to add the event to each one. You could also just grab all the text boxes from the form and loop the same way, though I don't know if you have other controls/text boxes that would make you avoid this method.
Use a foreach statement.
Example
List<TextBox> TextblockCollection = null;//You have to add them all individually to the list
foreach (var text in TextblockCollection)
{
//Change the text to the same thing, firing the method
text.Text = text.Text
}
So you want to check when any of the text boxes are changed, then check if the new input in the changed textbox is a number (I'm assuming an integer) and then display a message in a label if it's not a number.
public MainForm()
{
InitializeComponent();
foreach (Control control in this.Controls)
{
if (typeof(control)==typeof(TextBox))
{
(control as TextBox).TextChanged += CommonHandler_TextChanged;
}
}
}
private void CommonHandler_TextChanged(object sender, EventArgs e)
{
int number;
string input=(sender as TextBox).Text;
bool isnumber=false;
isnumber = Int32.TryParse(input, number);
if(isnumber==false)
{
yourLabel.Text = "This textbox contains an incorrect number: "
+(sender as TextBox).Name;
}
else{ /*use the number*/ }
}

c# format textboxes to currency when they lose focus

I have several lists of text boxes on a form each representing a column of a database. I want to update the form each time the user exits one of the boxes for price. the name of this list is priceBox[]. I am aware of the lostFocus event but I cant seem to figure a way to get it to work for a collection and this list can grow so I cant have a fixed number. I dont have any code for this yet. if it helps the text box controls are contained in a panel named panel1.
I have tried searching and cant find anything on this. only for singular instances, like updating 1 text box.
sorry if this is a duplicate but I did try to search. also I am new to c#.
thanks.
One approach is adding a ControlAdded handler to the panel, so every time a new textbox is added, it automatically adds LostFocus handler for it. Step-by-step below:
For your panel you bind a handler ControlAdded event, which would be something like:
private void Panel1_ControlAdded(object sender, ControlEventArgs e)
{
var tb = e.Control as TextBox;
if (tb != null)
{
tb.LostFocus += new EventHandler(TextBox_LostFocus);
}
}
Then in TextBox_LostFocus you can add whatever logic you want
void TextBox_LostFocus(object sender, EventArgs e)
{
var tb = sender as TextBox;
if (tb != null)
{
// modify tb.Text here, possibly like this...
tb.Text = String.Format("{0:C}", Decimal.Parse(tb.Text));
}
}
To update all existing controls (not tested)
foreach (TextBox in panel1.Controls)
{
tb.LostFocus += new EventHandler(TextBox_LostFocus);
}

Select UserControl from FlowLayoutPanel

I have set up UserControls in a FlowPanelLayout with the help in this question:
For Each DataTable Add UserControl to FlowLayoutPanel
I am now trying to implement a click event which allows me to put a border around the UserControl that has been selected. I have done this:
private void User_Load(object sender, EventArgs e)
{
flowlayoutpanelUsers.HorizontalScroll.Visible = false;
// Load and Sort Users DataTable
DataTable datatableUsers = UserMethods.GetUsers().Tables["Users"];
datatableUsers.DefaultView.Sort = "Name";
DataView dataviewUsers = datatableUsers.DefaultView;
// Loop Through Rows and Add UsersGrid to FlowLayoutPael
foreach (DataRowView datarowviewUsers in dataviewUsers)
{
var UsersGrid = new UsersGrid
{
Username = datarowviewUsers["Username"].ToString(),
User = datarowviewUsers["Name"].ToString(),
Admin = datarowviewUsers["Administrator"].ToString(),
};
flowlayoutpanelUsers.Controls.Add(UsersGrid);
UsersGrid.MouseClick += new MouseEventHandler(user_click);
}
}
private UsersGrid selectedUser;
void user_click(object sender, EventArgs e)
{
if (selectedUser != null)
selectedUser.BorderStyle = BorderStyle.None;
selectedUser = (UsersGrid)sender;
selectedUser.BorderStyle = BorderStyle.FixedSingle;
}
My issue is that it only works when I click in a white space in the UserControl but not when the user clicks on the two labels or image. How do I make it work for all child objects too?
Also, how can I then use the selected UserControl to do other things like open a form which shows all the details for that selected user?
I have a few suggestions for you. At the bottom of my response I included code that demonstrates my suggestions.
Suggestion 1: Fixing MouseClick in your UC
When you register the MouseClick event for a UserControl (UC) you're doing so for the UserControl itself, not for any controls that you place on the UserControl such as your Labels, etc. If you click one of these child controls the click won't be 'seen' by the underlying UC.
To fix this register the MouseClick event for all your child controls; you can even register the same MouseClick event handler you have for the UserControl itself.
Suggestion 2: Setting your UC's BorderStyle
I'd move your code for setting the UC's BorderStyle into the UC itself. Create public property IsSelected that's set to true when the UC is selected. In the property's setter update the UC's BorderStyle property depending on the value of the property.
Exposing an IsSelected property for your UC can be handy: you can query a group of these UCs to see which ones are selected rather than trying to track this status outside of the control like through a Form-level variable.
Edit in response to your comment:
Here's an example of how you might query the UCs in a FlowLayoutPanel to see if any are selected and if one is found how you might take some action. In this case the action is to call an EditUser method that takes as parameters values you get from properties in the selected UC:
var selectedUC = flowLayoutPanel.Controls.Cast<UserControl1>().FirstOrDefault(uc => uc.IsSelected);
if (selectedUC != null) {
// Use the properties of the UC found to be selected as parameters is method EditUser.
EditUser(selectedUC.Name, selectedUC.Username, selectedUC.Administrator);
}
Suggestion 3: Managing selection in a group of your UCs
If you want to unselect all UCs in a group except for the one that the user clicks (i.e. selects) you'll need to create an event in your UC that fires when a UC is clicked. The handler for this event explicitly sets IsSelected to false for all UCs in a set (such as in a container type control such as a Form, FlowLayoutPanel, etc.), the MouseClick handler in the UC that was clicked will then set the clicked UC's IsSelected to true.
It's worth considering creating another UserControl type that manages a group of your UCs. This new UserControl can encapsulate the code for the creation and state management of sets of your UC and would faciliate using your UCs in other projects as well as keeping the code of Forms hosting your UCs a bit cleaner.
I figured that rather than include a series of disjointed code snippets for each of my suggestions I'd include what I'm hoping is the minimum amount of code to allow you to reproduce what I'm talking about.
Create a new Visual Studio Winform project and use the following for class Form1:
public partial class Form1 : Form
{
public Form1() {
InitializeComponent();
flowLayoutPanel = new FlowLayoutPanel {
Dock = DockStyle.Fill,
};
this.Controls.Add(flowLayoutPanel);
// Add several sample UCs.
for (int i = 0; i < 10; i++) {
var uc = new UserControl1();
uc.WasClicked += UsersGrid_WasClicked;
flowLayoutPanel.Controls.Add(uc);
}
}
FlowLayoutPanel flowLayoutPanel;
// Event handler for when MouseClick is raised in a UserControl.
void UsersGrid_WasClicked(object sender, EventArgs e) {
// Set IsSelected for all UCs in the FlowLayoutPanel to false.
foreach (Control c in flowLayoutPanel.Controls) {
if (c is UserControl1) {
((UserControl1)c).IsSelected = false;
}
}
}
}
Next add a UserControl to the project. Keep the name UserControl1 and add a couple Labels and a PictureBox. Use this code for class UserControl1:
public partial class UserControl1 : UserControl
{
public UserControl1() {
InitializeComponent();
this.Load += UsersGrid_Load;
}
// Event fires when the MouseClick event fires for the UC or any of its child controls.
public event EventHandler<EventArgs> WasClicked;
private void UsersGrid_Load(object sender, EventArgs e) {
// Register the MouseClick event with the UC's surface.
this.MouseClick += Control_MouseClick;
// Register MouseClick with all child controls.
foreach (Control control in Controls) {
control.MouseClick += Control_MouseClick;
}
}
private void Control_MouseClick(object sender, MouseEventArgs e) {
var wasClicked = WasClicked;
if (wasClicked != null) {
WasClicked(this, EventArgs.Empty);
}
// Select this UC on click.
IsSelected = true;
}
private bool _isSelected;
public bool IsSelected {
get { return _isSelected; }
set {
_isSelected = value;
this.BorderStyle = IsSelected ? BorderStyle.Fixed3D : BorderStyle.None;
}
}
}
I know this is old, but I landed here looking for some pointers on how to do UC selection in a Container.
Jay's answer works well.
Just one update: The UsersGrid_Load method will only engage the top level controls, children of containers will not participate in the WasClicked event.
private void UsersGrid_Load(object sender, EventArgs e) {
// Register the MouseClick event with the UC's surface.
this.MouseClick += Control_MouseClick;
// Register MouseClick with all child controls.
RegisterMouseEvents(Controls);
}
Add recursive method RegisterMouseEvents to the UserControl
private void RegisterMouseEvents(ControlCollection controls)
{
foreach (Control control in controls)
{
// Subscribe the control to the
control.Click += Control_MouseClick;
if (control.HasChildren) RegisterMouseEvents(control.Controls);
}
}
You could possibly try subscribing to the GotFocus event on the UserControl.
Caveat:
Because this event uses bubbling routing, the element that receives focus might be a child element instead of the element where the event handler is actually attached. Check the Source in the event data to determine the actual element that gained focus.
UIElement.GotFocus Event
UPDATE
This question may be appropriate: Click event for .Net (Windows Forms) user control

Groupbox check for changes event

I have a DataGridView and a GroupBox control containing a few ComboBoxes.
Depending on what is selected in the ComboBoxes, the elements in the grid changes.
Is there a way to say
If (Something Changes Within The GroupBox)
{
//Update the grid
}
(Without writing a OnSelectedIndexChange event for every boxes)
I don't want the code for the updating part, I just need an event or something I could use to check if a the value of a control has changed within the GroupBox.
Any Idea ?
Update
Ok I think I didn't explained it the right way.
Forget about the ComboBox.
Let's say I have a bunch of controls in a GroupBox is there a way to say :
As soon as the value of one of the control changes, create an event.
You could hook up each combo box SelectedIndexChanged event to one method:
comboBox1.SelectedIndexChanged += new System.EventHandler(GroupBoxComboBoxChange);
comboBox2.SelectedIndexChanged += new System.EventHandler(GroupBoxComboBoxChange);
comboBox3.SelectedIndexChanged += new System.EventHandler(GroupBoxComboBoxChange);
comboBox4.SelectedIndexChanged += new System.EventHandler(GroupBoxComboBoxChange);
Or using LINQ to setup an event handler for any combo box selection change:
GroupBox.Controls.OfType<ComboBox>.ForEach(cb => cb.SelectedIndexChanged += new System.EventHandler(GroupBoxComboBoxChange));
Answer to your update: You are looking for a ControlValueChanged() event. I think the problem here is that all controls are different. What defines a "ValueChanged" event for a ComboBox isn't necessarily the same for a TextBox. It would be a semantic challenge and not very clear. Hope this makes sense.
There is no "something inside me changed" for GroupBoxes, but you can "cheat" and DYI like this (it's just a proof-of-concept without error checking and the sort):
// In a new Windows Forms Application, drop a GroupBox with a ComboBox and a CheckBox inside
// Then drop a TextBox outside the ComboBox. Then copy-paste.
// this goes somewhere in your project
public static class handlerClass
{
public static string ControlChanged(Control whatChanged)
{
return whatChanged.Name;
}
}
// And then you go like this in the Load event of the GroupBox container
void Form1_Load(object sender, EventArgs args)
{
foreach (Control c in groupBox1.Controls)
{
if (c is ComboBox)
(c as ComboBox).SelectedValueChanged += (s, e) => { textBox1.Text = handlerClass.Handle(c); };
if (c is CheckBox)
(c as CheckBox).CheckedChanged += (s, e) => { textBox1.Text = handlerClass.Handle(c); }; }
}
}
Since every Control has its own "I'm changed!" kind of event, I don't think it can be any shorter as far as boilerplate goes. Behavior is a mere sample that writes the name of the control that changed in a ComboBox
GroupBoxes are usually just decorative unless they are managing radio buttons or check boxes, so expecting them to be aware of changes made to combo boxes is not something easily done out of the box. If I may, why not code a method that does what you want it to do, and then call that method from all your combo boxes' SelectedIndexChanged events?

Categories

Resources