Checking each event in winforms - c#

I'm changing the visibility of some labels on the form , when a contol is moved. When all labels are invisible , i want to close application. Is there a way to do this ?

You can keep track of how many visible Label controls there are, and when the count reaches 0, you can close your form (or perform whatever other action is required to close the program in your case).
For example:
partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
int visibleLabelCount = 0;
foreach (Label label in Controls.OfType<Label>())
{
// Check property, in case some Label is initialized as not
// visible in the Designer
if (label.Visible)
{
visibleLabelCount++;
}
label.VisibleChanged += (sender, e) =>
{
visibleLabelCount += ((Label)sender).Visible ? 1 : -1;
if (visibleLabelCount <= 0)
{
Close();
}
}
}
}
}
The above attaches an event handler to each Label control found in your Form class, and at the same time counts the currently-visible Label controls. The event handler adjusts the count of visible Label controls as the visibility of each changes; if a Label becomes visible, the count is increased, if it becomes not visible, it's decreased. If the count reaches zero, the Form's Close() method is called to close the form and the program.

Create a function like this:
public void CheckLabels()
{
bool AllHidden = true;
foreach (Control c in this.Controls)
{
if (c.GetType().Name == "Label")
{
if (c.Visible == true)
{
AllHidden = false;
Break;
}
}
}
if (AllHidden)
{
//Do whatever you want. For example:
this.Close();
}
}
Call this function from a button click event of Visibility_Changed event of all labels.
EDIT:
Another way is create your own label by inheriting from System.Windows.Forms.Label.
public partial class MyLabel : System.Windows.Forms.Label
{
public MyLabel()
{
InitializeComponent();
this.VisibleChanged += new EventHandler(MyLabel_VisibleChanged);
}
void MyLabel_VisibleChanged(object sender, EventArgs e)
{
CheckLabels();
}
public void CheckLabels()
{
bool AllHidden = true;
foreach (System.Windows.Forms.Control c in this.FindForm().Controls)
{
if (c.GetType().Name == "MyLabel")
{
if (c.Visible == true)
{
AllHidden = false;
break;
}
}
}
if (AllHidden)
{
//Do whatever you want. For example:
this.FindForm().Close();
}
}
And use MyLabel in your form. Thats it!!

Related

C# WinForms - Cannot access a control in a handler method

I have a form containing two flow layout panels (FLP), which dynamically have buttons added to them. These buttons are actually a class called tagButton which inherits from Button and I have added a handler in the constructor for the click() method. On click, I want to remove the button from the FLP it is currently in then add it to the other FLP.
Below is a trimmed down version of my code for the tagButton class. Note that the tagButton class is defined inside the of the form class both FLPs are in:
class tagButton : Button
{
public string tag = "";
public bool useTag = false; //tells you which FLP the button is in
public tagButton(String tag, Boolean useTag)
{
this.tag = tag;
this.Text = tag;
this.useTag = useTag;
this.Click += TagButton_Click;
}
private void TagButton_Click(object sender, EventArgs e)
{
tagButton tagButton = (tagButton)sender;
tagButton.useTag = !tagButton.useTag;
if (tagButton.useTag)
{
flowLayoutPanel.Controls.Remove(tagButton);
}
}
}
I'm having problems with the last line:
flowLayoutPanel.Controls.Remove(tagButton);
I can switch it to the following and it works, however there is no way for me to add it to the other FLP. Or at least, not without doing Parent.Parent.Parent.Controls[1]... etc which is clearly a bad idea.
tagButton.Parent.Controls.Remove(tagButton);
I've tried switching different classes and methods to static but nothing I tried worked, the this keyword doesn't seem to work either.
I would recommend having a separate class overriding a parent control that's aware of both FlowLayoutPanels. Then, when your button wants to switch, it can find that custom control in its parents and invoke a custom "switch" function that would move the invoking button from the list it's in to the list it wasn't in.
One of many ways to achieve this outcome is to have MainForm expose a static array of the FlowLayoutPanel candidates as Panels property:
public partial class MainForm : Form
{
public static Control[] Panels { get; private set; }
char _id = (char)64;
public MainForm()
{
InitializeComponent();
Panels = new Control[]{ flowLayoutPanelLeft, flowLayoutPanelRight, };
buttonAddLeft.Click += (sender, e) =>
{
flowLayoutPanelLeft.Controls.Add(new tagButton
{
Height= 50, Width=150,
Name = $"tagButton{++_id}",
Text = $"Button {_id}",
});
};
buttonAddRight.Click += (sender, e) =>
{
flowLayoutPanelRight.Controls.Add(new tagButton
{
Height= 50, Width=150,
Name = $"tagButton{++_id}",
Text = $"Button {_id}",
});
};
}
}
Then, suppose you want to swap between panels when a tagButton gets a right-click (for example).
class tagButton : Button
{
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (MouseButtons.Equals(MouseButtons.Right))
{
Control dest;
if(Parent.Name.Contains("Left"))
{
dest = MainForm.Panels.First(_=>_.Name.Contains("Right"));
}
else
{
dest = MainForm.Panels.First(_ => _.Name.Contains("Left"));
}
Parent.Controls.Remove(this);
dest.Controls.Add(this);
}
}
}

