Dynamically looping through controls has no effect - c#

I have a GUI class. I pass a frmMain (form) to GUI contructor. Then I have the following method to access child controls:
public void assignEvents(frmMain frm)
{
foreach (Control ctl in frm.Controls)
{
ctl.BackColor = Color.GreenYellow;
Log.AddData(ctl.Name.ToString() + ".Backcolor = " + ctl.BackColor.ToString(), 3);
}
}
I get the new updated color in the output (log), but it takes no effect on controls and they are still in default color. Any ideas what I'm doing wrong?
EDIT:
I call it like this:
// GUI.cs
public class GUI {
public GUI(frmMain frm){
assignEvents(frm);
}
}
// frmMain.cs
public frmMain()
{
InitializeComponent();
gui = new M.Gui (this);
}

Based on your comment, you need to try to go recursively through each ControlCollection to set the BackColor property.
Try changing your code to something like this:
public GUI(frmMain frm) {
assignEvents(frm.Controls);
}
public void assignEvents(Control.ControlCollection controls) {
foreach (Control ctl in controls) {
ctl.BackColor = Color.GreenYellow;
assignEvents(ctl.Controls);
}
}

Related

Parent Form can't access Child Form Public Property - Winforms c#

I am feeling kind of stupid at the moment, because everywhere I read this is a normal procedure, and I just cannot find why I am not able to do it also!
So, the situation is the following, I have a Parent Form and a Child Form. The Child Form has a public property. From the Parent Form, i want to access the Child Form public property, and I can't.
My code is the following:
Parent code:
namespace myProgram.UserInterfaces
{
public partial class ProjectNew : Form
{
public ProjectNew()
{
InitializeComponent();
}
private void ButtonSelectCustomer_Click(object sender, EventArgs e)
{
using (Form f = new ProjectCustomerList())
{
this.SuspendLayout();
f.ShowDialog(this);
}
this.Show();
}
}
}
Child code:
namespace myProgram.UserInterfaces
{
public partial class ProjectCustomerList : Form
{
public EntCustomer _selectedCustomer = new EntCustomer();
public EntCustomer SelectedCustomer {
get
{
return _selectedCustomer;
}
}
public ProjectCustomerList()
{
InitializeComponent();
}
// --- other code ---
}
}
After the using (Form f = new ProjectCustomerList()) i would like to do the following: var sCustomer = f.SelectedCustomer;, but when I do this, Visual Studio doesn't recognize the Child Form public property.
What am I doing wrong? :|
This is normal with inheritance, since f in your case is handled as a simple Form.
You could typecast it to ProjectCustomerList to access the Property.
The is operator is also useful.
if (f is ProjectCustomerList)
{
(f as ProjectCustomerList).SelectedCustomer =...;
}
or simply
using (ProjectCustomerList f = new ProjectCustomerList())
{
f.SelectedCustomer =...;
}
seen var in other comments, works too
using (var f = new ProjectCustomerList())
{
f.SelectedCustomer =...;
}

How to enable a button to when one of my textbox changed in C#?

Today I got a problem in my development.
I have a Windows Form like this :
I need to enable the button "Appliquer" when the content of one of my textbox change.
I know that I can put the KeyPress event on each textbox and enable my button with that. In this window it can be easy to do that because there is only 10 textbox but I have an other window with more of 100 textbox and I think there is a better solution.
I tried to put the Keydown event directly in my windows form but it doesn't work.
So my question is, how can I do this. If someone have an idea ?
Thank you in advance !
Thomas
Since you already have 100+ textboxes in your form. I am assuming performance is not an issue for you.
In your form constructor, call this method. It will attach the event to all the textbox controls present in your form & inside sub controls such as groupbox, panel etc. (if you require)
There could be better ways of iteration..
public Form1()//your constructor
{
InitializeComponent();
AttachEvent(this);
}
void AttachEvent(Control CTrl)
{
foreach (Control c in CTrl.Controls)
{
if (c is TextBox)
{
c.TextChanged += new EventHandler(c_TextChanged);
continue;
}
if (c.HasChildren)
{
AttachEvent(c);
}
}
}
void c_TextChanged(object sender, EventArgs e)
{
//Your Code here btnGo.Enabled = !btnGo.Enabled;
}
What you can do is to extend TextBox make a field ( accessible from the designer ) to bind that TextBox into some other control.
public class MeTextBox
: TextBox
{
public override string Text
{
get { return base.Text; }
set
{
if ( m_DependantControl != null )
{
m_DependantControl.Enabled = !string.IsNullOrWhiteSpace(value);
}
base.Text = value;
}
}
Control m_DependantControl;
[Browsable(true)]
public Control DependantControl
{
get { return m_DependantControl; }
set { m_DependantControl = value; }
}
}
Now you can use MeTextBox as a regular TextBox. And if you want to make it control Enabled flag of some other Control you can just specify DependantControl property which will be accessible in the designer.
Fitting this into your example (code):
// assume you have a Button named btnConfirm
// and want to enable this button only when your `TextBox` has some text
MeTextBox mtb = new MeTextBox();
mtb.DependantControl = btnConfirm;
And if you do not want to make it in the code you can use designer directly.
To make it other way around ( one button dependant on many text boxes ) you can extend Button object :
public class MeButton
: Button
{
List<TextBox> m_DependantOn = new List<Control>();
[Browsable(true)]
public List<TextBox> DependantOn
{
get { return m_DependantOn; }
set { RemoveEvents(); m_DependantOn = value; AssignEvents(); }
}
void RemoveEvents()
{
foreach(TextBox ctrl in m_DependantOn)
ctrl.TextChanged -= WhenTextChanged;
}
void AssignEvents()
{
foreach(TextBox.ctrl in m_DependantOn)
ctrl.TextChanged += WhenTextChanged;
}
void WhenTextChanged(object sender, TextChangedEventArgs e)
{
this.Enabled = true;
}
}

