I have a Windows Mobile 6.5 App under development. When the user opens the App a dialog box appears with the login form. User logs in and then after 30 seconds (small time in production) when the timer has run out without activity I show the login Dialog box again using events:
static private void _TimerTick(object state)
{
// the user has been inactive for 30 secs; log him out
MainForm.timer = null;
using (LoginForm LoginForm = new LoginForm())
{
if (LoginForm.ShowDialog() == DialogResult.OK)
{
MainForm.timer = new System.Threading.Timer(_TimerTick, null, 1000 * 30 * 1, Timeout.Infinite);
}
else
{
Application.Exit();
}
}
}
But once I press login and return with an ok button from the login form the original form does show. Although it is still in the task manager. I have tried:
.TopMost = true; but then I can't assess the windows button in the bar at the bottom of the app and no other apps can run as form in my app is always in front of it.
A simple solution as this is only for the login and one main form:
LoginForm.cs (with two textboxes and main menu with Login and Exit):
using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace LoginFormTest
{
public partial class LoginForm : Form
{
public LoginForm()
{
InitializeComponent();
}
public void doShow(bool bShow)
{
if(bShow)
Invoke(new Action(() => this.Show()));
else
Invoke(new Action(() => this.Hide()));
}
public void doClose()
{
Invoke(new Action(() => this.Close()));
}
private void mnuLogin_Click(object sender, EventArgs e)
{
MainForm mainForm = new MainForm();
mainForm.Show();
System.Diagnostics.Debug.WriteLine("mainForm started");
}
}
}
Nothing special there.
The MainForm has code that will close the form if no activity:
using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace LoginFormTest
{
public partial class MainForm : Form
{
int countDown = 3; //number of seconds for timeout
System.Threading.Timer timer;
object lockCounter = new object(); //to sync access to counter var
public MainForm()
{
InitializeComponent();
//start a independent timer after 1000ms and with a 1000ms interval
timer = new System.Threading.Timer(new TimerCallback(this.timerCallback), null, 1000, 1000);
}
private void mnuExit_Click(object sender, EventArgs e)
{
doClose();
}
private void mnuLogout_Click(object sender, EventArgs e)
{
doClose();
}
private void doClose()
{
System.Diagnostics.Debug.WriteLine("mainForm closing");
try
{
timer.Dispose(); //else timer thread will continue running!
Invoke(new Action(() => this.Close()));
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("Exception in doClose(): " + ex.Message);
}
}
private void MainForm_MouseMove(object sender, MouseEventArgs e)
{
resetTimeout();
}
private void MainForm_KeyPress(object sender, KeyPressEventArgs e)
{
resetTimeout();
}
private void MainForm_Click(object sender, EventArgs e)
{
resetTimeout();
}
public void resetTimeout()
{
System.Diagnostics.Debug.WriteLine("resetTimeout()");
lock(lockCounter)
countDown = 3;
}
public void timerCallback(object stateInfo)
{
lock (lockCounter)
countDown--;
if (countDown == 0)
{
System.Diagnostics.Debug.WriteLine("timeout->doClose()");
doClose();
}
}
private void MainForm_Closed(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine("mainForm CLOSED");
}
}
}
Specials:
a lock object to sync access to the counter var
a threading timer that runs independent of the message pump
a delegate to by called from the TimerCallback function
Related
This question already has answers here:
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on
(22 answers)
Closed 5 years ago.
I use Cefsharp.Winform (http://cefsharp.github.io/).
I Try Form.Close() but it error:
System.InvalidOperationException: 'Cross-thread operation not valid: Control 'Form2' accessed from a thread other than the thread it was created on.'
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TEST_CEF
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Form2 frm2 = new Form2();
frm2.Show();
}
}
}
Form2.cs
using CefSharp;
using CefSharp.WinForms;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TEST_CEF
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
InitBrowser();
}
public ChromiumWebBrowser browser;
public void InitBrowser()
{
Cef.Initialize(new CefSettings());
browser = new ChromiumWebBrowser("www.google.com");
this.Controls.Add(browser);
browser.Dock = DockStyle.Fill;
browser.FrameLoadEnd += WebBrowserFrameLoadEnded;
}
private void WebBrowserFrameLoadEnded(object sender, FrameLoadEndEventArgs e)
{
if (e.Frame.IsMain)
{
if (browser.Address.IndexOf("google") > -1)
{
timer1.Start();
}
}
}
private void Form2_FormClosing(object sender, FormClosingEventArgs e)
{
browser.Dispose();
Cef.Shutdown();
}
int time = 0;
private void timer1_Tick(object sender, EventArgs e)
{
time++;
if (time==3)
{
this.Close();
}
}
}
}
Which kind of timer do you use?
Consider using InvokeRequired in the timer1_Tick method.
private void timer1_Tick(object sender, EventArgs e)
{
if (InvokeRequired) { Invoke(new Action(() => { timer1_Tick(sender, e); })); return; }
time++;
if (time==3)
{
this.Close();
}
}
From the docs (emphasis from me):
It's important to note this event is fired on a CEF UI thread, which by default is not the same as your application UI thread. It is unwise to block on this thread for any length of time as your browser will become unresponsive and/or hang.. To access UI elements you'll need to Invoke/Dispatch onto the UI Thread.
So you start the timer in another thread so I suppose the Tick event will be raised in this CEF UI thread, too.
So you must use Invoke if needed:
Action close = () => this.Close();
if (InvokeRequired)
Invoke(close);
else
close();
In the splash screen i'm using now just for the demo a timer.
But i want to use a class that using win32 classes and wait until all loops are finished. While it's looping to make the progressBar move until 100%.
In form1 i didn't make yet anything.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TestingHardware
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
}
}
In Program.cs i changed the Run:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TestingHardware
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Splash_Screen());
}
}
}
Then i added a new form for the splash screen:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TestingHardware
{
public partial class Splash_Screen : Form
{
Timer tmr;
public Splash_Screen()
{
InitializeComponent();
}
private void Splash_Screen_Load(object sender, EventArgs e)
{
}
private void Splash_Screen_Shown(object sender, EventArgs e)
{
tmr = new Timer();
//set time interval 3 sec
tmr.Interval = 3000;
//starts the timer
tmr.Start();
tmr.Tick += tmr_Tick;
}
void tmr_Tick(object sender, EventArgs e)
{
//after 3 sec stop the timer
tmr.Stop();
//display mainform
Form1 mf = new Form1();
mf.Show();
//hide this form
this.Hide();
}
}
}
In the splash screen form designer i added a progressBar.
And last the Form that get the hardware info. I want the splash screen and progressBar to shown while it's looping until the end.
using System;
using System.Collections;
using System.Management;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace GetHardwareInfo
{
public partial class frmMain : Form
{
public frmMain()
{
InitializeComponent();
cmbxOption.SelectedItem = "Win32_Processor";
}
private void InsertInfo(string Key, ref ListView lst, bool DontInsertNull)
{
lst.Items.Clear();
ManagementObjectSearcher searcher = new ManagementObjectSearcher("select * from " + Key);
try
{
foreach (ManagementObject share in searcher.Get())
{
ListViewGroup grp;
try
{
grp = lst.Groups.Add(share["Name"].ToString(), share["Name"].ToString());
}
catch
{
grp = lst.Groups.Add(share.ToString(), share.ToString());
}
if (share.Properties.Count <= 0)
{
MessageBox.Show("No Information Available", "No Info", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
foreach (PropertyData PC in share.Properties)
{
ListViewItem item = new ListViewItem(grp);
if (lst.Items.Count % 2 != 0)
item.BackColor = Color.White;
else
item.BackColor = Color.WhiteSmoke;
item.Text = PC.Name;
if (PC.Value != null && PC.Value.ToString() != "")
{
switch (PC.Value.GetType().ToString())
{
case "System.String[]":
string[] str = (string[])PC.Value;
string str2 = "";
foreach (string st in str)
str2 += st + " ";
item.SubItems.Add(str2);
break;
case "System.UInt16[]":
ushort[] shortData = (ushort[])PC.Value;
string tstr2 = "";
foreach (ushort st in shortData)
tstr2 += st.ToString() + " ";
item.SubItems.Add(tstr2);
break;
default:
item.SubItems.Add(PC.Value.ToString());
break;
}
}
else
{
if (!DontInsertNull)
item.SubItems.Add("No Information available");
else
continue;
}
lst.Items.Add(item);
}
}
}
catch (Exception exp)
{
MessageBox.Show("can't get data because of the followeing error \n" + exp.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
private void RemoveNullValue(ref ListView lst)
{
foreach (ListViewItem item in lst.Items)
if (item.SubItems[1].Text == "No Information available")
item.Remove();
}
#region Control events ...
private void cmbxNetwork_SelectedIndexChanged(object sender, EventArgs e)
{
InsertInfo(cmbxNetwork.SelectedItem.ToString(), ref lstNetwork, chkNetwork.Checked);
}
private void cmbxSystemInfo_SelectedIndexChanged(object sender, EventArgs e)
{
InsertInfo(cmbxSystemInfo.SelectedItem.ToString(), ref lstSystemInfo, chkSystemInfo.Checked);
}
private void cmbxUtility_SelectedIndexChanged(object sender, EventArgs e)
{
InsertInfo(cmbxUtility.SelectedItem.ToString(), ref lstUtility, chkUtility.Checked);
}
private void cmbxUserAccount_SelectedIndexChanged(object sender, EventArgs e)
{
InsertInfo(cmbxUserAccount.SelectedItem.ToString(), ref lstUserAccount, chkUserAccount.Checked);
}
private void cmbxStorage_SelectedIndexChanged(object sender, EventArgs e)
{
InsertInfo(cmbxStorage.SelectedItem.ToString(), ref lstStorage, chkDataStorage.Checked);
}
private void cmbxDeveloper_SelectedIndexChanged(object sender, EventArgs e)
{
InsertInfo(cmbxDeveloper.SelectedItem.ToString(), ref lstDeveloper, chkDeveloper.Checked);
}
private void cmbxMemory_SelectedIndexChanged(object sender, EventArgs e)
{
InsertInfo(cmbxMemory.SelectedItem.ToString(), ref lstMemory, chkMemory.Checked);
}
private void chkHardware_CheckedChanged(object sender, EventArgs e)
{
if (chkHardware.Checked)
RemoveNullValue(ref lstDisplayHardware);
else
InsertInfo(cmbxOption.SelectedItem.ToString(), ref lstDisplayHardware, chkHardware.Checked);
}
private void cmbxOption_SelectedIndexChanged(object sender, EventArgs e)
{
InsertInfo(cmbxOption.SelectedItem.ToString(), ref lstDisplayHardware, chkHardware.Checked);
}
private void chkDataStorage_CheckedChanged(object sender, EventArgs e)
{
if (chkDataStorage.Checked)
RemoveNullValue(ref lstStorage);
else
InsertInfo(cmbxStorage.SelectedItem.ToString(), ref lstStorage, chkDataStorage.Checked);
}
private void chkMemory_CheckedChanged(object sender, EventArgs e)
{
if (chkMemory.Checked)
RemoveNullValue(ref lstMemory);
else
InsertInfo(cmbxMemory.SelectedItem.ToString(), ref lstStorage, false);
}
private void chkSystemInfo_CheckedChanged(object sender, EventArgs e)
{
if (chkSystemInfo.Checked)
RemoveNullValue(ref lstSystemInfo);
else
InsertInfo(cmbxSystemInfo.SelectedItem.ToString(), ref lstSystemInfo, false);
}
private void chkNetwork_CheckedChanged(object sender, EventArgs e)
{
if (chkNetwork.Checked)
RemoveNullValue(ref lstNetwork);
else
InsertInfo(cmbxNetwork.SelectedItem.ToString(), ref lstNetwork, false);
}
private void chkUserAccount_CheckedChanged(object sender, EventArgs e)
{
if (chkUserAccount.Checked)
RemoveNullValue(ref lstUserAccount);
else
InsertInfo(cmbxUserAccount.SelectedItem.ToString(), ref lstUserAccount, false);
}
private void chkDeveloper_CheckedChanged(object sender, EventArgs e)
{
if (chkDeveloper.Checked)
RemoveNullValue(ref lstDeveloper);
else
InsertInfo(cmbxDeveloper.SelectedItem.ToString(), ref lstDeveloper, false);
}
private void chkUtility_CheckedChanged(object sender, EventArgs e)
{
if (chkUtility.Checked)
RemoveNullValue(ref lstUtility);
else
InsertInfo(cmbxUtility.SelectedItem.ToString(), ref lstUtility, false);
}
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
linkLabel1.LinkVisited = true;
System.Diagnostics.Process.Start("http://www.ShiraziOnline.net");
}
#endregion
}
}
It's using win32 classes and instead adding it to a ListView i want to add each part to a List. Then i want to use the Lists in form1 when it's finishing all loops and add the Lists to controls. Or maybe i should load it in form1 already ?
Not sure.
If searcher has a Count property, use that for your maximum value. Determine current percentage with:
int count = 0;
try
{
foreach (ManagementObject share in searcher.Get())
{
count++;
progressbar1.Value = count * 100 / Math.Max(1, searcher.Count);
Also, make sure that you are setting your ListView to suspend updates using .SuspendLayout() before adding the items and .ResumeLayout() afterwards as constantly updating your ListView is very slow.
Another recommendation would be to place all your ListViewItems into an List/Array and then just update your ListView once.
Create a List at the start just after the Clear()
lst.SuspendLayout();
lst.Items.Clear()
System.Collections.Generic.List<ListViewItem> listViewItems = new System.Collections.Generic.List<ListViewItem>();
Then where you were adding the items before add them to the List<>
listViewItems.Add(item);
And finally after all records have been processed add the List<> to your ListView and resume updates
try
{
// All your code here
lst.Items.AddRange(listViewItems.ToArray());
}
finally
{
lst.ResumeLayout();
}
This question already has answers here:
Thread.Sleep() in C#
(4 answers)
Closed 6 years ago.
Here's my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
namespace _8BB_2._0
{
public partial class Form1 : Form
{
public static class globalVars
{
public static bool spacerunning = false;
}
public Form1()
{
InitializeComponent();
globalVars.spacerunning = false;
}
private void button1_Click(object sender, EventArgs e)
{
if (!globalVars.spacerunning)
{
globalVars.spacerunning = true;
while (globalVars.spacerunning)
{
Thread.Sleep(1000);
SendKeys.Send(" ");
}
}
else if (globalVars.spacerunning)
{
globalVars.spacerunning = false;
}
}
}
}
When I click button1 it's starts hitting space every second like it should but when I try to click it again to shut it off the application freezes and it keeps pressing space. I've tried multiple other ways but can't seem to figure out how I can do two things at once since I get locked inside of the while loop.
Calling Thread.Sleep() will block the UI thread. Try to use async/await instead.
private async void button1_Click(object sender, EventArgs e)
{
globalVars.spacerunning = !globalVars.spacerunning;
while (globalVars.spacerunning)
{
await Task.Delay(1000);
SendKeys.Send(" ");
}
}
UPDATE:
You may use a Timer instead.
public class MainForm : Form
{
private Timer timer = new Timer() { Interval = 1000 };
public MainForm()
{
/* other initializations */
timer.Enabled = false;
timer.Tick += timer_Tick;
}
private void timer_Tick(object sender, EventArgs e)
{
SendKeys.Send(" ");
}
private void button1_Click(object sender, EventArgs e)
{
globalVars.spacerunning = !globalVars.spacerunning;
timer.Enabled = globalVars.spacerunning;
}
}
i have created a form1 with button1 to start a task, which will be processed on the background with BackGroundWorker on a separate class and display a ProgressBar on a separate window.
my problem is the process is working fine except that the task is done yet progressbar form hangs on the middle.
below is BackGroundLoading Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.ComponentModel;
namespace sample2
{
public class BackgroundLoading
{
public BackgroundWorker Bw;
public delegate void RunFunction();
public RunFunction thisFunction;
mycontrols.LoadProgress p = new mycontrols.LoadProgress();
public BackgroundLoading(RunFunction newFunction)
{
thisFunction = newFunction;
Bw = new BackgroundWorker();
Bw.WorkerReportsProgress = true;
Bw.DoWork += new DoWorkEventHandler(Bw_DoWork);
Bw.ProgressChanged += new ProgressChangedEventHandler(Bw_ProgressChanged);
Bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Bw_RunWorkerCompleted);
}
public void Start()
{
Bw.RunWorkerAsync();
}
void Bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show("Error: " + e.Error.Message);
}
if (e.Cancelled)
{
MessageBox.Show("Cancelled!");
}
else
{
MessageBox.Show("Completed!");
p.Close();
}
}
public void Bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
/// If I put
/// p.ProgBar.Value = e.ProgressPercentage;
/// the progressbar hangs upon form show.
}
void Bw_DoWork(object sender, DoWorkEventArgs e)
{
p.Show();
BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 1; (i <= 100); i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
if (thisFunction != null)
{
thisFunction();
worker.ReportProgress((i * 1));
p.ProgBar.Value = i;
}
else
{
MessageBox.Show("Error, no method found");
}
}
}
}
}
}
this is the Form1 code.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace sample2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
BackgroundLoading BL = new BackgroundLoading(workingmethod);
BL.Start();
}
private void workingmethod()
{
System.Threading.Thread.Sleep(10);
}
}
}
this is the code for the LoadProgress form
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace sample2.mycontrols
{
public partial class LoadProgress : Form
{
public LoadProgress()
{
InitializeComponent();
}
private void LoadProgress_Load(object sender, EventArgs e)
{
}
}
}
no code on the progressbar form since update is done on the class.
can't find the causing of hanging.
Thanks in Advance.
Show the form on the main UI thread, immediately prior to running your BackgroundWorker:
public void Start()
{
p.Show();
Bw.RunWorkerAsync();
}
Then you can make calls to the form in the ProgressChanged event, which runs on the UI thread.
Remove p.Show() and p.ProgBar.Value = i; from the DoWork event - you don't want to touch the UI thread from your background thread.
public void Bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
p.ProgBar.Value = e.ProgressPercentage;
}
Finally, close the form when the BackgroundWorker is complete (which you're already doing):
void Bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
p.Close();
}
Hey I have this test back ground worker which seems to get stuck on the DoWork method or maybe the RunWorkerCompleted is not being fired can you guys see anything wrong here?
Maybe I am not implementing this properly :/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using MailChimp;
using System.Threading;
using System.Runtime.InteropServices;
namespace Chimporter
{
public partial class Form1 : Form
{
//Worker thread flag set to false
static bool done = false;
//Console dll
[DllImport("Kernel32.dll")]
static extern Boolean AllocConsole();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
}
private void label1_Click(object sender, EventArgs e)
{
}
private void accountInformationToolStripMenuItem_Click(object sender, EventArgs e)
{
//loadWindow pleaseWait = new loadWindow();
//pleaseWait.Show();
BackgroundWorker bg = new BackgroundWorker();
bg.DoWork += new DoWorkEventHandler(bg_DoWork);
bg.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bg_RunWorkerCompleted);
bg.RunWorkerAsync();
while (!done)
{
//Console.WriteLine("Waiting in Main, tid " + Thread.CurrentThread.ManagedThreadId);
//Thread.Sleep(100);
}
//AccountInfo accInfo = new AccountInfo();
//accInfo.Show();
}
public void bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (!AllocConsole())
{
Console.WriteLine("Fnished! " + Thread.CurrentThread.ManagedThreadId);
}
done = true;
}
public void bg_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i <= 3; i++)
{
if (!AllocConsole())
{
Console.WriteLine("Work Line: " + i + ", tid " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(500);
}
}
//string key = "e42713458882f6c2c27b3d6d951174a2-us6";
//var mc = new MCApi(key, true);
//string user = mc.GetAccountDetails().Username.ToString();
return;
}
private void menuStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
//Exit application Form.Close()
Application.Exit();
}
}
}
Take your while (!done) out. It is locking up the main thread. The worker completed event gets raised on that thread, but since it is busy in a loop it will never get raised.
The whole point of the RunWorkerCompleted event is so that you get a notification on the main thread and you don't have to lock it up in a busy loop and make your gui unresponsive.