ShowDialog, PropertyGrid and Timer problem - c#

I have a strange bug, please, let me know if you have any clues about the reason.
I have a Timer (System.Windows.Forms.Timer) on my main form, which fires some updates, which also eventually update the main form UI. Then I have an editor, which is opened from the main form using the ShowDialog() method. On this editor I have a PropertyGrid (System.Windows.Forms.PropertyGrid).
I am unable to reproduce it everytime, but pretty often, when I use dropdowns on that property grid in editor it gets stuck, that is OK/Cancel buttons don't close the form, property grid becomes not usable, Close button in the form header doesn't work.
There are no exceptions in the background, and if I break the process I see that the app is doing some calculations related to the updates I mentioned in the beginning.
What can you recommend? Any ideas are welcome.

What's happening is that the thread timer's Tick method doesn't execute on a different thread, so it's locking everything else until it's done. I made a test winforms app that had a timer and 2 buttons on it whose events did this:
private void timer1_Tick(object sender, EventArgs e)
{
Thread.Sleep(6000);
}
private void button1_Click(object sender, EventArgs e)
{
timer1.Start();
}
private void button2_Click(object sender, EventArgs e)
{
frmShow show = new frmShow();
show.ShowDialog(); // frmShow just has some controls on it to fiddle with
}
and indeed it blocked as you described. The following solved it:
private void timer1_Tick(object sender, EventArgs e)
{
ThreadPool.QueueUserWorkItem(DoStuff);
}
private void DoStuff(object something)
{
Thread.Sleep(6000);
}

Related

How to detect if a window is closed C#, Windows Form App?

So, I'm making a payroll management system as a hobby project to help my resume and general knowledge of c#. So, I'm making a UI and I can open a new window just fine with this code:
private void button1_Click(object sender, EventArgs e)
{
CreateAdminAcct createAcct = new CreateAdminAcct();
createAcct.StartPosition = FormStartPosition.CenterScreen;
createAcct.Show();
this.Hide();
}
however, I don't know the event to check when the little red "x" button is clicked, because when that button is clicked, I want to go back to the main screen because I hide the main screen when that button is clicked, and when i click the red "x" on the screen that just opened, it closes, but the application continues to run in the background.
If there is some better way to manage multiple menus, I'm open to suggestions, however, this is what I've found easiest.
Thanks in advance
I second Robert Harvey's suggestion; this gives the user the reassurance tha tht emain window is still open/ nothing got lost, but it's unreachably "behind" the CreateAdminAcct form while the CreateAdminAcct form is open
private void button1_Click(object sender, EventArgs e)
{
CreateAdminAcct createAcct = new CreateAdminAcct();
createAcct.StartPosition = FormStartPosition.CenterScreen;
createAcct.ShowDialog();
//do any code here that needs to access createAcct before it's lost
MessageBox.Show(createAcct.NewAdmin.Name);
}
If you really do want to hide your main form, pass the main form itself to createAcct, and make it createAcct's job to re-open the main form when it is closing
private void button1_Click(object sender, EventArgs e)
{
CreateAdminAcct createAcct = new CreateAdminAcct(this); //note passing this form to constructor
createAcct.StartPosition = FormStartPosition.CenterScreen;
createAcct.Show();
}
class CreateAcctForm : Form{
private Form _showWhenClosing;
CreateAcctForm(Form revertTo){
InitializeComponent();
_showWhenClosing = revertTo;
}
}
void Form_Closing(object sender, ...){ //event
_showWhenClosing.Show();
}
Side note: please rename your controls after you drop them ona form. code that's stuffed with label57, textbox25 is effectively obfuscated and really wearisome to follow

C# Form - MouseClick will click the actual control after losing form focus

If you ever remove focus from any professional application like Chrome/FireFox/Visual Studio, and then reclick a button/menu item, it will actually click it as if you never lost focus.
How can I apply the same concept in C# WinForm? I tried many things like
private void form1_MouseClick(object sender, MouseEventArgs e)
{
BringToFront();
Activate();
}
Activate/focus/select/etc... nothing worked to react the same way, it always takes 3-4 clicks to actually click on a menu!
I thought about making a click event for every single control, but that seemed rather redundant.
Check this for example (Yellow Clicks)
You are right about Menues taking an extra click to get focus.
Which is extra annoying since the menue get highlighted anyway but doesn't react to the 1st click..
You can avoid that by coding the MouseEnter event:
private void menuStrip1_MouseEnter(object sender, EventArgs e)
{
// either
menuStrip1.Focus();
// or
this.Focus();
}
The downside of this is, that it is stealing focus from other applications, which is not something a well-behaved application should do..
So I think it is better to wait for a definitive user action; code the MouseDown event in a similar way..:
private void menuStrip1_MouseDown(object sender, MouseEventArgs e)
{
menuStrip1.Focus();
}
Or use the event that was made for the occasion:
private void menuStrip1_MenuActivate(object sender, EventArgs e)
{
menuStrip1.Focus();
}
I can't confirm a similar problem with Buttons or any other controls, though.
I have find trick to solve your problem. it work for me 100%
See this code:
dynamic elem1;
private void menuStrip1_MouseEnter(object sender, EventArgs e)
{
elem1 = sender;
}
private void menuStrip1_MouseLeave(object sender, EventArgs e)
{
elem1 = null;
}
private void Form1_Activated(object sender, EventArgs e)
{
if(elem1 != null){
elem1.PerformClick();
if (elem1.GetType().ToString() == "System.Windows.Forms.ToolStripMenuItem") elem1.ShowDropDown();
}
elem1 = null;
}
Here what happend.
When mouse enter button/menu item elem1 = this button/menu, and when mouse leave it set back to null.
so when form Activated we can call elem1.PerformClick() to click the button/menu item.