Main control to close child

I have one MainControl that contains a ChildControl. The ChildControl has a hide button that would hide itself.
When hidden I expect the MainControl to hook the event and dispose it.
MainControl
ChildControl > Hide button
Can't figure out how I should hook those.
Any tip? Thank you!
You can create an event that will notify the main control that the child control is hidden, and in your main control, handling the event, you can dispose of your control.
Below is a small sample code of how you can go about creating your event for the hidden action.
class MainControl
{
ChildControl childControl;
public MainControl()
{
childControl = new ChildControl();
childControl.VisibilityChanged += childControl_VisibilityChanged;
}
void childControl_VisibilityChanged(object sender, HiddenEvent e)
{
if (e.isHidden)
{
//close control here
}
}
}
public class HiddenEvent : EventArgs
{
public HiddenEvent(bool propertyValue)
{
this.isHidden = propertyValue;
}
public bool isHidden { get; set; }
}
public class ChildControl
{
public event EventHandler<HiddenEvent> VisibilityChanged;
public ChildControl()
{
}
private bool _isHidden;
public bool Control
{
get
{
return _isHidden;
}
set
{
_isHidden = value;
Hidden_Handler(value);
}
}
private void Hidden_Handler(bool isHidden)
{
var handler = VisibilityChanged;
if (handler != null)
VisibilityChanged(this, new HiddenEvent(isHidden));
}
}
As an option you could bind ChildControl's button to a remove command on the main control (using RelativeSource) and let MainControl do all the work

give same property to all textbox controls