Add a UserControl that is created in another thread to a form

I have an application that has a form named as MainAppForm (Thread1). I have a panel in this form which will host UserControls.
When a user clicks the button, I want to create another thread (Thread2) which will create an instance of the UserControl and call a method that is on the Thread1 to add UserControl to the panel in mentioned in the first paragraph.
This is how I call main Thread1 from Thread2
public class SecondThread
{
public void start()
{
ModuleWindow userControl = new ModuleWindow(new Module.ModuleLayer());
Global.SetModuleWindowThreadSafe(userControl);
}
}
My method that will add the passed in user control to the panel.
public static class Global
{
private delegate void SetModuleWindowThreadSafeDelegate(UserControl userControl);
public static void SetModuleWindowThreadSafe(UserControl userControl)
{
if (Global.mainAppForm.pnlMain.InvokeRequired)
{
Global.mainAppForm.pnlMain.Invoke(
new SetModuleWindowThreadSafeDelegate(SetModuleWindowThreadSafe),
userControl);
}
else
{
Global.mainAppForm.pnlMain.Controls.Add(userControl);
}
}
}
After I do the call in the SetModuleWindowThreadSafe() method it raises
Cross-thread operation not valid: Control 'menuStrip1' accessed from a thread other than the thread it was created on.
Note: menuStrip1 is a control on UserControl.
How can I add the UserControl that is created in the second thread to the panel???
UPDATED:
Thanks for the answers. I am sure they're helpful in some ways but not in my condition. The reason is my MainAppForm(AKTAP project) and the generated UserControl's(KKM project) are being created in different projets even solutions. The project output of KKM is a .dll and I am loading those dll files on runtime using reflections. So MainAppForm does not know what type of usercontrols and controls are being generated in each dll.
What I want to do is in the following order:
1- AKTAP project has an interface which is implemented by a class in KKM project.
2- KKM project is being built and puts dll files to a specified directory.
3- AKTAP starts to run and loads dll files using reflections by filtering the interface mentioned in 1.
4- AKTAP calls a method in KKM hich will generate and return the usercontrol.
5- AKTAP adds the returned usercontrol to the MainAppForm. (And this is where I get the exception above.)
How can I add the UserControl that is created in the second thread to the panel?
You don't. You create the UserControl in the UI thread, rather than in some background thread.
If you have some expensive CPU bound computation to do in order to figure out what data the user control will need then use another thread to compute that data and then have the UI thread take that data and create the UI controls to display it.
Servy is correct - you don't.
However, you can! Meaning, it is possible.
Passing data from a thread is complicated, but the System.ComponentModel.BackgroundWorker (part of WinForms) greatly simplifies threading operations and makes tasks like this rather fun to do.
Here is a generic technique that uses two (2) Windows Forms, one as a variable inside the other. Both are in the same namespace (same project, etc).
Form1:
public partial class Form1 : Form
{
private Button btnGetInteger;
private Button btnGetMenuStrip;
private Button btnGetString;
private Form2 _form2;
private Form2.ReturnType _getType;
private Object _form2Argument;
public Form1()
{
InitializeComponent();
btnGetInteger = new Button();
btnGetInteger.Click += Form2_GetInteger;
btnGetMenuStrip = new Button();
btnGetMenuStrip.Click += Form2_GetInteger;
btnGetString = new Button();
btnGetString.Click += Form2_GetString;
Shown += (s, e) => { Form2_CreateMenuStrip(s, EventArgs.Empty); };
}
public void Form2_ThreadChanged(object sender, ProgressChangedEventArgs e)
{
var returned = (Form2.ReturnType)e.ProgressPercentage;
switch (returned)
{
case Form2.ReturnType.MenuStrip:
var menuStrip = (MenuStrip)e.UserState;
this.Controls.Add(menuStrip);
break;
case Form2.ReturnType.Integer:
var numberBack = (int)e.UserState;
Text = String.Format("Form1 : (int){0}", numberBack);
break;
case Form2.ReturnType.String:
var stringBack = e.UserState.ToString();
Text = String.Format("Form1 : (String){0}", stringBack);
break;
}
}
public void Form2_ThreadCompleted(object sender, RunWorkerCompletedEventArgs e)
{
_form2Argument = null;
if (e.Error != null)
{
String title;
if (_form2 != null)
{
title = String.Format("{0}: {1}", _form2.Text, e.Error.GetType());
} else
{
title = String.Format("Form2: {0}", e.Error.GetType());
}
MessageBox.Show(e.Error.Message, title, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
if (_form2 != null)
{
_form2.Close();
_form2.Dispose();
_form2 = null;
}
btnGetInteger.Enabled = true;
btnGetMenuStrip.Enabled = true;
btnGetString.Enabled = true;
}
private void Form2_CreateMenuStrip(object sender, EventArgs e)
{
if (_form2 == null)
{
_getType = Form2.ReturnType.MenuStrip;
var item = new ToolStripMenuItem(Text);
item.Click += Form2_GetInteger;
_form2Argument = item;
Form2_StartWork();
}
}
private void Form2_GetInteger(object sender, EventArgs e)
{
if (_form2 == null)
{
_getType = Form2.ReturnType.Integer;
Form2_StartWork();
}
}
private void Form2_GetString(object sender, EventArgs e)
{
if (_form2 == null)
{
_getType = Form2.ReturnType.String;
Form2_StartWork();
}
}
private void Form2_StartWork()
{
btnGetInteger.Enabled = false;
btnGetMenuStrip.Enabled = false;
btnGetString.Enabled = false;
_form2 = new Form2();
_form2.Show(); // Show returns immediately
_form2.StartThread(this, _form2Argument, _getType);
}
}
Form2_ThreadChanged and Form2_ThreadCompleted are both set to PUBLIC so that they can be visible by the instance of Form2.
Form2:
public partial class Form2 : Form
{
private ReturnType _getType; // thread safe
private BackgroundWorker _bwThread;
public enum ReturnType { MenuStrip, String, Integer }
public Form2() // Do Not Call this method
{
InitializeComponent();
}
public void StartThread(Form1 parent, Object argument, ReturnType getType)
{
_getType = getType;
if (_bwThread == null)
{
_bwThread = new BackgroundWorker() {
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
_bwThread.DoWork += ThreadWork;
_bwThread.ProgressChanged += parent.Form2_ThreadChanged;
_bwThread.RunWorkerCompleted += parent.Form2_ThreadCompleted;
}
if (!_bwThread.IsBusy)
{
_bwThread.RunWorkerAsync(argument);
}
}
private void ThreadWork(object sender, DoWorkEventArgs e)
{
switch (_getType)
{
case ReturnType.MenuStrip:
var menuStrip = new MenuStrip();
if (e.Argument != null)
{
var mi = (ToolStripMenuItem)e.Argument;
menuStrip.Items.Add(mi);
}
_bwThread.ReportProgress((int)_getType, menuStrip);
break;
case ReturnType.Integer:
var numberBack = 1;
_bwThread.ReportProgress((int)_getType, numberBack);
break;
case ReturnType.String:
var stringBack = "Worker String";
_bwThread.ReportProgress((int)_getType, stringBack);
break;
}
}
}
If you make a new, small project with 2 empty forms in it called Form1 and Form2, you can go into the code and simply paste everything from above into those two forms.
With that done, just put breakpoints on all of the methods (both public and private) to see how they work.

Unable to register click event from composite control to parent form

What I am attempting to do is create a complex control that has a picture box, track slider and numeric up down controls. In the parent form, when the user clicks on an image, then this composite control appears and the background color is then sent to it and the image in the control is then set with that background color. Then if the user clicks on the image on the composite control, the parent form is then notified of the click event and then subsequently removes that specific composite control from the parent form.
Composite Control code
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace ctlClusterControlLib
{
public partial class UserControl1 : UserControl
{
private Color colImageBackground;
private int intThreadCount;
private PictureBox pictureBoxControl; // Compiler informs me that this is never assigned to and will always have its default value null.
private TrackBar trackBar; // Compiler informs me that this is never assigned to and will always have its default value null.
private NumericUpDown numericUpDown; // Compiler informs me that this is never assigned to and will always have its default value null.
private string strImageToolTip1;
private string strImageToolTip2;
private static object EventSubmitKey = new object();
public UserControl1()
{
InitializeComponent();
}
public Color ImageBackground
{
get { return colImageBackground; }
set { colImageBackground = value; Invalidate(); }
}
public int ThreadCount
{
get { return intThreadCount; }
set { intThreadCount = value; }
}
[
Category("Action"),
Description("Raised when the user clicks on the image.")
]
public event EventHandler PictureClick
{
add { Events.AddHandler(EventSubmitKey, value); }
remove { Events.RemoveHandler(EventSubmitKey, value); }
}
public event EventHandler TrackBarScroll
{
add { trackBar.Scroll += value; }
remove { trackBar.Scroll -= value; }
}
public event EventHandler numericUpDownChange
{
add { numericUpDown.ValueChanged += value; }
remove { numericUpDown.ValueChanged -= value; }
}
public string ImageToolTip1
{
get { return strImageToolTip1; }
set { strImageToolTip1 = value; }
}
public string ImageToolTip2
{
get { return strImageToolTip2; }
set { strImageToolTip2 = value; }
}
private void trackBar1_Scroll(object sender, EventArgs e)
{
numericUpDown1.Value = trackBar1.Value;
}
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
trackBar1.Value = Convert.ToInt32(numericUpDown1.Value);
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
Color c = Color.FromArgb(0xFF, colImageBackground);
pictureBox1.BackColor = c;
}
}
}
Parent Form CS relevant section:
private void newPictureBox_Click(object sender, EventArgs e)
{
UserControl1 _UserControl = new UserControl1();
PictureBox _PictureBox = (PictureBox)sender;
string _NewControlClusterName = "_New" + _PictureBox.Name;
_UserControl.Name = _NewControlClusterName;
_UserControl.ThreadCount = 16;
_UserControl.ImageBackground = _PictureBox.BackColor;
_UserControl.Dock = DockStyle.Top;
_UserControl.PictureClick += new EventHandler(ClusterControl_Click);
//_UserControl.TrackBarScroll += new EventHandler(GetTartanCode);
panel3.Controls.Add(_UserControl);
panel3.Controls.SetChildIndex(_UserControl, 0);
}
And I am having intermittent issues with raising the click event to the parent form using this control.
I have tried everything I can find in Google and Stack Overflow with no joy. My questions are this:
Am I even in the right ballpark?
Is this something that needs to be coded in the parent form cs file?
Is this something that needs to be reconfigured in the composite control cs file?
Is this something that needs to be configured in both files?
I believe I have a solution.
What I was not doing was directly assigning the request to the control I wanted to register the event for. Instead I was assigning it to a new control and therefore nothing would happen.
public event EventHandler PictureClick
{
add { pictureBox1.Click += value; }
remove { pictureBox1.Click -= value; }
}
And so far, It works every time.

In which form event I can hide label of user control

In a windows application project I have a form which used a user control. I want to hide a label and textbox on user control. In which event of form I can do this ?
This method in user control which named DoctorPermissionApprove:
public void LoadDoctorPermission(int fromWhere)
{
if (fromWhere == 0) // Başhekimden geldiyse?
{
labelDoctor.Visible = true;
editDoctorWithoutHead.Visible = true;
}
else if (fromWhere == 1) // Normal Hekimden geldiyse
{
labelDoctor.Visible = false;
editDoctorWithoutHead.Visible = false;
}
}
And in form:
private void ExistRequestAndNewEntryForm_Shown(object sender, EventArgs e)
{
var obj = new DoctorPermissionApprove();
obj.LoadDoctorPermission(0);
}
For example I tried in shown event. But it still visible
I want to hide or show this components when the anybody open the form
Thank you a lot
In the UserControl class add a public property to set the internal label visibility true or false. This can be accessed from your parent form where your usercontrol is added.
Example:
public class YourUserControl
{
//This code will be in designer class
private Label lblYourLabelToHide = new Label();
//Create this public property to hide the label
public bool IsLabelVisible
{
set { lblYourLabelToHide.Visible = value; }
}
}
public class YourParentForm
{
//This will be in designer
private YourUserControl userControl = new YourUserControl();
public void Form_Load()
{
//based on some criteria
userControl.IsLabelVisible = false;
}
}

Changing the properties of the active control automatically

Please consider that im a newcomer to c#. After scanning about 700 posts i decided to post one more question:
On my windows form (c#) I have some controls including textboxes, checkboxes and so on.
I want to change the backcolor whenever the controls become active.
I know i could raise 'enter' and 'leave' events for each control to change the corresponding properties but there should be another way.
Simply hook Enter and Leave events - toggling the color in each. Save the last color saved in OnEnter to use in OnLeave
public Form1()
{
InitializeComponent();
var lastColorSaved = Color.Empty;
foreach(Control child in this.Controls)
{
child.Enter += (s, e) =>
{
var control = (Control)s;
lastColorSaved = control.BackColor;
control.BackColor = Color.Red;
};
child.Leave += (s, e) =>
{
((Control)s).BackColor = lastColorSaved;
};
}
}
You customize control classes just like you customize any class, you derive your own class and override the virtual methods. Arbitrarily:
using System;
using System.Drawing;
using System.Windows.Forms;
class MyTextBox : TextBox {
protected override void OnEnter(EventArgs e) {
prevColor = this.BackColor;
this.BackColor = Color.Cornsilk;
base.OnEnter(e);
}
protected override void OnLeave(EventArgs e) {
this.BackColor = prevColor;
base.OnLeave(e);
}
private Color prevColor;
}
Now any MyTextBox you drop on the form will have this behavior without having to implement events. Although there's certainly nothing wrong with using events.
Create a class (eg. ControlColorizer) and in its constructor pass:
1) The backcolor for the 'active control' and save to a internal Color variable
2) a variable length Control array
In the contructor add the same event handler for OnEnter and OnLeave on each control
In the OnEnter event set the backcolor
In the OnLeave event set the standard background color
The advantage is all in the use of the class:
1) Declare a global instance in your form class
2) Initialize in the form contructor after the InitializeComponent.
3) Forget everything else. No other code required
So let me explain everything with code:
This will go in a file called ControlColorizer.cs
public class ControlColorizer
{
private Color _setBColor = SystemColors.Window;
public ControlColor(Color bkg, params Control[] ctls)
{
_setBColor = bkg;
foreach (Control o in ctls)
{
o.Enter += new EventHandler(o_Enter);
o.Leave += new EventHandler(o_Leave);
}
}
private void o_Enter(object sender, EventArgs e)
{
if (sender is Control)
{
Control c = (Control)sender;
c.BackColor = _setBColor;
}
}
private void o_Leave(object sender, EventArgs e)
{
Control c = sender as Control;
c.BackColor = SystemColors.Window;
}
Now, in every form contructor where you need the functionality you have this
ControlColirizer _ccz;
public Form1()
{
InitializeComponent();
// Create an instance of ControlColorizer, pass the background color
// the list of Controls and that's all
_ccz = new ControlColorizer(Color.LightYellow, this.TextBox1,
this.TextBox2, this.TextBox3, this.TextBox4);
}

Categories