To focus or not to focus - c#

I have a Panel that has AutoScroll = true.
In that Panel is a series of TextBoxes. I should note that the TextBoxes aren't on the panel directly but are nested several levels (about 4-5).
Now, the scrolling with my mouse wheel works only if the panel has focus, naturally. I can use Focus() within mouseEnter event to make sure the panel has focus.
However, the TextBoxes I mentioned earlier rely heavily on focus. Only the user should be able to remove focus from the TextBox by clicking somewhere else.
The TextBoxes are created dynamically and would make for a very messy code to keep an array of them, or any type of reference to check if they have focus. Not to mention that there could be a lot of them.
How do I give the focus to the Panel, but only if none of the TextBoxes is focused?

You don't need to keep an array of the dynamically created Textboxes, you can get the array using:
bool anyTextBoxFocused = false;
foreach (Control x in this.Controls)
{
if (x is TextBox && x.Focused)
{
anyTextBoxFocused = true;
break;
}
}
if (!anyTextBoxFocused)
{
//give focus to your panel
}
Edit
Based on How to get ALL child controls of a Windows Forms form of a specific type (Button/Textbox)?, even nested controls can be obtained using:
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);
}
then use it with:
var c = GetAll(this,typeof(TextBox));

Related

Scrollbar in user control

I have a simple user control like this:
And yes, it has nothing inside, the controls will be dynamically added inside the user control using below on control load event:
List<Control> controlsNeed = getUserControls();
foreach(Control c in controlsNeed)
{
this.Controls.Add(c);
}
The controls loaded correctly, but if there're many controls, there won't be enough space and scroll bars will be needed.
The problem is, the scroll bar isn't added to the control despite setting the below:
Setting AutoScroll to true in the user control
Try setting AutoSize to false and true
What settings / code should be added to add scrollbar to the control?
List<Control> controlsNeed = getUserControls();
int PaddingTop = 10;
foreach (Control c in controlsNeed)
{
this.Controls.Add(c);
c.Location = new Point(0, c.Height + PaddingTop);
}
Or you can inherit the Panle control to your user control that will work like a panel control
public partial class MyControl : Panel
{
}
I think adding AutoScrollMinSize to user control should solve the problem
public MyUserControl()
{
InitializeComponent();
AutoScrollMinSize=new Size(0,1);
AutoScroll = true;
}

A Collection *OF* DataGridViews? Manipulating all DataGridViews at Once

I'm continuing to work on a few VSTO's and I was wondering if there was a way to refer to all the datagridviews in a class at once. I can't seem to figure out what the container should be, and I can't seem to add them to arrays/other containers?
The psudo code for what I'm tring to do would be something like:
For Each datagridview in Globals.MyUserControl
'change some datagridview property ie:
datagridview1.ReadOnly = True
Next
I would be hapy in C# or VB.net, or really any explanation of if this can or can't be done. At the moment I'm manually setting it for all the different datagridviews, as that number grows, I would like a way to hit them all at once.
Still trying to work on the solutions below, another way I've tried this that doesn't work:
For Each ctl In Me.Controls
If TypeOf ctl Is DataGridView Then
ctl.ReadOnly = True
ctl.AllowUserToDeleteRows = False
End If
Next
But I don't know why that doesn't work.
You can use a foreach loop:
foreach (DataGridView ctrl in Globals.MyUserControl.Controls)
ctrl.ReadOnly = true;
If you're expecting any non-datagridview controls in the controls collection that you don't want to set to read-only, then instead of a single statement, you can check the type of ctrl.
foreach (Control ctrl in Globals.MyUserControl.Controls)
if(ctrl is DataGridView) ctrl.ReadOnly = true;
Using LINQ, you can do this:
Globals.MyUserControl.Controls.Cast<Control>().ToList().ForEach((ctrl) => { if (ctrl is DataGridView) ((DataGridView)ctrl).ReadOnly = true; });
Or if all your controls are known to be DataGridView controls, then you can do this:
Globals.MyUserControl.Controls.Cast<DataGridView>().ToList().ForEach(ctrl => ctrl.ReadOnly = true);
To find child controls inside other controls, define a recursive method and call that:
private static void FindControlsRecursively(Control.ControlCollection collection)
{
foreach (Control ctrl in collection)
{
if (ctrl is DataGridView)
((Label)ctrl).ReadOnly = true;
else if (ctrl.Controls.Count > 0)
FindControlsRecursively(ctrl.Controls);
}
}
Then call it with the controls of your user control from your user control:
FindControlsRecursively(this.Controls);
Something like this should work:
For Each ctl In Me.Controls.OfType(Of DataGridView)()
ctl.ReadOnly = True
ctl.AllowUserToDeleteRows = False
Next
Or C#
foreach (DataGridView ctrl in this.Controls.OfType<DataGridView>())
{
ctrl.ReadOnly = true;
ctrl.AllowUserToDeleteRows = false;
}
This loops through only the DataGridViews in the form.
Additionally you can add them to a List(Of DataGridView), if necessary
Another option is to declare a class that inherits DataGridView, set the properties you want, and declare new datagridviews of this type to add to your form(s).
I'm not sure if I should put this in the answer, or if Tombola wants to move it to his, but here was my solution. The problem was that all my datagridviews were nested on tab pages in a tab control. To get it to work I used:
For Each tabpg In TabControl1.TabPages()
For Each ctl In tabpg.Controls 'note, was not able to use 'OfType here, but had to drop to the If statement or I ran into runtime errors.
If TypeOf ctl Is DataGridView Then
ctl.ReadOnly = True
ctl.AllowUserToDeleteRows = False
End If
Next
Next