how to give same property to all textboxes present in the same form.
foreach (var textbox in this.Controls.OfType<TextBox>())
{
textbox.ContextMenu = new ContextMenu();
}
The above code works only if the textboxes are not in nested format.
In my project I have multiple tabpages in tabcontrol. so i cant implement the above code. but i can implement the below code:
foreach (TextBox textbox in this.Controls.OfType<TabControl>().SelectMany(tc => tc.Controls.OfType<TabPage>().SelectMany(page => page.Controls.OfType<TextBox>())))
{
textbox.ContextMenu = new ContextMenu();
}
foreach (var textbox in this.tabCarInsurance.Controls.OfType<TextBox>())
{
textbox.ContextMenu = new ContextMenu();
}
foreach (var textbox in this.tabHomeLoans.Controls.OfType<TextBox>())
{
textbox.ContextMenu = new ContextMenu();
}
foreach (var textbox in this.tabRetirement.Controls.OfType<TextBox>())
{
textbox.ContextMenu = new ContextMenu();
}
Here I am implementing for each tabControl. which still i dont like (because I have more tab pages to take care of). Is there anyway to reduce the above code.
I tried the below code: (not working)
foreach (var textbox in this.Controls.OfType<TabControl>().OfType<TextBox>())
{
textbox.ContextMenu = new ContextMenu();
}
I got the above code knowledge from my previous question.
Please Help
Thanks in Advance.
private void SetProperty(Control ctr)
{
foreach(Control control in ctr.Controls)
{
if (control is TextBox)
{
control.ContextMenu = new ContextMenu();
}
else
{
if (control.HasChildren)
{
SetProperty(control);
}
}
}
}
How about an extension method to do it, called from your tabcontrol container...
public static class ControlExtensions
{
public static void SetContextMenuOnChildTextBoxes(this Control control)
{
if (control is TextBox)
{
control.ContextMenu = new ContextMenu();
}
if (control.Controls != null)
{
foreach (Control controlChild in control.Controls)
{
controlChild.SetContextMenuOnChildTextBoxes();
}
}
}
}
This could be put in a common area of code so that it could be called from any parents that wanted this functionality.
Just use the recursion to go through all controls subcollections:
void SetControl(ContextMenu menu, Control control)
{
if (control is TextBox)
control.ContextMenu = menu;
else
{
foreach (Control c in control.Controls)
SetControl(menu, c);
}
}
It will find all the textboxes and set one and the same context menu to all of them.
You mal call it,say, from form's OnLoad event handler. While it's assumed that you have yourContextMenu defined for the form.
private void Form1_Load(object sender, EventArgs e)
{
SetControl(yourContextMenu, this);
}
Try:
private void CtxMenu(Control parent)
{
foreach (Control child in parent.Controls)
{
if (child is TextBox)
{
(child as TextBox).ContextMenu = new ContextMenu();
}
}

Limit the type of a base class parameter

I have a code like this:
public static void ToUpperCase(params Control[] controls)
{
foreach (Control oControl in controls)
{
if (oControl is TextBox)
{
oControl.TextChanged += (sndr, evnt) =>
{
TextBox txtControl = sndr as TextBox;
int pos = txtControl.SelectionStart;
txtControl.Text = txtControl.Text.ToUpper();
txtControl.SelectionStart = pos;
};
}
else if (oControl is ComboBox)
{
oControl.TextChanged += (sndr, evnt) =>
{
ComboBox cmbControl = sndr as ComboBox;
int pos = cmbControl.SelectionStart;
cmbControl.Text = cmbControl.Text.ToUpper();
cmbControl.SelectionStart = pos;
};
}
else throw new NotImplementedException(oControl.GetType().DeclaringType.ToString() + " is not allowed.");
}
}
I want to limit the params Control[] controls to accept only a TextBox and a ComboBox type.
My code is in C#, framework 4, build in VS2010Pro, the project is in WinForms.
Please help. Thanks in advance.
You can't- they don't have a good common ancestor.
What you can (and probably should) do is make two overloads of your method, which take parameters of each:
public static void ToUpperCase(params TextBox[] controls)
{
foreach (TextBox oControl in controls)
oControl.TextChanged += (sndr, evnt) =>
{
TextBox txtControl = sndr as TextBox ;
int pos = txtControl.SelectionStart;
txtControl.Text = txtControl.Text.ToUpper();
txtControl.SelectionStart = pos;
};
}
public static void ToUpperCase(params ComboBox[] controls)
{
foreach (ComboBoxControl oControl in controls)
oControl.TextChanged += (sndr, evnt) =>
{
ComboBox txtControl = sndr as ComboBox;
int pos = txtControl.SelectionStart;
txtControl.Text = txtControl.Text.ToUpper();
txtControl.SelectionStart = pos;
};
}
Normally you should use a common base class for TextBox or ComboBox but that is already Control. Also you cannot change the base classes of those.
The best I can come up with is to add a Debug.Assert to check the type.
Something like:
foreach (var control in controls)
Debug.Assert((control is TextBox) || (control is ComboBox));
OPTION ONE
If you want to be able to pass the mixed collection of textboxes and comboboxed in your function and still have the static type check, what you can do is to implement it in a following way.
public interface ISupportUpperCase {
event EventHandler ValueChanged;
void TransformValueToUpperCase();
}
public class UpperCaseableTextbox : Textbox, ISupportUpperCase {
//TextChanged event is already here, just use it.
//Implement TransformValueToUpperCase in a way that suits your control
public void TransformValueToUpperCase() {
int pos = this.SelectionStart;
this.Text = this.Text.ToUpper();
this.SelectionStart = pos;
}
}
public class UpperCaseableComboBox : ComboBox, ISupportUpperCase {
//TextChanged event is already here, just use it.
//Implement TransformValueToUpperCase in a way that suits your control
}
Then your function will be:
public static void ToUpperCase(params ISupportUpperCase[] controls)
{
foreach (var oControl in controls)
{
oControl.TextChanged += (sndr, evnt) =>
{
oControl.TransformValueToUpperCase();
}
}
}
By doing this you end up with better encapsulation as only the specific control should know HOW to make ITS value UpperCase, not some magic functions somewhere around.
You will also be able to easily introduce more controls that support this feature without changing other functionality.
OPTION TWO
In fact, you may get rid of this function at all with the same approach, just slightly change the interface to:
public interface ISupportUpperCase {
bool AlwaysInUpperCase { get; set }
}
so your controls will be fully responsible for this feature based on this flag:
public class UpperCaseableTextbox : Textbox, ISupportUpperCase {
public bool AlwaysInUpperCase { get; set }
//constructor
public UpperCaseableTextbox () {
this.TextChanged += (sender, args) => {
if (this.AlwaysInUpperCase) {
int pos = this.SelectionStart;
this.Text = this.Text.ToUpper();
this.SelectionStart = pos;
}
}
}
}
So instead of having a function you can just set the property when you need the control to be always in upper case and the control will manage itself.

Categories

Resources