C# - Threading in forms [duplicate] - c#

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How to update GUI from another thread in C#?
I have form with textBox1 and textBox2. I need very simple example of an extra thread to fill textBox2, just to understand principle. So far i am using MessageBox to display value, but i want to use textBox2 instead. Here is 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.Windows.Forms;
using System.Threading;
namespace Threads_example
{
public partial class Form1 : Form
{
Thread t = new Thread(t1);
internal static int x1 = 1;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
static void t1()
{
do
{
x1++;
MessageBox.Show("x1 - " + x1.ToString());
}
while(x1<10);
}
private void button1_Click(object sender, EventArgs e)
{
t.Start();
for (int x = 1; x < 100000; x++)
{
textBox1.Text = x.ToString();
textBox1.Refresh();
}
}
}
}

You need to invoke UI actions in the GUI thread.
It can be done with something like this:
private void RunFromAnotherThreadOrNot()
{
if (InvokeRequired)
{
Invoke(new ThreadStart(RunFromAnotherThread));
return;
}
textBox2.Text = "What Ever";
// Other UI actions
}

Related

Error: "Cross-thread operation not valid" when Form.close() with CefSharp [duplicate]

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();

Background worker to async await task [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
Below is the sample code, this uses devexpress bareditItem(progressbar) to show progress when data is loaded. I would like to know if there is a way I could show the same progress bar(show progress when data is loading) using async await and task.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace ProgressBar {
public partial class Form1 : DevExpress.XtraEditors.XtraForm {
DataTable workTable;
public Form1() {
InitializeComponent();
workTable = new DataTable("Records");
workTable.Columns.Add("Id", typeof(int));
workTable.Columns.Add("Data", typeof(String));
}
//this data varies from 0 to 50,000 rows
private void LoadData(DoWorkEventArgs e) {
for(int i = 0; i < 1001; i++) {
System.Threading.Thread.Sleep(5);
workTable.Rows.Add(i, String.Format("Record {0}", i));
this.backgroundWorker1.ReportProgress(i, i);
}
}
private void button1_Click(object sender, EventArgs e) {
this.backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
LoadData(e);
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) {
DataTable up = workTable.Clone();
this.barEditItem1.EditValue = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
gridControl1.DataSource = workTable;
}
}
}
Your code can be converted and simplified using the async/await pattern and the Progress<T> class to ensure that the progress report update occurs on the correct thread:
namespace ProgressBar {
public partial class Form1 : DevExpress.XtraEditors.XtraForm {
DataTable workTable;
public Form1() {
InitializeComponent();
workTable = new DataTable("Records");
workTable.Columns.Add("Id", typeof(int));
workTable.Columns.Add("Data", typeof(String));
}
//this data varies from 0 to 50,000 rows
private void LoadData(IProgress<int> progress) {
for(int i = 0; i < 1001; i++) {
System.Threading.Thread.Sleep(5);
workTable.Rows.Add(i, String.Format("Record {0}", i));
progress.Report(i);
}
}
private async void button1_Click(object sender, EventArgs e) {
// 1. This replaces: backgroundWorker1_ProgressChanged
var progress = new Progress<int>(
i =>
{
// This code will execute on the UI thread, as it should
DataTable up = workTable.Clone();
this.barEditItem1.EditValue = i;
});
// 2. This replaces: backgroundWorker1_DoWork
await Task.Run(() => this.LoadData(progress));
// 3. This replaces: backgroundWorker1_RunWorkerCompleted
gridControl1.DataSource = workTable;
}
}
}
I left in a few lines of code that you have, that I don't think you actually need, like the Thread.Sleep and DataTable up = workTable.Clone();, but I'm sure you can figure that out.
BackgroundWorker is an obsolete feature for me (Since the release of the async-await feature). Try not to use it from now on. This is the equivalent using the fancy async-await:
private async void button1_Click(object sender, EventArgs e)
{
await LoadData();
}
private async Task LoadData()
{
for (int i = 0; i < 1001; i++)
{
await Task.Delay(5);
workTable.Rows.Add(i, String.Format("Record {0}", i));
DataTable up = workTable.Clone(); // useless but copied from your code
this.barEditItem1.EditValue = i;
}
gridControl1.DataSource = workTable;
}
Sure there is. Don't use a background worker at all as it's obsolete.
Here's some (untested) code that should get you on your way. The SynchronizationContext prevents Cross-threaded exceptions. There might be a better way to do that, but that's what I use.
namespace ProgressBar
{
using System.ComponentModel;
using System.Data;
using System.Threading;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
public partial class Form1 : DevExpress.XtraEditors.XtraForm
{
private DataTable workTable;
private SynchronizationContext _context;
public Form1()
{
InitializeComponent();
this._context = SynchronizationContext.Current;
workTable = new DataTable("Records");
workTable.Columns.Add("Id", typeof(int));
workTable.Columns.Add("Data", typeof(String));
}
private async Task LoadData()
{
const int iterations = 1001; //whatever you want...
for (int i = 0; i < iterations; i++)
{
workTable.Rows.Add(i, String.Format("Record {0}", i));
this._context.Post(() =>
{
this.UpdateWorkProgress((int) i /iterations)
});
}
}
private void UpdateWorkProgress(int percent)
{
this.barEditItem1.EditValue = percent;
}
private void button1_Click(object sender, EventArgs e)
{
await this.LoadData();
gridControl1.DataSource = workTable;
}
}
}

Visual Studio C# while loop freezing forms application [duplicate]

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;
}
}