C# LoadControl() (.ascx) and add into "this" rather than sub control

I'm good with Loading the control, using the LoadControl("~/vitrualPath"), so I have:
UserControl ctrl = (UserControl)LoadControl("~/controls/someControl.ascx");
this.Controls.Add(ctrl);
//plcCtrl.Controls.Add(ctrl);
The trouble is that I wish to then loop through all the controls in the usercontrol:
foreach (Label c in this.Controls.OfType<Label>())
{
// It's a label for an input
if (c.ID.Substring(0, 8) == "lblInput")
{
// Do some stuff with the control here
}
}
However, the added controls aren't part of this, but part of ctrl
Is there a way I can add the contents of the loaded control to this or a way to loop through both this and ctrl in one hit?
If you simply want to loop through both top-level labels and labels in ctrl, try this.Controls.Concat(ctrl.Controls).OfType<Label>() in your foreach loop.
You can also move your if into a LINQ Where call:
.Where(l => l.ID.Substring(0, 8) == "lblInput")
By using a recursive function you don't need to worry about controls within sub levels/ containers. Something like this should be OK (all you need to do is to pass the top level control along with the id substring that you are interested in). So if the conditions are met it will do whatever you have intended to do with the control and at any sub level.
public void ProcessControl(Control control, string ctrlName)
{
foreach (Label c in control.Controls.OfType<Label>())
{
// It's a label for an input
if (c.ID.Substring(0, 8) == ctrlName)
{
// Do some stuff with the control here
}
}
foreach (Control ctrl in control.Controls)
{
ProcessControl(ctrl, ctrlName);
}
}
You should write a recursive method that starts looping the controls in this.Controls and goes down the tree of controls. It will then also go in your user control and find your labels.
I don't think there is a way to loop through both like you want.
You can easily create a method that receives a Control as parameter and iterate though its controls. Something like this:
void Function(Control control)
{
foreach (Label c in control.Controls.OfType<Label>())
{
// It's a label for an input
if (c.ID.Substring(0, 8) == "lblInput")
{
// Do some stuff with the control here
}
}
}
You should be able to access the controls inside the user control by accessing the this.Controls[index].Controls I think, however it kind of depends what you are trying to achieve? Their might be a cleaner way of doing what you are trying to do?

Iterating through textbox controls in a panel C#

