fire panel events for child controls - c#

I have a Panel named panel1. panel1 has a "mosuseHover" eventhandler .panel1 also has some controls like pictureBox , label etc.
When i move mouse on panel1 , the event fire correctly , but when the mouse courser goes on panel1 controls , like pictureBox , the event not work .
how can i make event to be invoke when mouse courser is on child controls.
I should note that i dont want create eventhandler for each child contol.
Best Regards

You can add an IMessageFilter to implement your own global MouseHover for your Panel like this:
//This code uses some reflection so you must add using System.Reflection
public partial class Form1 : Form, IMessageFilter
{
public Form1(){
InitializeComponent();
Application.AddMessageFilter(this);
}
public bool PreFilterMessage(ref Message m) {
Control c = Control.FromHandle(m.HWnd)
if (HasParent(c,panel1)){
if (m.Msg == 0x2a1){//WM_MOUSEHOVER = 0x2a1
//Fire the MouseHover event via Reflection
typeof(Panel).GetMethod("OnMouseHover", BindingFlags.NonPublic | BindingFlags.Instance)
.Invoke(panel1, new object[] {EventArgs.Empty});
}
}
return false;
}
//This method is used to check if a child control has some control as parent
private bool HasParent(Control child, Control parent) {
if (child == null) return false;
Control p = child.Parent;
while (p != null) {
if (p == parent) return true;
p = p.Parent;
}
return false;
}
}
NOTE: The code above is implemented to work for nested controls of any level in your Panel. If your panel contains only child controls stopping at level 1. You can change the code a bit by using c = Control.FromChildHandle(m.Hwnd) and check the control's parent by c==panel1 without having to use the HasParent method to check for child-ancestor relationship.

You may create an eventhandler on the children and simply call the panels handler with the same arguments.
Also have a look at this thread

I countered the same problem. I solved it by creating a MouseEnter event and in it I declared Control ctl = sender as Control; Then I called ctl's Parent, with, Control panel = ctl.Parent; Now do whatever you want, as in panel.BackColor = Color.Red;
Like this:
private void label_MouseEnter(object sender, System.EventArgs e)
{
Control ctl = sender as Control; // gets the label control
Control panel = ctl.Parent; // gets the label's parent control, (Panel in this case)
if (ctl != null)
{
ctl.Font = new Font(ctl.Font.Name, 11, FontStyle.Underline); // change the font of the Label and style...
panel.BackColor = Color.Blue; // change the panel's backColor
ctl.Cursor = Cursors.Hand;
// do more with the panel & label
}
}
But don't forget to iterate through all the controls and to get the Panel and whatever that's inside the Panel.
Like this:
public YourApp()
{
InitializeComponent();
foreach (Control objCtrl in this.Controls) // get all the controls in the form
{
if (objCtrl is Panel) // get the Panel
{
objCtrl.MouseEnter += new EventHandler(panel_MouseEnter); // do something to the panel on MouseEnter
objCtrl.MouseLeave += new EventHandler(panel_MouseLeave);
foreach (Control ctl in objCtrl.Controls) // get all the controls inside the Panel
{
if (ctl is Label) // get the label inside the panel
{
ctl.MouseEnter += new System.EventHandler(label_MouseEnter);
ctl.MouseLeave += new EventHandler(label_MouseLeave);
ctl.Click += new EventHandler(label_Click);
}
if (ctl is PictureBox) // get the pictureBox inside the panel
{
ctl.MouseEnter += new EventHandler(label_MouseEnter);
}
}
}
}
}
You can figure out the rest.
Hope this helps.

Related

Change all buttons background color by equation

I have created a class named Design
contain this codes
public static void Edit(Form frm, Color bkColor, Color btnColor,Color pnlColor)
{
frm.BackColor = bkColor;
frm.RightToLeft = RightToLeft.Yes;
frm.RightToLeftLayout = true;
foreach (Button btn in frm.Controls.OfType<Button>())
{
btn.BackColor = btnColor;
}
foreach (Panel pnl in frm.Controls.OfType<Panel>())
{
pnl.BackColor = pnlColor;
}
}
and I am calling it by this in the form:
Design.Edit(this, Color.Blue, Color.Green, Color.Yellow);
NOW it works good on the form background BUT on the panel and buttons not working at all
You need a recoursive search of your control inside of all the controls of the form.
Look that accepted answer that implement a very good recoursive approach.
Doing this:
frm.Controls.OfType<Button>()
you search only in the first layer of controls of your forms, so if you've a button inside a panel or another element (the 99,999999% of the situations) your foreach cannot find it.
adapting the Accepted answer at your Question:
public IEnumerable<Control> GetAll(this Control control,Type type)
{
var controls = control.Controls.Cast<Control>();
return controls.SelectMany(ctrl => ctrl.GetAll(type))
.Concat(controls)
.Where(c => c.GetType() == type);
}
[...]
foreach (Button btn in frm.GetAll(typeof(Button)))
{
btn.BackColor = btnColor;
}
L-

