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-
Related
I have a Form that contains a few TableLayoutPanel (List Panels) that I create dynamically.
Each TableLayoutPanel has a unique Tag. Actually each panel has Lable and this Lable has Name = "lable_name"
I need to update this exact Label inside a TableLayoutPanel.
public void UpdateLable(string tag, string newText)
{
foreach(var tlp in Views)
{
if (tlp.Tag.ToString().Equals(tag))
{
var lable = tlp.findViewByName("lable_name") as Label;
lable.Text = newText;
}
}
}
But I can't find method like findViewByName()
So, question is - how to find view by name?
Create a method as below
public static IEnumerable<Control> GetControlsOfType<T>(Control control)
{
var controls = control.Controls.Cast<Control>();
return controls.SelectMany(ctrl => GetControlsOfType<T>(ctrl)).Concat(controls).Where(c => c is T);
}
Use it like
Var control= GetControlsOfType<Label>(yourView).FirstOrDefault(x => x.Tag == tag);
if(control != null)
control.Text = newText
there is no such method directly in TableLayoutPanel class. But each Control has Controls property - collection of child controls.
that specialized collection has Find method which allows to get child by name:
var label = tlp.Controls.Find("lable_name", true)[0] as Label;
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.
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.
I have a Panel and two LinkLabels added on the panel and a treeview.
now in the panel_Paint event i want that the linklabel colors become white and background color of treeview turns black.
how do i do this?
the below code works only when there is no tree view in the panel but when i add a treeview also in the panel then it says :
Unable to cast object of type 'System.Windows.Forms.TreeView' to type 'System.Windows.Forms.LinkLabel'.
foreach (LinkLabel link in panel1.Controls)
{
link.LinkColor = Color.White;
}
Your panel contains all the controls - one of them is a TreeView which cannot be cast into a LinkLabel. In your loop you need to check the type of the control like this:
foreach (Control control in panel1.Controls)
{
if (control is LinkLabel)
{
... set link color
}
else if (control is TreeView)
{
... set background
}
}
Alternatively if you only have one LinkLabel and one TreeView you would not need a loop - just access them by name like you did with panel1
Try this:
foreach (Control ctrl in panel1.Controls)
{
LinkLabel link = ctrl as LinkLabel;
if(link != null)
link.LinkColor = Color.White;
}
Your getting the error because your trying to cast all controls in panel1 to a LinkLabel. You need to try something like this
foreach (Control control in panel1.Controls)
{
if (control.GetType() == typeof(LinkLabel))
{
LinkLabel link = (LinkLabel)control;
link.LinkColor = Color.White;
}
}
Hope this helps.
Edit: I knew there was a method but wasn't sure 100% of the name or syntax. See below an improved answer.
foreach (LinkLabel link in panel1.Controls.OfType<LinkLabel>())
{
link.LinkColor = Color.White;
}
Hope this is better for you.
I loaded 4 radios buttons and when I run the program and click on them, if i click one, and click another the other ones goes away. What if I wanted to ahve two radio buttons but they did different things?
Group the different choice sets in separate group boxes (or panels, or other container controls, but group boxes are probably what you're after).
MSDN:
Windows Forms RadioButton controls are designed to give users a choice among two or more settings, of which only one can be assigned to a procedure or object. For example, a group of RadioButton controls may display a choice of package carriers for an order, but only one of the carriers will be used. Therefore only one RadioButton at a time can be selected, even if it is a part of a functional group.
You group radio buttons by drawing them inside a container such as a Panel control, a GroupBox control, or a form.
This assumes you have four or more radio buttons on your form. If you have only two, say, and you want to allow the user the possibility of selecting both, use a set of checkboxes.
You should use GroupBox control.
All radio buttons in a GroupBox are mutually exclusive - put 2 radio buttons in one GroupBox, and the other 2 in another GroupBox.
If you don't like your container to be visible - use Panel control instead of GroupBox.
(Just drag and drop)
As you can probably guess, the upper one is a GroupBox, the lower one (which is invisble, but allows only one readioButton within it to be selected) is a panel.
HTH.
I like the concept of grouping RadioButtons in WPF. There is a property GroupName that specifies which RadioButton controls are mutually exclusive (http://msdn.microsoft.com/de-de/library/system.windows.controls.radiobutton.aspx).
So I wrote a derived class for WinForms that supports this feature:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Windows.Forms.VisualStyles;
using System.Drawing;
using System.ComponentModel;
namespace Use.your.own
{
public class AdvancedRadioButton : CheckBox
{
public enum Level { Parent, Form };
[Category("AdvancedRadioButton"),
Description("Gets or sets the level that specifies which RadioButton controls are affected."),
DefaultValue(Level.Parent)]
public Level GroupNameLevel { get; set; }
[Category("AdvancedRadioButton"),
Description("Gets or sets the name that specifies which RadioButton controls are mutually exclusive.")]
public string GroupName { get; set; }
protected override void OnCheckedChanged(EventArgs e)
{
base.OnCheckedChanged(e);
if (Checked)
{
var arbControls = (dynamic)null;
switch (GroupNameLevel)
{
case Level.Parent:
if (this.Parent != null)
arbControls = GetAll(this.Parent, typeof(AdvancedRadioButton));
break;
case Level.Form:
Form form = this.FindForm();
if (form != null)
arbControls = GetAll(this.FindForm(), typeof(AdvancedRadioButton));
break;
}
if (arbControls != null)
foreach (Control control in arbControls)
if (control != this &&
(control as AdvancedRadioButton).GroupName == this.GroupName)
(control as AdvancedRadioButton).Checked = false;
}
}
protected override void OnClick(EventArgs e)
{
if (!Checked)
base.OnClick(e);
}
protected override void OnPaint(PaintEventArgs pevent)
{
CheckBoxRenderer.DrawParentBackground(pevent.Graphics, pevent.ClipRectangle, this);
RadioButtonState radioButtonState;
if (Checked)
{
radioButtonState = RadioButtonState.CheckedNormal;
if (Focused)
radioButtonState = RadioButtonState.CheckedHot;
if (!Enabled)
radioButtonState = RadioButtonState.CheckedDisabled;
}
else
{
radioButtonState = RadioButtonState.UncheckedNormal;
if (Focused)
radioButtonState = RadioButtonState.UncheckedHot;
if (!Enabled)
radioButtonState = RadioButtonState.UncheckedDisabled;
}
Size glyphSize = RadioButtonRenderer.GetGlyphSize(pevent.Graphics, radioButtonState);
Rectangle rect = pevent.ClipRectangle;
rect.Width -= glyphSize.Width;
rect.Location = new Point(rect.Left + glyphSize.Width, rect.Top);
RadioButtonRenderer.DrawRadioButton(pevent.Graphics, new System.Drawing.Point(0, rect.Height / 2 - glyphSize.Height / 2), rect, this.Text, this.Font, this.Focused, radioButtonState);
}
private 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);
}
}
}
Try placing similar options in a container, like GroupBox
Typically a group of radio buttons is used when only one option applies. If it is valid to select multiple options simultaneously, use Checkboxes instead.
Put all radio buttons into a group box.
Now, on all radio button Properties, set Auto Check to false.
Alternatively, you can set
radioButton1->AutoCheck = false;
Inside the radioButton1_Click function, handle as you needed. Example:
Void radioButton1_Click(Object^ sender, EventArgs^ e) {
radioButton1->Enabled = true;
radioButton2->Enabled = true;
radioButton3->Enabled = true;
radioButton1->Checked = true;
radioButton2->Checked = true;
radioButton3->Checked = true;
}
This should make those selected radio buttons to check.