I have seen many others with similar problems but I cannot find the flaw in my logic here. Any help would be greatly appreciated.
I have a Panel which I have added numerous label and textbox controls to, ie:
myPanel.Controls.Add(txtBox);
These controls are created and added in a method called previous to the iteration method.
I want to iterate through each textbox and use its Text property as a parameter in another method but I am not having any luck. Here is my attempt to iterate:
public void updateQuestions()
{
try
{
foreach (Control c in editQuestionsPanel.Controls)
{
if (c is TextBox)
{
TextBox questionTextBox = (TextBox)c;
string question = questionTextBox.Text;
writeNewQuestionToTblQuestions(question);
}
}
}
catch (Exception err)
{
Console.WriteLine(err.Message);
}
}
The problem I am having is that the controls are not in the Panel when I arrive at this updateQuestions() method. Here is the process involved:
A commandButton is clicked and the questions are read from a DB, for each question a method is called which adds 2 labels and a textbox to editQuestionsPanel.Controls. This panel is inside a PlaceHolder which is then made visible.
When a button inside the PlaceHolder is clicked, the updateQuestions() method is called and the editQuestionsPanel.Controls.Count = 1. As there are approx 12 questions in the DB it should be around 36. The one control inside the Panel is of type:
System.Web.UI.LiteralControl
It contains no controls.
I am sure that somwhere in the lifecycle the Panel's controls are being cleared but I do not know how to step thru the life cycle. I have a Page_load method which is called as soon as a button is clicked but once the button which calls updateQuestions() is clicked the editQuestionsPanel.Controls.Count is already back to 1 so it must be cleared before this but I do not know how to correct this...
Any help you can give to help me solve this would be greatly appreciated - its killing me!
This selects from collection controls only that which are of type TextBox.
(the same as control is TextBox or (control as TextBox) != null)
If controls are contained in editQuestionsPanel.Controls:
using System.Linq;
IEnumerable<TextBox> textBoxes = editQuestionsPanel.Controls.OfType<TextBox>();
foreach (TextBox textBox in textBoxes)
{
// do stuff
}
To select all child controls use next extension method:
public static IEnumerable<T> GetChildControls<T>(this Control control) where T : Control
{
var children = control.Controls.OfType<T>();
return children.SelectMany(c => GetChildControls<T>(c)).Concat(children);
}
Using:
IEnumerable<TextBox> textBoxes = editQuestionsPanel.GetChildControls<TextBox>();
When you add controls dynamically, you need to do that on every request - asp.net doesn't do that for you!
Add the controls in the Init or Load phase, then they will get populated with the postback values.
A frequently made mistake: Container.Controls only contains the first level child controls in this container. That is: TextBox1 in PanelA, PanelA in PanelB, you can't get TextBox1 in PanelB.Controls.
My solution is to write an extension method:
public static IEnumerable<Control> AllControls(this Control ctl)
{
List<Control> collection = new List<Control>();
if (ctl.HasControls())
{
foreach (Control c in ctl.Controls)
{
collection.Add(c);
collection = collection.Concat(c.AllControls()).ToList();
}
}
return collection;
}
Now TextBox1 is in PanelB.AllControls(). To filter all controls with type, using PanelB.AllControls().OfType<TextBox>()
If the other answers don't help, try doing your code but add recursivity. Your code would not work if it's editQuestionsPanel => Panel => Textbox
You can do something like this instead:
var questions = from tb in editQuestionsPanel.Controls.OfType<TextBox>()
select tb.Text;
foreach(var question in questions)
{
writeNewQuestionToTblQuestions(question);
}
Try this
int Count = 0;
foreach (Control ctr in Panel1.Controls)
{
if (ctr is TextBox)
Count++;
}

Loop through controls in TabControl

I am looking for a way to loop through controls on a particular tab of a tabcontrol. For example, I have a tabcontrol with the following tabs:
Cars,
Pets,
Admin
On each of these tabs are several controls to display/edit/save data, etc. On the "Save" button, I would like to loop through the controls for that particular tab to check whether all required fields have been filled in.
So, if I am on the Cars tab and click "Save," I want to loop ONLY through the controls on the Cars tab and NOT the Pets or Admin tabs.
How can achieve this result?
As for looping through a TabControl's controls, you need to use the Controls property.
Here's an MSDN article on the TabControl.
Example:
TabPage page = aTabControl.SelectedTab;
var controls = page.Controls;
foreach (var control in controls)
{
//do stuff
}
I feel it's important to note that, in general, you should take a more structured approach to your application. E.g., instead of having all the controls on three tab pages, include exactly one UserControl on each tabpage. A CarUserControl, PetUserControl, and AdminUserControl e.g. Then each user control knows how to create the proper respective data structure so you don't have to manually munge it all together at the same level of abstraction using inter-tab loops and whatnot.
Such a separation of concerns will make it much easier to reason about your program and is good practice for writing maintainable code for your future career.
Example where I wanted to get the DataGridView in a particular tab for an application I wrote.
TabPage pg = tabControl1.SelectedTab;
// Get all the controls here
Control.ControlCollection col = pg.Controls;
// should have only one dgv
foreach (Control myControl in col)
{
if (myControl.ToString() == "System.Windows.Forms.DataGridView")
{
DataGridView tempdgv = (DataGridView)myControl;
tempdgv.SelectAll();
}
}
The Controls property is the way to go...
foreach(Control c in currentTab.Controls)
{
if(c is TextBox)
// check for text change
if(c is CheckBox)
//check for check change
etc...
}
TabControl has a SelectedTab property, so you'd do something like this:
foreach(Control c in tabControl.SelectedTab.Controls)
{
//do checks
}
foreach (Control c in this.tabControl1.SelectedTab.Controls)
{
// Do something
}
I had the need to disable or enable controls of a tab as well. I had to go a bit more generic though. Hope it helps people and I didn't make a mistake
private void toggleControls(Control control, bool state)
{
foreach (Control c in control.Controls)
{
c.Enabled = state;
if (c is Control)
{
toggleControls(c, state);
}
}
}

Categories