I was putting together a simple WinForm that would spawn a number of threads to loop from 0 to 10000 - the purpose of this - to slow down Windows to make some other programs run slow.
Basically the form has a text box that I want to write the loop index to from each thread. All was fine for a single thread, but since I have introduced more threads, I would appear to hang the application when I click on the Stop button - I am not too sure where to go from here.
My sample is probably not written well. I want to get a better understanding of multithreadding, deadlocks, etc. I have dabbled a little with BackgroundWorker in the past, but have been doing Java for the majority of the last 2+ years.
Form1.cs
public delegate void SetTextDelegate(string text);
public partial class Form1 : Form
{
private Thread[] _slow;
private object lockTextBox = new object();
public Form1()
{
InitializeComponent();
}
#region Event Handlers
private void ui_btnClose_Click(object sender, EventArgs e)
{
this.Close();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void ui_btnStart_Click(object sender, EventArgs e)
{
if (_slow != null)
{
StopAllThreads();
}
_slow = new Thread[ (int) numNoOfTheads.Value ];
for( int i = 0; i < numNoOfTheads.Value; i++)
{
_slow[i] = new Thread(ThreadRunLoop);
_slow[i].Start();
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (_slow != null)
{
StopAllThreads();
}
}
private void ui_btnStop_Click(object sender, EventArgs e)
{
if (_slow != null)
{
StopAllThreads();
}
}
private void ui_btnClear_Click(object sender, EventArgs e)
{
this.textBox1.Clear();
}
#endregion
protected void ThreadRunLoop()
{
try
{
for (int i = 0; i < 10000; i++)
{
UpdateText("Loop " + i + " for " + Thread.CurrentThread.ManagedThreadId);
}
}
catch (ThreadInterruptedException ex)
{
Console.WriteLine("Thread has been interrupted.");
}
}
private void UpdateText(string text)
{
//lock (lockTextBox)
//{
if (textBox1.InvokeRequired)
{
textBox1.Invoke(new SetTextDelegate(UpdateText), text);
}
else
{
textBox1.SuspendLayout();
textBox1.Text = textBox1.Text + text + System.Environment.NewLine;
textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();
textBox1.ResumeLayout();
}
//}
}
private void StopAllThreads()
{
for (int i = 0; i < _slow.Length; i++)
{
if (_slow[i] != null)
{
_slow[i].Interrupt();
_slow[i] = null;
}
}
_slow = null;
}
}
Form1.Designer.cs
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.ui_btnClose = new System.Windows.Forms.Button();
this.ui_btnStart = new System.Windows.Forms.Button();
this.ui_btnStop = new System.Windows.Forms.Button();
this.textBox1 = new System.Windows.Forms.TextBox();
this.ui_btnClear = new System.Windows.Forms.Button();
this.numNoOfTheads = new System.Windows.Forms.NumericUpDown();
this.label1 = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.numNoOfTheads)).BeginInit();
this.SuspendLayout();
//
// ui_btnClose
//
this.ui_btnClose.Location = new System.Drawing.Point(433, 268);
this.ui_btnClose.Name = "ui_btnClose";
this.ui_btnClose.Size = new System.Drawing.Size(75, 23);
this.ui_btnClose.TabIndex = 0;
this.ui_btnClose.Text = "Close";
this.ui_btnClose.UseVisualStyleBackColor = true;
this.ui_btnClose.Click += new System.EventHandler(this.ui_btnClose_Click);
//
// ui_btnStart
//
this.ui_btnStart.Location = new System.Drawing.Point(12, 12);
this.ui_btnStart.Name = "ui_btnStart";
this.ui_btnStart.Size = new System.Drawing.Size(75, 23);
this.ui_btnStart.TabIndex = 1;
this.ui_btnStart.Text = "Start";
this.ui_btnStart.UseVisualStyleBackColor = true;
this.ui_btnStart.Click += new System.EventHandler(this.ui_btnStart_Click);
//
// ui_btnStop
//
this.ui_btnStop.Location = new System.Drawing.Point(12, 41);
this.ui_btnStop.Name = "ui_btnStop";
this.ui_btnStop.Size = new System.Drawing.Size(75, 23);
this.ui_btnStop.TabIndex = 2;
this.ui_btnStop.Text = "Stop";
this.ui_btnStop.UseVisualStyleBackColor = true;
this.ui_btnStop.Click += new System.EventHandler(this.ui_btnStop_Click);
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(93, 12);
this.textBox1.Multiline = true;
this.textBox1.Name = "textBox1";
this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.textBox1.Size = new System.Drawing.Size(415, 241);
this.textBox1.TabIndex = 3;
//
// ui_btnClear
//
this.ui_btnClear.Location = new System.Drawing.Point(352, 268);
this.ui_btnClear.Name = "ui_btnClear";
this.ui_btnClear.Size = new System.Drawing.Size(75, 23);
this.ui_btnClear.TabIndex = 4;
this.ui_btnClear.Text = "Clear";
this.ui_btnClear.UseVisualStyleBackColor = true;
this.ui_btnClear.Click += new System.EventHandler(this.ui_btnClear_Click);
//
// numNoOfTheads
//
this.numNoOfTheads.Location = new System.Drawing.Point(12, 98);
this.numNoOfTheads.Name = "numNoOfTheads";
this.numNoOfTheads.Size = new System.Drawing.Size(74, 20);
this.numNoOfTheads.TabIndex = 5;
this.numNoOfTheads.Value = new decimal(new int[] {
1,
0,
0,
0});
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(9, 82);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(83, 13);
this.label1.TabIndex = 6;
this.label1.Text = "No. Of Threads:";
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(520, 303);
this.Controls.Add(this.label1);
this.Controls.Add(this.numNoOfTheads);
this.Controls.Add(this.ui_btnClear);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.ui_btnStop);
this.Controls.Add(this.ui_btnStart);
this.Controls.Add(this.ui_btnClose);
this.Name = "Form1";
this.Text = "Slow My Machine";
this.Load += new System.EventHandler(this.Form1_Load);
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing);
((System.ComponentModel.ISupportInitialize)(this.numNoOfTheads)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button ui_btnClose;
private System.Windows.Forms.Button ui_btnStart;
private System.Windows.Forms.Button ui_btnStop;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Button ui_btnClear;
private System.Windows.Forms.NumericUpDown numNoOfTheads;
private System.Windows.Forms.Label label1;
}
Update
If I move the lock to the else in the UpdateText method and add a Thread.Sleep(20); in the loop, then my GUI is more responsive and I can click on the Stop button and move the form around.
Any feedback, fixes will be appreciated.
The lock inside UpdateText will cause a deadlock. The worker thread acquires the lock and then calls Invoke. The UI thread then calls tries to acquire the same lock, but has to wait until it is released. The thing is that the lock will never get released because Invoke blocks until the UI thread has finished executing the delegate. That never happens because the UI thread is still waiting to acquire the lock. Deadlock!
Change the for loop to
for (int i = 0; i < 10000; i++)
{
var text = "Loop " + i + " for " + Thread.CurrentThread.ManagedThreadId;
if (textBox1.InvokeRequired)
textBox1.Invoke(new SetTextDelegate(UpdateText), text);
else
UpdateText(text);
}
And change the UpdateText to
private void UpdateText(string text)
{
textBox1.SuspendLayout();
textBox1.Text = textBox1.Text + text + System.Environment.NewLine;
textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();
textBox1.ResumeLayout();
}
EDIT: My mistake. This will just improve the organization and not in any aspect. If you want to update the UI so frequently you should use BackgroundWorker what rdkleine said.
Try moving the lock in UpdateText to inside the else.
Use a BackgroundWorker which updates the UI thread.
Here a good example how to use the BackgroundWorker:
http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
It doesn't explain how to retrieve the data (your int value) and put it in a textbox but it is a good start.
Related
This question already has answers here:
"Invoke or BeginInvoke cannot be called on a control until the window handle has been created" only occurs the second time form opens
(1 answer)
Invoke or BeginInvoke cannot be called on a control until the window handler has been created
(1 answer)
Closed 1 year ago.
I have Windows Form project that turns one format of csv file into another.
I am trying to show the converting progress while converting.
Converting method throws an exception if converting fails.
When converting fails, I close ConvertingScreen but when I close ConvertingScreen System.InvalidOperationException occurs.
invoke or begininvoke cannot be called on a control until the window handle has been created
It seems it never happens if I add some waits before I close. I have tried checking HandleCreated but it didnt go well. I used to do this kind of jobs by adding only visible label while it is on progress. This is my first time doing such like this way. Is this bad practice? If not how can I fix this? Below is simple code that can describe everything what is happening to me.
// MainForm
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
try
{
ConvertingScreen.ShowScreen();
// Converts here and results in failure so throwing an exception
throw new NotImplementedException();
}
catch (Exception)
{
ConvertingScreen.CloseForm();
MessageBox.Show("error");
}
}
}
// ConvertingScreen
public class ConvertingScreen : Form
{
public ConvertingScreen()
{
InitializeComponent();
}
private void InitializeComponent()
{
this.label1 = new System.Windows.Forms.Label();
this.SuspendLayout();
// label1
this.label1.Font = new System.Drawing.Font("MS UI Gothic", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(128)));
this.label1.Location = new System.Drawing.Point(12, 9);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(316, 22);
this.label1.TabIndex = 0;
this.label1.Text = "";
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
// ConvertingScreen
this.BackColor = System.Drawing.Color.LightGray;
this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center;
this.ClientSize = new System.Drawing.Size(340, 40);
this.ControlBox = false;
this.Controls.Add(this.label1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.MaximizeBox = false;
this.MaximumSize = new System.Drawing.Size(340, 40);
this.MinimizeBox = false;
this.MinimumSize = new System.Drawing.Size(340, 40);
this.Name = "ConvertingScreen";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.Text = "ConvertingScreen";
this.TopMost = true;
this.ResumeLayout(false);
}
private Label label1;
private delegate void CloseDelegate();
private delegate void UpdateTextDelegate(string text);
private static ConvertingScreen form;
public static void ShowScreen()
{
if (form != null) return;
form = new ConvertingScreen();
Thread thread = new Thread(new ThreadStart(ConvertingScreen.ShowForm));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
private static void ShowForm()
{
if (form != null) Application.Run(form);
}
public static void CloseForm()
{
form?.Invoke(new CloseDelegate(ConvertingScreen.CloseFormInternal));
}
private static void CloseFormInternal()
{
if (form != null)
{
form.Close();
form = null;
}
}
public static void Update(string text)
{
if (form != null) form.UpdateLabel(text);
}
private void UpdateLabel(string text)
{
if (InvokeRequired)
{
this.Invoke(new UpdateTextDelegate(UpdateLabel), new object[] { text });
return;
}
label1.Text = text;
}
}
I have a simple winform application which allows users to drag controls to a tablelayoutpanel. But after some testing and trying to drag controls to a specific index row, I found out it doesn't work, not even with a hardcoded index number.
With the provided code example, I'm trying to add a textbox to row index 2, but when I drag content from the listbox to the tablelayoutpanel, it just adds the textbox in 'random' places as seen in the screenshot below
I expect the existing textboxes to shift down and make place for the textbox that's being added, as far as I understand from this: https://social.msdn.microsoft.com/Forums/windows/en-US/e4312cd8-6031-4a5c-92bf-e8adb1941fe5/insert-row-at-particular-position-in-table-layout-panel?forum=winforms.
Am I doing something wrong?
Designer code:
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.listBox1 = new System.Windows.Forms.ListBox();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.SuspendLayout();
//
// listBox1
//
this.listBox1.FormattingEnabled = true;
this.listBox1.Location = new System.Drawing.Point(367, 12);
this.listBox1.Name = "listBox1";
this.listBox1.Size = new System.Drawing.Size(190, 407);
this.listBox1.TabIndex = 0;
this.listBox1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.listBox1_MouseDown);
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 1;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel1.Location = new System.Drawing.Point(13, 12);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 1;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(332, 407);
this.tableLayoutPanel1.TabIndex = 1;
this.tableLayoutPanel1.DragDrop += new System.Windows.Forms.DragEventHandler(this.tableLayoutPanel1_DragDrop);
this.tableLayoutPanel1.DragEnter += new System.Windows.Forms.DragEventHandler(this.tableLayoutPanel1_DragEnter);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(569, 431);
this.Controls.Add(this.tableLayoutPanel1);
this.Controls.Add(this.listBox1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.ListBox listBox1;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
}
Form code:
public partial class Form1 : Form
{
private int tempInt = 0;
public Form1()
{
InitializeComponent();
listBox1.Items.AddRange(new object[] { "test" });
tableLayoutPanel1.AllowDrop = true;
tableLayoutPanel1.GrowStyle = TableLayoutPanelGrowStyle.AddRows;
tableLayoutPanel1.AutoScroll = true;
tableLayoutPanel1.RowCount = 3;
}
private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
tempInt++;
DoDragDrop("test" + tempInt, DragDropEffects.Copy);
}
private void tableLayoutPanel1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
private void tableLayoutPanel1_DragDrop(object sender, DragEventArgs e)
{
string text = e.Data.GetData(typeof(String)) as string;
TextBox tb = new TextBox();
tb.Dock = DockStyle.Fill;
tb.Text = text;
// I want to add the textbox to the second row
tableLayoutPanel1.Controls.Add(tb, 0, 2);
tableLayoutPanel1.SetRow(tb, 2);
}
}
EDIT:
Added code based as DonBoitnott's suggested
private void tableLayoutPanel1_DragDrop(object sender, DragEventArgs e)
{
string text = e.Data.GetData(typeof(String)) as string;
TextBox tb = new TextBox();
tb.Dock = DockStyle.Fill;
tb.Text = text;
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.AutoSize));
for (int i = 0; i < tableLayoutPanel1.Controls.Count; i++)
{
int pos = tableLayoutPanel1.GetRow(tableLayoutPanel1.Controls[i]);
if (pos > 1)
{
tableLayoutPanel1.SetRow(tableLayoutPanel1.Controls[i], pos + 1);
}
}
tableLayoutPanel1.Controls.Add(tb, 0, 2);
}
Give this modified form class a try, I've made a few changes throughout:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
listBox1.Items.AddRange(new Object[] { "TextBox" });
tableLayoutPanel1.AllowDrop = true;
tableLayoutPanel1.GrowStyle = TableLayoutPanelGrowStyle.AddRows;
tableLayoutPanel1.AutoScroll = true;
tableLayoutPanel1.RowStyles.Clear();
tableLayoutPanel1.ColumnStyles.Clear();
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f));
}
private void listBox1_MouseDown(Object sender, MouseEventArgs e)
{
var count = tableLayoutPanel1.Controls.Count;
DoDragDrop($"test{count + 1}", DragDropEffects.Copy);
}
private void tableLayoutPanel1_DragEnter(Object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
private void tableLayoutPanel1_DragDrop(Object sender, DragEventArgs e)
{
var tb = new TextBox();
tb.Dock = DockStyle.Fill;
tb.Text = (e.Data.GetData(typeof(String)) as String);
var newRow = tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.AutoSize));
var ctrl = tableLayoutPanel1.GetChildAtPoint(tableLayoutPanel1.PointToClient(new Point(e.X, e.Y)));
if (ctrl != null)
{
var pos = tableLayoutPanel1.GetRow(ctrl);
for (Int32 i = tableLayoutPanel1.RowStyles.Count - 2; i >= pos; i--)
{
var c = tableLayoutPanel1.GetControlFromPosition(0, i);
if (c != null)
tableLayoutPanel1.SetRow(c, i + 1);
}
tableLayoutPanel1.Controls.Add(tb, 0, pos);
}
else
tableLayoutPanel1.Controls.Add(tb, 0, newRow);
}
}
The basic steps are:
Did I drop onto an existing control? If so, determine where.
If so, work bottom to top, moving each control down a slot. You must avoid overlapping, two controls cannot occupy the same cell.
If so, insert the new control into the newly emptied slot at the drop point.
If not, simply add the control at the end.
I have a problem with background worker, it gets called twice thus, increasing the time of execution for my long routine, I created background worker manually so, there is no chance for the DoWork to be initialized within the InitializeComponent() method, any help is appreciated.
Here is my code:
namespace formStudent2
{
public partial class formStudent2 : Form
{
private BackgroundWorker backgroundWorker1 = new BackgroundWorker();
private ProgressBar progressBar1 = new ProgressBar();
private Label label1 = new Label();
public formStudent2()
{
InitializeComponent();
}
...
private void btnExportCsv_Click(object sender, EventArgs e)
{
// Path output file CSV
string pathFile = #"D:\DataTest\ListStudent.csv";
int filesCount = 3;
// Check if the backgroundWorker is already busy running the asynchronous operation
if (!backgroundWorker1.IsBusy)
{
btnExportCsv.Enabled = false;
// This method will start the execution asynchronously in the background
backgroundWorker1.RunWorkerAsync();
}
else
{
label1.Text = "Busy processing, please wait...";
}
/**
* Display dialog Progress Bar : Exporting data...
*/
Form form = new Form();
form.Text = "Exporting data...";
form.ClientSize = new Size(376, 100);
form.FormBorderStyle = FormBorderStyle.FixedSingle;
form.ShowIcon = false;
form.ControlBox = false;
form.MaximizeBox = false;
form.MinimizeBox = false;
form.StartPosition = FormStartPosition.CenterScreen;
label1.AutoSize = true;
label1.Location = new System.Drawing.Point(17, 25);
label1.Name = "lblPercent";
label1.Size = new System.Drawing.Size(102, 15);
label1.TabIndex = 1;
label1.Text = "0% Completed";
form.Controls.Add(label1);
Button btnCancel = new Button();
btnCancel.Location = new System.Drawing.Point(268, 19);
btnCancel.Name = "btnCancel";
btnCancel.Size = new System.Drawing.Size(99, 27);
btnCancel.TabIndex = 3;
btnCancel.Text = "Cancel";
btnCancel.UseVisualStyleBackColor = true;
btnCancel.Click += (senderq, eq) =>
{
if (backgroundWorker1.IsBusy)
{
// Cancel the asynchronous operation id still in progress
backgroundWorker1.CancelAsync();
}
else
{
form.Close();
}
};
form.Controls.Add(btnCancel);
progressBar1.Location = new System.Drawing.Point(17, 61);
progressBar1.Name = "progressBar1";
progressBar1.Size = new System.Drawing.Size(350, 27);
form.Controls.Add(progressBar1);
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.DoWork += (senderq, eq) =>
{
for (int i = 0; i < filesCount; i++)
{
/**
* Export to file CSV
*/
ExportFileCsv(pathFile, listStudent);
int percentage = (i + 1) * 100 / filesCount;
backgroundWorker1.ReportProgress(percentage);
// Set cancellation to pending
if (backgroundWorker1.CancellationPending)
{
// Execute cancellation
eq.Cancel = true;
backgroundWorker1.ReportProgress(0);
return;
}
}
};
backgroundWorker1.ProgressChanged += (senderq, eq) =>
{
// This is updated from doWork. Its where GUI components are update,
// receives updates after 100ms
progressBar1.Value = eq.ProgressPercentage;
label1.Text = eq.ProgressPercentage.ToString() + "% Completed";
};
backgroundWorker1.RunWorkerCompleted += (senderq, eq) =>
{
// Called when the heavy operation in background is over.
// Can also accept GUI components
if (eq.Cancelled)
{
label1.Text = "Processing cancelled.";
}
else if (eq.Error != null)
{
label1.Text = eq.Error.Message;
}
else
{
btnExportCsv.Enabled = true;
MessageBox.Show("Export Finished!");
form.Close();
}
};
form.ShowDialog();
}
}
}
The running status of the worker is not static. It is dependent an instance of a new background worker. Try to make it shared.
Below is some sort of a pseudo-code (I am a java guy, never programmed with C# before) to do it:
public partial class formStudent {
boolean workerRunningStatus = false;
private void btnExportCsv_Click(object sender, EventArgs e)
{
// Path output file CSV
string pathFile = #"D:\DataTest\ListStudent.csv";
int filesCount = 3;
// Check if the backgroundWorker is already busy running the asynchronous operation
if (!workerRunning)
{
btnExportCsv.Enabled = false;
// This method will start the execution asynchronously in the background
backgroundWorker1.RunWorkerAsync();
}
else
{
workerRunningStatus = true;
//then exit
return;
}
//Continue with the remaining logic
}
}
It's just all about having a single instance of the worker running. Or see how to use a singleton pattern in C#.
I'm using a C# form and I essentially want to update a label for every second of the current time. Obviously the UI thread is getting in the way and stuff so I resorted to background workers and I'm sitting here scratching my head trying to update my label every second. I've tried using a while loop in several locations but it won't work. I'm obviously doing something wrong... Any solutions?
Code:
MainForm.Designer.cs:
using System;
namespace alarmClock
{
partial class MainForm
{
System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing) {
if (components != null) {
components.Dispose();
}
}
base.Dispose(disposing);
}
public static DateTime now = DateTime.Now;
public static string time = now.ToLongTimeString();
public void setRealHour() {
hour.Text = time[0].ToString() + time[1].ToString();
}
public void setRealMinute() {
minute.Text = time[3].ToString() + time[4].ToString();
}
public void setRealSecond() {
second.Text = time[6].ToString() + time[7].ToString();
}
public void setHourTime() {
setHour.Text = MainForm.setH.ToString();
if(MainForm.setH < 10) {
setHour.Text = "0" + MainForm.setH.ToString();
}
if(MainForm.setH > 24) {
MainForm.setH=0;
setHour.Text = "0" + MainForm.setH.ToString();
}
}
public void setMinuteTime() {
setMinute.Text = MainForm.setM.ToString();
if(MainForm.setM < 10) {
setMinute.Text = "0" + MainForm.setM.ToString();
}
if(MainForm.setM > 60) {
MainForm.setM=0;
setMinute.Text = "0" + MainForm.setM.ToString();
}
}
public void setSecondTime() {
setSecond.Text = MainForm.setS.ToString();
if(MainForm.setS < 10) {
setSecond.Text = "0" + MainForm.setS.ToString();
}
if(MainForm.setS > 60) {
MainForm.setS=0;
setSecond.Text = "0" + MainForm.setS.ToString();
}
}
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.minute = new System.Windows.Forms.Label();
this.hour = new System.Windows.Forms.Label();
this.second = new System.Windows.Forms.Label();
this.checkboxTime = new System.Windows.Forms.CheckedListBox();
this.delTime = new System.Windows.Forms.Button();
this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel();
this.setHour = new System.Windows.Forms.Label();
this.setMinute = new System.Windows.Forms.Label();
this.setSecond = new System.Windows.Forms.Label();
this.tableLayoutPanel4 = new System.Windows.Forms.TableLayoutPanel();
this.subHour = new System.Windows.Forms.Button();
this.addHour = new System.Windows.Forms.Button();
this.subMin = new System.Windows.Forms.Button();
this.addMin = new System.Windows.Forms.Button();
this.subSec = new System.Windows.Forms.Button();
this.addSec = new System.Windows.Forms.Button();
this.setTime = new System.Windows.Forms.GroupBox();
this.addTime = new System.Windows.Forms.Button();
this.bgwM = new System.ComponentModel.BackgroundWorker();
this.bgwS = new System.ComponentModel.BackgroundWorker();
this.bgwH = new System.ComponentModel.BackgroundWorker();
this.tableLayoutPanel1.SuspendLayout();
this.tableLayoutPanel2.SuspendLayout();
this.tableLayoutPanel3.SuspendLayout();
this.tableLayoutPanel4.SuspendLayout();
this.setTime.SuspendLayout();
this.SuspendLayout();
//
// tableLayoutPanel1
//
resources.ApplyResources(this.tableLayoutPanel1, "tableLayoutPanel1");
this.tableLayoutPanel1.Controls.Add(this.minute, 1, 0);
this.tableLayoutPanel1.Controls.Add(this.hour, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.second, 2, 0);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
//
// minute
//
resources.ApplyResources(this.minute, "minute");
this.minute.ForeColor = System.Drawing.Color.Black;
this.minute.Name = "minute";
//
// hour
//
resources.ApplyResources(this.hour, "hour");
this.hour.ForeColor = System.Drawing.Color.Black;
this.hour.Name = "hour";
//
// second
//
resources.ApplyResources(this.second, "second");
this.second.ForeColor = System.Drawing.Color.Black;
this.second.Name = "second";
//
// checkboxTime
//
this.checkboxTime.BackColor = System.Drawing.Color.Silver;
resources.ApplyResources(this.checkboxTime, "checkboxTime");
this.checkboxTime.ForeColor = System.Drawing.Color.Black;
this.checkboxTime.FormattingEnabled = true;
this.checkboxTime.Items.AddRange(new object[] {
resources.GetString("checkboxTime.Items"),
resources.GetString("checkboxTime.Items1")});
this.checkboxTime.Name = "checkboxTime";
this.checkboxTime.SelectedIndexChanged += new System.EventHandler(this.CheckboxTimeSelectedIndexChanged);
//
// delTime
//
resources.ApplyResources(this.delTime, "delTime");
this.delTime.Name = "delTime";
this.delTime.UseVisualStyleBackColor = true;
this.delTime.Click += new System.EventHandler(this.DelTimeClick);
//
// tableLayoutPanel2
//
resources.ApplyResources(this.tableLayoutPanel2, "tableLayoutPanel2");
this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel3, 0, 1);
this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel4, 0, 0);
this.tableLayoutPanel2.Name = "tableLayoutPanel2";
//
// tableLayoutPanel3
//
resources.ApplyResources(this.tableLayoutPanel3, "tableLayoutPanel3");
this.tableLayoutPanel3.Controls.Add(this.setHour, 0, 0);
this.tableLayoutPanel3.Controls.Add(this.setMinute, 1, 0);
this.tableLayoutPanel3.Controls.Add(this.setSecond, 2, 0);
this.tableLayoutPanel3.Name = "tableLayoutPanel3";
//
// setHour
//
resources.ApplyResources(this.setHour, "setHour");
this.setHour.ForeColor = System.Drawing.Color.Black;
this.setHour.Name = "setHour";
//
// setMinute
//
resources.ApplyResources(this.setMinute, "setMinute");
this.setMinute.ForeColor = System.Drawing.Color.Black;
this.setMinute.Name = "setMinute";
//
// setSecond
//
resources.ApplyResources(this.setSecond, "setSecond");
this.setSecond.ForeColor = System.Drawing.Color.Black;
this.setSecond.Name = "setSecond";
//
// tableLayoutPanel4
//
resources.ApplyResources(this.tableLayoutPanel4, "tableLayoutPanel4");
this.tableLayoutPanel4.Controls.Add(this.subHour, 0, 0);
this.tableLayoutPanel4.Controls.Add(this.addHour, 1, 0);
this.tableLayoutPanel4.Controls.Add(this.subMin, 2, 0);
this.tableLayoutPanel4.Controls.Add(this.addMin, 3, 0);
this.tableLayoutPanel4.Controls.Add(this.subSec, 4, 0);
this.tableLayoutPanel4.Controls.Add(this.addSec, 5, 0);
this.tableLayoutPanel4.Name = "tableLayoutPanel4";
//
// subHour
//
resources.ApplyResources(this.subHour, "subHour");
this.subHour.Name = "subHour";
this.subHour.UseVisualStyleBackColor = true;
this.subHour.Click += new System.EventHandler(this.SubHourClick);
//
// addHour
//
resources.ApplyResources(this.addHour, "addHour");
this.addHour.Name = "addHour";
this.addHour.UseVisualStyleBackColor = true;
this.addHour.Click += new System.EventHandler(this.AddHourClick);
//
// subMin
//
resources.ApplyResources(this.subMin, "subMin");
this.subMin.Name = "subMin";
this.subMin.UseVisualStyleBackColor = true;
this.subMin.Click += new System.EventHandler(this.SubMinClick);
//
// addMin
//
resources.ApplyResources(this.addMin, "addMin");
this.addMin.Name = "addMin";
this.addMin.UseVisualStyleBackColor = true;
this.addMin.Click += new System.EventHandler(this.AddMinClick);
//
// subSec
//
resources.ApplyResources(this.subSec, "subSec");
this.subSec.Name = "subSec";
this.subSec.UseVisualStyleBackColor = true;
this.subSec.Click += new System.EventHandler(this.SubSecClick);
//
// addSec
//
resources.ApplyResources(this.addSec, "addSec");
this.addSec.Name = "addSec";
this.addSec.UseVisualStyleBackColor = true;
this.addSec.Click += new System.EventHandler(this.AddSecClick);
//
// setTime
//
resources.ApplyResources(this.setTime, "setTime");
this.setTime.BackColor = System.Drawing.Color.Silver;
this.setTime.Controls.Add(this.addTime);
this.setTime.Controls.Add(this.tableLayoutPanel2);
this.setTime.Name = "setTime";
this.setTime.TabStop = false;
//
// addTime
//
resources.ApplyResources(this.addTime, "addTime");
this.addTime.Name = "addTime";
this.addTime.UseVisualStyleBackColor = true;
this.addTime.Click += new System.EventHandler(this.AddTimeClick);
//
// bgwM
//
this.bgwM.DoWork += new System.ComponentModel.DoWorkEventHandler(this.BgwMDoWork);
//
// bgwS
//
this.bgwS.DoWork += new System.ComponentModel.DoWorkEventHandler(this.BgwSDoWork);
//
// bgwH
//
this.bgwH.DoWork += new System.ComponentModel.DoWorkEventHandler(this.BgwHDoWork);
//
// MainForm
//
resources.ApplyResources(this, "$this");
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.DarkGray;
this.Controls.Add(this.setTime);
this.Controls.Add(this.delTime);
this.Controls.Add(this.checkboxTime);
this.Controls.Add(this.tableLayoutPanel1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.Name = "MainForm";
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.tableLayoutPanel2.ResumeLayout(false);
this.tableLayoutPanel2.PerformLayout();
this.tableLayoutPanel3.ResumeLayout(false);
this.tableLayoutPanel3.PerformLayout();
this.tableLayoutPanel4.ResumeLayout(false);
this.setTime.ResumeLayout(false);
this.setTime.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
private System.ComponentModel.BackgroundWorker bgwH;
private System.ComponentModel.BackgroundWorker bgwS;
private System.ComponentModel.BackgroundWorker bgwM;
System.Windows.Forms.Button addTime;
System.Windows.Forms.GroupBox setTime;
System.Windows.Forms.Button addSec;
System.Windows.Forms.Button subSec;
System.Windows.Forms.Button addMin;
System.Windows.Forms.Button subMin;
System.Windows.Forms.Button addHour;
System.Windows.Forms.TableLayoutPanel tableLayoutPanel4;
System.Windows.Forms.Label setSecond;
System.Windows.Forms.Label setMinute;
System.Windows.Forms.Label setHour;
System.Windows.Forms.TableLayoutPanel tableLayoutPanel3;
System.Windows.Forms.Button subHour;
System.Windows.Forms.TableLayoutPanel tableLayoutPanel2;
System.Windows.Forms.Button delTime;
System.Windows.Forms.CheckedListBox checkboxTime;
System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
System.Windows.Forms.Label second;
System.Windows.Forms.Label hour;
System.Windows.Forms.Label minute;
void BgwHDoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
setRealHour();
}
void BgwMDoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
setRealMinute();
}
void BgwSDoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
setRealSecond();
}
}
}
MainForm.cs:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace alarmClock
{
public partial class MainForm : Form
{
public static int setH = 0;
public static int setM = 0;
public static int setS = 0;
public MainForm()
{
InitializeComponent();
bgwH.RunWorkerAsync();
bgwM.RunWorkerAsync();
bgwS.RunWorkerAsync();
}
void DelTimeClick(object sender, EventArgs e)
{
}
void SubHourClick(object sender, EventArgs e)
{
setH--;
setHourTime();
}
void AddHourClick(object sender, EventArgs e)
{
setH++;
setHourTime();
}
void SubMinClick(object sender, EventArgs e)
{
setM--;
setMinuteTime();
}
void AddMinClick(object sender, EventArgs e)
{
setM++;
setMinuteTime();
}
void SubSecClick(object sender, EventArgs e)
{
setS--;
setSecondTime();
}
void AddSecClick(object sender, EventArgs e)
{
setS++;
setSecondTime();
}
void AddTimeClick(object sender, EventArgs e)
{
}
void CheckboxTimeSelectedIndexChanged(object sender, EventArgs e)
{
}
}
}
As Jeremy noted in the comments, a Timer would be a better choice.
For completeness, if you still wanted to use a BackgroundWorker, due to some other reason, you will need to use BackgroundWorker.ReportProgress() method. Any UI work must be done on the main UI thread. Using a BackgroundWorker you need to use the Reporting event handler to get your callback back on the main Thread.
You need to ensure BackgroundWorker.WorkerReportsProgress is set to TRUE. This is not by default. Then move your label set method into an Event Handler on ProgressChanged
I have this code:
namespace TuyenTk
{
public partial class Form1 : Form
{
Form2 _form2 = new Form2("");
public Form1()
{
InitializeComponent();
_form2.Show();
int i = 0;
while (i < 5)
{
_form2.label1.Text = "" + i;
Thread.Sleep(500);
i++;
}
}
}
public class Form2 : Form
{
public System.Windows.Forms.Label label1;
public System.ComponentModel.Container components = null;
public Form2()
{
InitializeComponent();
}
private void InitializeComponent(string t)
{
this.label1 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// label1
//
this.label1.Location = new System.Drawing.Point(5, 5);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(290, 100);
this.label1.TabIndex = 0;
this.label1.Text = t;
//
// Form2
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(300, 100);
this.ControlBox = false;
this.Controls.Add(this.label1);
this.Name = "Form2";
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.ShowInTaskbar = false;
this.ResumeLayout(false);
}
}
}
when Form1 run, it show the Form2, but the Form2.label1 background is white, and have no text.
After 2.5 seconds, Form2.label1.Text = 4. So the value 0, 1, 2, 3 of i dont appear. How I can fix it? Thank you very much.
What you want to do (periodically update label) is achieved by usage of Timer component (you can drag it from ToolBox and place on your form).
public partial class Form1 : Form
{
Form2 _form2 = new Form2("");
Timer _timer;
int _counter;
public Form1()
{
InitializeComponent();
_form2.Show();
if (components == null)
components = new System.ComponentModel.Container();
_timer = new Timer(components); // required for correct disposing
_timer.Interval = 500;
_timer.Tick += timer_Tick;
_timer.Start();
}
private void timer_Tick(object sender, EventArgs e)
{
if (_counter < 5)
{
_form2.label1.Text = _counter.ToString();
_counter++;
return;
}
_timer.Stop();
}
Also creating public controls on other forms is not very good idea - if you really need to update some value on form2, then its better to declare public method/property in Form2 class, which will update label:
public partial class Form2 : Form
{
public int Value
{
set { label1.Text = value.ToString(); }
}
}
Also consider to move timer to Form2 (let this form update itself).
If you invoke Thread.Sleep(500); in your UI thread, the GUI will be unresponsable. That is why you get your Fomr2.label1's background white. I suggest you move your code
while (i < 5)
{
_form2.label1.Text = "" + i;
Thread.Sleep(500);
i++;
}
to another thread. And you can refer to this link to fullfil your goal.