How to get a value from an Array via a class in Winforms using C#

I have a class called Game.cs and in the class I have the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Simon
{
class Game
{
public int[] TheArray = new int[1000];
private bool m_Play;
public bool Play
{
set { m_Play = value; }
get { return m_Play; }
}
public Game()
{
Random rnd = new Random();
for (int i = 0; i < 8; i++)
{
TheArray[i] = rnd.Next(0, 4); // between 0 and 3
}
}
}
}
I want to be able to call TheArray from my form. I want the loop to iterate based on the times I click button5 and then I want to click my buttons programmatically based on what my array returns. On my form I have 4 buttons called, button1,button2,button3 and button4.
Once I click on button5 my code needs to click the button based on the array each time it loops through TheArray
So far I have this:
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 Simon
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private Game m_game;
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("box1");
}
private void button2_Click(object sender, EventArgs e)
{
MessageBox.Show("box2");
}
private void button3_Click(object sender, EventArgs e)
{
MessageBox.Show("box3");
}
private void button4_Click(object sender, EventArgs e)
{
MessageBox.Show("box4");
}
private void button5_Click(object sender, EventArgs e)
{
// Determine which button to click based on TheArray
}
}
}
To click on the buttons based on the array (I'm assuming 0 in the array corresponds to button1, etc, you could try something like this:
private void button5_Click(object sender, EventArgs e)
{
Button[] button = { button1, button2, button3, button4 };
for (int i = 0; i < m_game.TheArray.Length; i++)
{
button[m_game.TheArray[i]].PerformClick();
}
}
As a side note, as #ThunderGr pointed out in a comment, you'd have to create an instance of your game or else make it static.
In the form declaration define Game MyGameClass=new Game();.
You code has lots of gaps, though. In order to get the last int, you need to also define a public int CurrentInt=0; in the Game Class and do a CurrentInt++; from within Game().
Now, you can do a int theInt=MyGameClass.TheArray[MyGameClass.CurrentInt]; from within the button5 event and implement a switch statement to find which button to click.
Of course, this is still inefficient.
Perhaps it would be better if you declared a public int GetLastInt() in Game that would return TheArray[CurrentInt];.

c# event handling between two forms

I have two forms and I am trying to capture an event generated from frmEventGenerate.cs in frmEventReceive.cs.
In this example I can receive the event from frmEventGenerate.cs but not sure how I can catch this in frmEventReceive.cs? frmEventReceive.cs is my startup form which creates frmEventGenerate.cs.
Can someone point me in the right direction, I think I am being stupid!
Thank you
frmEventGenerate.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Events
{
public delegate void LinkToEventHandler();
public partial class frmEventGenerate : Form
{
public static event LinkToEventHandler Evt;
public frmEventGenerate()
{
InitializeComponent();
Evt += new LinkToEventHandler(ReceiveEvent);
SendEvent();
}
public static void SendEvent()
{
if (Evt != null)
{
Evt();
}
}
public void ReceiveEvent()
{
System.Console.WriteLine("Received Event - This works ok");
}
}
}
frmEventReceive.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Events
{
public partial class frmEventReceive : Form
{
public frmEventReceive()
{
InitializeComponent();
frmEventGenerate frmGen = new frmEventGenerate();
}
public void ReceiveEvent()
{
System.Console.WriteLine("I want to be able to receive the even here!");
}
}
}
In your constructor, after instantiating frmEventGenerate:
frmGen.Evt += ReceiveEvent;
You don't need new LinkEventHandler(...) any more - as of C# 2, there's a method group conversion available which you can use to convert from a method group (the name of a method) to a delegate type.
EDIT: I hadn't seen that your event was static. That suggests you should actually use:
frmEventGenerate.Evt += ReceiveEvent;
... and you don't need the frmGen variable at all.
However, I would strongly discourage you from this - why do you want the event to be static in the first place? (I'd also urge you to name your types more sensibly - something like "EventGenerator" would be better here, for example. Ignoring the convention that type names should be in Pascal case leads to confusing code.)
//Receiver
using System;
using System.Windows.Forms;
namespace eTest
{
public partial class frmMain : Form
{
public frmMain()
{
InitializeComponent();
}
public void ReceiveEvent(int i)
{
MessageBox.Show("Event Received from Form: " + i.ToString());
}
private void btnNew_Click(object sender, EventArgs e)
{
int num = 0;
int x = 0;
num = Convert.ToInt32(txtForms.Text);
for (x = 0; x < num; x++)
{
frmDL f = new frmDL();
f.Evt += ReceiveEvent;
f.iID = x;
f.Text = x.ToString();
f.Show();
f.Activate();
Application.DoEvents();
}
}
}
}
//Sender
using System;
using System.Windows.Forms;
namespace eTest
{
public delegate void myEventHandler(int i);
public partial class frmDL : Form
{
public event myEventHandler Evt;
public int iID = 0;
public frmDL()
{
InitializeComponent();
}
public void SendEvent()
{
if (Evt != null)
{
Evt(this.iID);
}
}
private void btnEvent_Click(object sender, EventArgs e)
{
SendEvent();
}
}
}

Categories

Resources