How to visible all items inside groupbox in c#

I have a windows form which include some textbox and labels.In my program I set all of them unvisible and when I press button it makes all of the labels and textbox visible with the code below and it works perfect.
List<Label> lbls = this.Controls.OfType<Label>().ToList();
foreach (var lbl in lbls)
{
if (lbl.Name.StartsWith("label"))
{
lbl.Visible = true;
}
}
List<TextBox> txts = this.Controls.OfType<TextBox>().ToList();
foreach (var txt in txts)
{
if (txt.Name.StartsWith("textBox"))
{
txt.Visible = true;
}
}
But when I put all of my labels and textboxes into groupbox.My code doesn't work.How can I do this?
Note: My groupbox is also unvisible and when I press button.
groupBox1.visible =true;
This code works and groupbox panel seems, but the code of labels and textboxes doesn't work.
Because you are working with the immediate child of Form here
List<Label> lbls = this.Controls.OfType<Label>().ToList();
Notice this that means your current form. so when you have controls outside in form it works,
But when you put them inside group box it won't be the immediate child anymore.
so use
List<Label> lbls = groupBox1.Controls.OfType<Label>().ToList();
This will give you access to immediate children of the group box.
You're better off creating a recursive method of your own. Try implementing something like this:
private void MakeControlsInvisible(Control container, params Type[] controlTypes)
{
foreach (Control control in container.Controls)
{
if (controlTypes.Contains(control.GetType()))
{
control.Visible = false;
}
if (control.Controls.Count > 0)
{
MakeControlsInvisible(control, controlTypes);
}
}
}
And then using it on whatever container you wish:
MakeControlsInvisible(this, typeof(Label), typeof(TextBox)); // Will make all labels and textboxes inside the entire form invisible.
MakeControlsInvisible(groupBox1, typeof(Label), typeof(TextBox));// Will make all labels and textboxes inside groupBox1 invisible.

How to set the margins of text box red if error is present

