I want to change wpf controls status after click button start.
The picture is what I want.
Following is my code
private bool _bWorking = false;
public delegate void UpdateStatusDelegate();
private void SetStatus(bool bEnable)
{
if (bEnable)
{
tbName.IsReadOnly = false;
barStatus.Visibility = Visibility.Hidden;
btnStart.IsEnabled = true;
btnStop.IsEnabled = false;
btnClose.IsEnabled = true;
}
else
{
tbName.IsReadOnly = true;
barStatus.Visibility = Visibility.Visible;
btnStart.IsEnabled = false;
btnStop.IsEnabled = true;
btnClose.IsEnabled = false;
}
}
internal void UpdateStatus()
{
SetStatus(true);
_bWorking = false;
}
private void ThreadFunc()
{
//for (; ; )
//{
// // do something here
// if (_bWorking == false)
// break;
//}
Thread.Sleep(500);
this.Dispatcher.Invoke(new UpdateStatusDelegate(UpdateStatus));
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
_bWorking = true;
SetStatus(false);
this.UpdateLayout();//this.InvalidateVisual();
try
{
Thread t = new Thread(new ThreadStart(() =>
{
ThreadFunc();
}));
t.IsBackground = true;
t.Name = "test status";
t.Start();
while (t.IsAlive)
{
// wait thread exit
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void btnStop_Click(object sender, RoutedEventArgs e)
{
_bWorking = false;
SetStatus(true);
}
But actually after I click button start, the UI seems frozen, and the thread exited, the UI become normal.
my VS is VS2010.
Force a WPF control to refresh?
this post is not work for me.
edit summary:
add delegate void UpdateStatusDelegate() and function UpdateStatus() to my code
// wait thread exit
The whole point of a background thread is to not wait for it by blocking your UI thread. Don't.
Instead, have the thread notify your UI when it's done.
Try to use BackgroundWorker component it might help you. For more information check the MSDN article
workable code
public delegate void UpdateStatusDelegate();
private bool _bWorking = false;
private void SetStatus(bool bEnable)
{
if (bEnable)
{
tbName.IsReadOnly = false;
barStatus.Visibility = Visibility.Hidden;
btnStart.IsEnabled = true;
btnStop.IsEnabled = false;
btnClose.IsEnabled = true;
}
else
{
tbName.IsReadOnly = true;
barStatus.Visibility = Visibility.Visible;
btnStart.IsEnabled = false;
btnStop.IsEnabled = true;
btnClose.IsEnabled = false;
}
}
internal void UpdateStatus()
{
SetStatus(true);
}
private void ThreadFunc()
{
try
{
// use Stopwatch to simulate jobs
var watch = Stopwatch.StartNew();
for (; ; )
{
var elapsedMs = watch.ElapsedMilliseconds;
if (elapsedMs > 10000 // 10 seconds
|| _bWorking == false)
{
break;
}
}
watch.Stop();
this.Dispatcher.Invoke(new UpdateStatusDelegate(UpdateStatus));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
_bWorking = false;
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
try
{
_bWorking = true;
SetStatus(false);
Thread t = new Thread(new ThreadStart(() =>
{
ThreadFunc();
}));
t.IsBackground = true;
t.Name = "test status";
t.Start();
//while (t.IsAlive)
{
// wait thread exit
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void btnStop_Click(object sender, RoutedEventArgs e)
{
_bWorking = false;
SetStatus(true);
}
Related
I have a button that starts two threads
private void CrawdBtn_Click(object sender, EventArgs e)
{
CrawdBtn.Enabled = false;
t = new Thread(AddLinksToList);
b = new Thread(EnqueueFromList);
t.Start();
b.Start();
}
and there are another buttons to pause, Resume, Stop those threads
My question is how can I disable (pause, Resume, Stop) buttons while the threads are working and re enable Crawl after the threads finished
Here is how you could start a Thread and have a way to await its completion:
public static Thread CreateAwaitableThread(Action action, out Task threadCompletion)
{
var tcs = new TaskCompletionSource<bool>();
threadCompletion = tcs.Task;
return new Thread(() =>
{
try
{
action();
}
finally
{
tcs.SetResult(true);
}
});
}
This method returns the newly created Thread, and also a Task that will be completed when the Thread is completed. You could use it like this:
private async void CrawdBtn_Click(object sender, EventArgs e)
{
CrawdBtn.Enabled = false;
Thread t1 = CreateAwaitableThread(AddLinksToList, out var t1c);
Thread t2 = CreateAwaitableThread(EnqueueFromList, out var t2c);
t1.Start();
t2.Start();
await Task.WhenAll(t1c, t2c);
CrawdBtn.Enabled = true;
}
In case of an exception the error will not be propagated through the Task. It is assumed that the delegates already include error handling logic. If not, an unhandled exception will occur as usual.
To solve your problem you can make a thread to check the ThreadState of thread t and thread b
private void btnstart_Click(object sender, EventArgs e)
{
t = new Thread(AddLinksToList);
b = new Thread(EnqueueFromList);
t.Start();
b.Start();
if (threadchecker == null)//this if determines Whether it's the first time or not
{
threadchecker = new Thread(() => ChekingStateOfThreads());
threadchecker.IsBackground = true;
threadchecker.Start();
}
}
Since the thread wants to check the ThreadState of those threads it should always run.
and This is ChekingStateOfThreads
private void ChekingStateOfThreads()
{
while (true)
{
Thread.Sleep(1000);
if (t.ThreadState == ThreadState.Stopped)
{
this.Invoke(new Action(() =>
{
btnpause.Enabled = btnstart.Enabled = false;
btnresume.Enabled = btnstop.Enabled = true;
}));
}
else if (t.ThreadState == ThreadState.Running)
{
this.Invoke(new Action(() =>
{
btnstart.Enabled = btnresume.Enabled = false;
btnpause.Enabled = btnstop.Enabled = true;
}));
}
else if (t.ThreadState == ThreadState.Aborted)
{
this.Invoke(new Action(() =>
{
btnstart.Enabled = true;
btnpause.Enabled = btnresume.Enabled = btnstop.Enabled = false;
}));
}
else if (t.ThreadState == ThreadState.Suspended)
{
this.Invoke(new Action(() =>
{
btnpause.Enabled = btnstart.Enabled = false;
btnresume.Enabled = btnstop.Enabled = true;
}));
}
}
}
The concept of function is pretty simple. Each 1 second the thread is check the state of thread t.
See Why use Invoke on Controls in .net? to figure out why should we use INVOKE.
To abort the threadchecker just use the Form_closing Event
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
threadchecker.Abort();
}
This is a bit difficult to explain. I have a thread instantiated within a WindowsForm, the thread is not a priority and does its job if nobody has taken the resource exclusively using Monitor.TryEnter.
But when closing the form I use the FormClosing event to stop the processing, save the processed data before closing, etc. When trying to take the resource with another Monitor.Enter .. the block is produced.
But the funny thing is that in the following way it works!!. Using a new/secondary thread or task to block the internal thread and perform the final job of save buffering.
"Minimal, Complete, and Verifiable example:"
namespace WindowsFormsApplication3
{
using System;
using System.Drawing;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
public partial class Form1 : Form
{
private readonly object _syncRoot = new object();
private volatile bool _working = false;
readonly Label lblInfo = new Label();
public Form1()
{
InitializeComponent();
SuspendLayout();
lblInfo.Text = "...";
lblInfo.Location = new Point(0, 0);
Controls.Add(lblInfo);
CheckBox chkBox = new CheckBox();
chkBox.Text = "Use Task inside FormClosing";
chkBox.AutoSize = true;
chkBox.Location = new Point(Width / 2 - chkBox.Width / 2, Height / 2 + chkBox.Height);
chkBox.Click += ChkBox_Click; ;
Controls.Add(chkBox);
ResumeLayout(false);
FormClosing += Form1_FormClosing;
}
private CancellationTokenSource _cancellationTokenS;
private bool _flUseThread = false;
private void Form1_Load(object sender, EventArgs e)
{
_cancellationTokenS = new CancellationTokenSource();
CancellationToken token = _cancellationTokenS.Token;
_working = true;
Task.Run(() =>
{
while (_working)
{
if (token.IsCancellationRequested)
break;
processData(DateTime.Now);
}
}, token);
}
private void ChkBox_Click(object sender, EventArgs e)
{
_flUseThread = ((CheckBox)sender).Checked;
}
private bool _alreadySafedClose = false;
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (_flUseThread)
{
if (_alreadySafedClose) return;
if (MessageBox.Show(this,
"Do you really want to cancel?", "Question:",
MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
e.Cancel = true; // Avoid close form.
Enabled = false; // Avoid interact with form.
Task.Factory.StartNew(() =>
{
bool lockWasTaken = false;
try
{
_working = false;
// Lock other thread.
Monitor.Enter(_syncRoot, ref lockWasTaken);
_cancellationTokenS.Cancel();
saveBufferResults();
MessageBox.Show("OK with Thread");
}
catch (Exception)
{
// ignored
}
finally
{
// Release lock other thread if already locked.
if (lockWasTaken)
Monitor.Exit(_syncRoot);
Invoke((MethodInvoker)delegate
{
_alreadySafedClose = true;
Close(); //Finally Close.
});
}
});
}
else
e.Cancel = true;
}
else
{
if (MessageBox.Show(this,
"Do you really want to cancel?", "Question:",
MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
bool lockWasTaken = false;
try
{
_working = false;
_cancellationTokenS.Cancel();
// Lock other thread avoid continue processing.
Monitor.Enter(_syncRoot, ref lockWasTaken); // <------ here it stays blocked!!
saveBufferResults();
MessageBox.Show("OK");
}
catch (Exception)
{
Console.WriteLine("<DEBUG> The data could not be processed.!");
}
finally
{
// Release lock other thread if already locked.
if (lockWasTaken)
Monitor.Exit(_syncRoot);
}
}
else
e.Cancel = true;
}
}
private void processData(DateTime data)
{
//flag to reduce locks..
if (!_working) return;
if (Monitor.TryEnter(_syncRoot))
{
try
{
//To escape quickly
if (_working)
innerProcessFrame(data);
}
catch (Exception)
{
Console.WriteLine("<DEBUG> The data could not be processed.!");
}
finally
{
Monitor.Exit(_syncRoot);
}
}
}
private ulong _dataCounter = 0;
private void innerProcessFrame(DateTime data)
{
Thread.Sleep(100);
Invoke((MethodInvoker)delegate
{
lblInfo.Text = data.Millisecond + " " + _dataCounter;
});
_dataCounter++;
}
private void saveBufferResults()
{
Thread.Sleep(1);
}
}
}
I believe that FormClosing is breaking the synchronization context or blocking the thread of work and
therefore in the event of the thread of work it never reaches the line where it releases the resource
using Monitor.Exit, in the processData(...) method:
My question is why does this happen only when closing the form within the FormClosing event?, because
if I put a button on the form and put the same blocking code everything stops correctly there is no DeadLock, and why does it work creating a secondary thread to do the final work when closing the form ?, and finally if someone knows a better or correct way to solve it.
I have an application where user can click on a Scan button to scan the image to preview in the application. When user clicks, usually a "Preparing to scan" message will be shown and goes away when the scan is 100% complete.
The scan works fine. The problem if I stress test it by pressing the scan button many times while it's doing it's work, the application completely hangs and the message just stays there and I had to restart my whole application.
The code: It's just a small section
private void ScanStripButton_Click(object sender, EventArgs e)
{
if (SCAN_INTO_BATCH)
{
GENERATE_BATCH_FOLDER = true;
StartTwainScan();
}
}
Any idea on how to prevent this issue?
Appreciate the help
EDIT:
public void StartTwainScan()
{
Boolean EnableUI = false;
Boolean ADF = false;
Boolean EnableDuplex = false;
if (Properties.Settings.Default.TwainShow.Equals("1"))
{
EnableUI = true;
}
if (Properties.Settings.Default.ScanType.Equals("2"))
{
ADF = true;
}
if (Properties.Settings.Default.DuplexEnable.Equals("1"))
{
EnableDuplex = true;
}
var rs = new ResolutionSettings
{
Dpi = GetResolution(),
ColourSetting = GetColorType()
};
var pg = new PageSettings()
{
Size = GetPageSize()
};
var settings = new ScanSettings
{
UseDocumentFeeder = ADF,
ShowTwainUI = EnableUI,
ShowProgressIndicatorUI = true,
UseDuplex = EnableDuplex,
Resolution = rs,
Page = pg
};
try
{
TwainHandler.StartScanning(settings);
}
catch (TwainException ex)
{
MessageBox.Show(ex.Message);
//Enabled = true;
//BringToFront();
}
}
This isn't going to be the correct answer, but you haven't shown enough code to give you the right code. It should point you in the right direction.
private void ScanStripButton_Click(object sender, EventArgs e)
{
ScanStripButton.Enabled = false;
if (SCAN_INTO_BATCH)
{
GENERATE_BATCH_FOLDER = true;
StartTwainScan();
}
ScanStripButton.Enabled = true;
}
Basically you disable the button when the scan starts and enable it when it finishes.
private async void ScanStripButton_Click(object sender, EventArgs e)
{
await Task.Run(() =>
{
if (SCAN_INTO_BATCH)
{
GENERATE_BATCH_FOLDER = true;
StartTwainScan();
}
});
}
or
private bool clicked = false;
private void ScanStripButton_Click(object sender, EventArgs e)
{
try
{
if(clicked)
return;
clicked = true;
if (SCAN_INTO_BATCH)
{
GENERATE_BATCH_FOLDER = true;
StartTwainScan();
}
}
finally
{
clicked = false;
}
}
As title, how to release thread is required in multiple thread ?
Ex : I have 5 thread is waiting. I only want thread position 3 is released
I use autoresetevent/manualresetevent/monitor.wait and monitor.pulse but all release thread follow FIFO
help me !!!
UPDATED:
This is form1:
private BackgroundWorker[] threadArray;
public static ManualResetEvent _manualResetEvent = new ManualResetEvent(false);
private void btn_Start_Scraping_Click(object sender, EventArgs e)
{
threadArray = new BackgroundWorker[listView_Site.Items.Count];
for (var f = 0; f < listView_Site.Items.Count; f++)
{
threadArray[f] = new BackgroundWorker();
threadArray[f].DoWork += new DoWorkEventHandler(BackgroundWorkerFilesDoWork);
threadArray[f].RunWorkerCompleted += new RunWorkerCompletedEventHandler(BackgroundWorkerFilesRunWorkerCompleted);
threadArray[f].ProgressChanged += new ProgressChangedEventHandler(BackgroundWorkerFilesProgressChanged);
threadArray[f].WorkerReportsProgress = true;
threadArray[f].WorkerSupportsCancellation = true;
threadArray[f].RunWorkerAsync(listView_Site.Items[f].Tag.ToString());
}
}
private void BackgroundWorkerFilesDoWork(object sender, DoWorkEventArgs e)
{
....// all above code is fine
requestCaptcha = (HttpWebRequest)WebRequest.Create(uriCaptchaPage);
requestCaptcha.Pipelined = true;
requestCaptcha.KeepAlive = true;
requestCaptcha.AllowAutoRedirect = false;
//request.Proxy = null;
requestCaptcha.Timeout = 60000;
requestCaptcha.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip,deflate");
requestCaptcha.CookieContainer = sessionID;
request.ServicePoint.Expect100Continue = false;
requestCaptcha.Method = "GET";
requestCaptcha.Referer = uriloginPage.AbsoluteUri;
//get response.
responseCaptcha = (HttpWebResponse)requestCaptcha.GetResponse();
Stream imagestream = responseCaptcha.GetResponseStream();
if (imagestream != null)
{
Image image = Image.FromStream(imagestream);
if (Directory.Exists(Application.StartupPath + "\\Captcha") == false)
{
Directory.CreateDirectory(Application.StartupPath + "\\Captcha");
}
switch (responseCaptcha.ContentType)
{
case "image/jpeg":
{
saveLocation += ".jpg";
if (File.Exists(saveLocation))
{
File.Delete(saveLocation);
}
image.Save(saveLocation,ImageFormat.Jpeg);
break;
}
case "image/gif":
{
saveLocation += ".gif";
if (File.Exists(saveLocation))
{
File.Delete(saveLocation);
}
image.Save(saveLocation, ImageFormat.Gif);
break;
}
case "image/png":
{
saveLocation += ".png";
if (File.Exists(saveLocation))
{
File.Delete(saveLocation);
}
image.Save(saveLocation, ImageFormat.Png);
break;
}
}
//show form2 to enter captcha
lock (_lockObj)
{
if (Application.OpenForms.OfType<frmCaptchaQuestion>().Any() == false)
{
DoOnUIThread(delegate()
{
_formCaptchaQuestion.CreatePanelCaptcha(uriloginPage, saveLocation,idHomePage);
_formCaptchaQuestion.Show();
});
}
else
{
DoOnUIThread(() => _formCaptchaQuestion.CreatePanelCaptcha(uriloginPage, saveLocation,idHomePage));
}
}
//wait and get captcha from form2 and only run thread is required
//this is my problem <<<<========================================
lock (_lockObj)
{
//_manualResetEvent.WaitOne(30000);
//_manualResetEvent.Reset();
//if (clsValueStatic.CaptchaText != null)
//{
// foreach (var id in clsValueStatic.CaptchaText)
// {
while (!_go)
{
Monitor.Wait(_lockObj);
}
// }
//}
}
requestCaptcha = (HttpWebRequest)WebRequest.Create(uriActionLoginPage);
requestCaptcha.Pipelined = true;
requestCaptcha.KeepAlive = true;
requestCaptcha.AllowAutoRedirect = false;
//request.Proxy = null;
requestCaptcha.Timeout = 60000;
requestCaptcha.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip,deflate");
requestCaptcha.CookieContainer = sessionID;
request.ServicePoint.Expect100Continue = false;
requestCaptcha.Method = "GET";
}
Form2:
private void textBoxCaptcha_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
var textBox = sender as TextBoxX;
if (textBox != null)
{
clsValueStatic.CaptchaText = textBox.Text.Trim();
textBox.Parent.Parent.Dispose();
frmScrapingAnalysis._manualResetEvent.Set();
}
}
}
PS : Form1 have 1 button to start multiple backgroundworker and show form2 then all backgroundworker wait to get text captcha of textbox from form2
my way want when user enter text of backgroundworker is shown on form2 then only the backgroundworker is released. All other backgroundworker still wait
I'm creating FileWatcher and I'm adding watcher_Changed method as event.
After some time it crashes in catch section as NullReferenceObject (IIS Express error).
private static void watcher_Changed(object sender, FileSystemEventArgs e)
{
try
{
fWatcher.EnableRaisingEvents = false;
System.Threading.Thread.Sleep(300);
var newLoc = ReadLines();
var context = GlobalHost.ConnectionManager.GetHubContext<MyHub1>();
//Do something
fWatcher.EnableRaisingEvents = true;
}
catch(Exception err)
{
//Error appears here
fWatcher.EnableRaisingEvents = false;
fWatcher = null;
fWatcher.Dispose();
fWatcher = new FileSystemWatcher();
while (fWatcher.EnableRaisingEvents != true)
{
try
{
fWatcher.EnableRaisingEvents = true;
}
catch(Exception exc)
{
System.Threading.Thread.Sleep(100);
}
}
}
}
How can I fix it?
fWatcher = null; //You cannot do this "fWatcher." after this line
//(Until you call fWatcher = new FileSystemWatcher() at least)
fWatcher.Dispose(); //You cannot do this !!!!!
If you want to dispose it ,call Dispose() First and then create a new one.
You cannot call a method on an object that is null