StatusLabel - how to reset, or allow status text to time out or fade away

I have a tabbed form with a StatusStrip at the bottom, which includes a StatusLabel. I want to use this status label for various actions ("1 record updated" etc). It is simple enough to create specific events to set the label's text property.
But how best to reset the status to blank? The user could perform any number of other operations where the status is no longer meaningful (going to another tab, clicking other buttons etc.).
It is not feasible to create all the possible events to reset the status message. Is there a way to incorporate some type of timer so that the message fades out after several seconds? Has anyone else found a good solution for this?
Is it truly important to clear the status though? There are plenty of products which will keep their status label unchanged until the next status event occurs. Visual Studio is a good example of this. It may be worth simplifying your scenario and taking this approach.
If you do want to clear the status after an event I think the most maintainable way to do this is with a Timer. Essentially clear after a few seconds when the status is set
Timer m_timer;
void SetStatus(string text) {
m_statusLabel.Text = text;
m_timer.Reset();
}
void OnTimerTick(object sender, EventArgs e) {
m_statusLabel.Text = "";
m_timer.Stop();
}
Yes a timer would work for this to clear it. Here is an example of one I've knocked together.
public partial class Form1 : Form
{
private System.Timers.Timer _systemTimer = null;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
_systemTimer = new System.Timers.Timer(500);
_systemTimer.Elapsed += _systemTimer_Elapsed;
}
void _systemTimer_Elapsed(object sender, ElapsedEventArgs e)
{
toolStripStatusLabel1.Text = string.Empty;
_systemTimer.Stop(); // stop it if you don't want it repeating
}
private void button1_Click(object sender, EventArgs e)
{
toolStripStatusLabel1.Text = "random text just as an example";
}
private void button2_Click(object sender, EventArgs e)
{
_systemTimer.Start();
}
}
Assume button1 is your action to update the status, and button2 is just a random way to start the timer (this can be however you want to start it, I've only used another button click as an example). After the set amount of time passes the status label will be cleared.

Defining a MouseEventHandler in C#

I'm trying to define MouseEventHandlers such that the application will exit whenever the mouse is clicked or moved or whenever a key is pressed. This is my first time using C#, but based on what I found online, I've written the code as follows:
MouseDown += new MouseEventHandler(mouseClickedResponse);
MouseMove += new MouseEventHandler(mouseMovedResponse);
KeyDown += new KeyEventHandler(keyResponse);
which connects to:
private void keyResponse(object sender, EventArgs e)
{
Application.Exit();
}
private void mouseClickedResponse(object sender, EventArgs e)
{
Application.Exit();
}
private void mouseMovedResponse(object sender, EventArgs e)
{
if (firstCall) //Keeps the application from exiting immediately
firstCall = false;
else Application.Exit();
}
The problem that I'm finding is that while the KeyEventHandler works perfectly, I can move and click the mouse as much as I want to no avail.
This is the sum total of the code that I've written to allow for user control; am I missing something?
On the surface, everything looks good with your code.
One possibility - The MouseEventHandler is defined in both the System.Windows.Input (MSDN) namespace as well as the System.Windows.Forms namespace (MSDN).
I believe the one you want is the one in the Forms namespace. Is it possible that you're using the one from the Input namespace instead?
I fixed my problem--my Form was filled with Panels, and by moving the code for mouse input over to the panels, everything worked instantly.
Change:
private void mouseClickedResponse(object sender, EventArgs e)
to:
private void mouseClickedResponse(object sender, MouseEventArgs e)
It should now work fine.

Is The reuse of the backgroundworker object possible?

I have a button "refresh" which every time i click on it i want my backgroundworker object to work.
i use
if (main_news_back_worker.IsBusy != true)
{
// Start the asynchronous operation.
main_news_back_worker.RunWorkerAsync();
}
private void main_news_back_worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
show_system_urls(urls);
displayNewMes(newMes, newStock, newSource);
displayOldMes(oldMes, oldStock);
}
The first time i use the backgroundworker it work good and also get to the RunWorkerCompleted and do his work.
But the second time i try to run the object the is_busy property of the object is 'true' and i cant run the object again...
Do i need to create a new backgroundworker every time i want to run it? how do i do it?
Thanks.
Yes, no problem. You will however have to make sure that the user cannot click the button again while the BGW is busy. Easily done by setting the Enabled property, stops the button action and provides excellent visual feedback to the user. Try this for example:
private void button1_Click(object sender, EventArgs e) {
button1.Enabled = false;
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
System.Threading.Thread.Sleep(2000);
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
button1.Enabled = true;
}
But the second time i try to run the object the is_busy property of the object is 'true'
That means the first background action is still running.
You will first have to decide if you want 2 of these actions to be going on at the same time.
If No, implement Cancellation so that you can Stop (and then restart) the Bgw.
If Yes, create a new Bgw each time.
And while you can re-use a Bgw, and that makes sense in the 1st scenario, there is no great saving in doing so. The Bgw Thread comes from the ThreadPool and will be re-used anyway.

Categories