I was just wondering if its was easily possible to set the margins of a textbox a particular color? Im using winforms and in my validating event handlers i have a series of error providers, which if return false I want to set just the margins red and if successful green. is this possible with out having a any controls hidden behind ? I know how to set the foreground and panel colour but it just seems so sloppy to have to have this all hidden behind it. this is my validating event handler.
private void txt_LndLine_Validating(object sender, CancelEventArgs e)
{
if (utility.isNum,(txt_LndLine.Text))
{
epLandline.SetError(txt_LndLine, "Please use unknown Entity!!!");
return;
}
else
{
epLandline.Clear();
_IsValid = true;
}
}
Just a query, as the event hadnler works fine just wouldn't mind a smarter way of presenting the errors rather than the icon
Here is an alternative solution, which won't add any extra controls. It paints the border onto the TextBoxes' Parent.
Set it up by hooking the routine into the Paint event of your TextBox's Parent once, maybe like this:
textBox1.Parent.Paint += DrawMargins;
Now you can set the Tag to hold the Brush you want to use:
textBox1.Tag = Brushes.Red;
textBox2.Tag = Brushes.Green;
After changing the Brush you need to trigger the routine, by Invalidating the Parent:
textBox1.Parent.Invalidate();
To take one TextBox out of the painting reset the Tag to null:
textBox1.Tag = null;
You can also un-hook the whole event of course:
textBox1.Parent.Paint -= DrawMargins;
Here is the drawing method:
private void DrawMargins(object sender, PaintEventArgs e)
{
Control parent = sender as Control;
foreach ( Control ctl in parent.Controls)
{
SolidBrush brush = ctl.Tag as SolidBrush;
if (brush == null) continue;
e.Graphics.FillRectangle(brush, ctl.Left - ctl.Margin.Left,
ctl.Top - ctl.Margin.Top,
ctl.Width + ctl.Margin.Horizontal,
ctl.Height + ctl.Margin.Vertical);
}
}
Note that this will work for any control which has a SolidBrush in the Tag and is a child of the same parent. If some controls as nested, say in a Panel or a GroupBox, I guees you should replace the loop over the parent.Controls collection by a List<Control> of the participating controls..
I have enlarged the left margin of the 1st TextBox, as you can see..
May be the answer in comments is a good one (#RudyTheHunter).
I have a simple way of doing.
Put a panel control right below the text box and change the border color of the panel.
I tried it and looks as shown in below image.
Code:
if (utility.isNum,(txt_LndLine.Text))
{
epLandline.SetError(txt_LndLine, "Please use unknown Entity!!!");
//PANEL COLOR
this.panel1.BackColor = System.Drawing.Color.Green;
this.panel1.Padding = new System.Windows.Forms.Padding(5);
return;
}
else
{
epLandline.Clear();
//PANEL COLOR
this.panel1.BackColor = System.Drawing.Color.Red;
this.panel1.Padding = new System.Windows.Forms.Padding(5);
_IsValid = true;
}

foreach loop seems to slow

I'm currently trying to add some custom controls to a panel in winforms.
Every control will be docked and build something like a "list".
Now i'm trying to implement a feature to select/deselect every control.
Its working fine, my problem is that it seems to be very slow sometimes.
Currently its about ~50 custom controls in the panel.
modtable.Click += (s, e) =>
{
foreach (Control m in pnl_ucMods.Controls)
{
if(m is ModTableEntry)
{
if(m != modtable)
{
((ModTableEntry)m).BackColor = SystemColors.Control;
}
else if (m == modtable && m.BackColor == SystemColors.Control)
m.BackColor = SystemColors.ActiveCaption;
else
m.BackColor = SystemColors.Control;
}
}
};
Whenever i click on one of the controls it will change the backcolor, on a second click it will change it back but thats only working if i wait like a second before i click again. If i click to fast, nothing happens and i have to click again.
I understand that winforms is not designed to have tons of controls and i understand that foreach will need some time to loop through all the controls, but maybe someone here has a small idea how to improve the code and maybe solve this problem.
tl;dr
Click on one of the custom controls in the panel will change its backcolor. (Selected)
Every other control will change the backcolor too (deselected)
If the clicked control is already selected, it will deselect.
EDIT:
A small example to test that problem.
Just create a new project, add the code and call it.
private void addPanels()
{
Panel newPanel = new Panel();
newPanel.AutoScroll = true;
newPanel.Dock = DockStyle.Fill;
this.Controls.Add(newPanel);
for (int i = 0; i < 50; i++)
{
Panel childPanel = new Panel();
childPanel.Size = new Size(100, 30);
childPanel.Dock = DockStyle.Top;
childPanel.Click += (s, e) =>
{
foreach (Control p in newPanel.Controls)
{
if (p is Panel)
{
if (p != childPanel)
((Panel)p).BackColor = SystemColors.Control;
else if (p == childPanel && p.BackColor == SystemColors.Control)
p.BackColor = SystemColors.ActiveCaption;
else
p.BackColor = SystemColors.Control;
}
}
};
newPanel.Controls.Add(childPanel);
}
}
Use MouseDown event instead of Click event.
When you click twice too fast, it would be a DoubleClick event and no other Click event will raise.
With your permission, Reza.

Getting Control details loaded in Panel dynamically in WinForms

I have a panel in WinForms which loads panels at run time during a method call.
My code looks like:
//Adding a child panel
Panel p = new Panel();
//Adding controls to panel
Label lbl5 = new Label();
lbl5.Location = new Point(105, 3);
lbl5.Text = note.noteName;
Label lbl6 = new Label();
lbl6.Location = new Point(105, 43);
lbl6.Text = note.noteName;
p.Controls.Add(lbl5);
p.Controls.Add(lbl6);
//Adding child panel to main panel
Panel1.Controls.Add(p);
In this way whenever the method is called a new child panel will be added to main panel.
Can I Click a particular panel which is displayed in main panel ?
I want to get the value of the controls present in selected panel and show it somewhere.
I would appreciate any help on this.
Name your panel....
var pPanel = new Panel();
pPanel.Name = "pPanel";
// or write it this way....using object initializer
var pPanel = new Panel
{
Name = "pPanel"
};
Then loop through the controls in you master panel for the control you are looking for...
foreach(Control ctrl in mainPanel)
{
if (ctrl.Name.Contains("pPanel")) .... then do something etc...;
}
You can also search for other controls in your panels the same way ...
Subscribe to a event like so:
Panel p = new Panel();
p.Click += panel_click;
And then create the event:
private void panel_click(object sender, EventArgs e)
{
Panel childPanel = sender as Panel;
foreach(Control c in childPanel.Controls)
{
//Do something with you values...
}
}

Categories

Resources