c# multiple instances of methods? - c#

I have a c# program.
the form1 of the program makes new instances of form3 on different thread from time to time.
so each of the form3 instances contains these methods.
I suspect that the problem occours when two threads calls the same method at the same time or while another thread is allready using one of them. especially because they have the same webbrowser name
private void vent()
{
while (wb.ReadyState != WebBrowserReadyState.Complete)
{
Application.DoEvents();
}
}
public bool containss(string SearchText)
{
string page = wb.Document.Body.InnerText;
if (page.ToLower().Contains(SearchText.ToLower()))
return true;
else return false;
}
The Problem with this containss() method is that it works with
string page = wb.Document.Body.InnerHtml;
and not
string page = wb.Document.Body.InnerText;
I get: Object reference not set to an instance of an object.
on this line:
if (page.ToLower().Contains(SearchText.ToLower()))
I make the threads like this:
private void ThreadProc()
{
Form frm = new Form3(currentAB);
frm.ShowDialog();
}
Thread ab1 = new Thread(new ThreadStart(ThreadProc));
ab1.SetApartmentState(ApartmentState.STA);
ab1.Start();

Related

Accessing UI from outside thread in Winforms

In WPF, one could use something like:
Application.Current.Dispatcher.BeginInvoke(new Action(() => Form1.grid.Items.Refresh()));
to access UI functions outside of the main thread. In Winforms however, the same functionality isn't there. What would be the easiest way to access a BindingList that exists inside of my Form1 class from my "working" thread? Currently I getting the following error when trying to access "Form1.record_list":
System.InvalidOperationException: Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on.
edit: I appreciate the help so far, but I'm lost on "this.Invoke". My method in the separate thread has no "Invoke".
Here's an example of my code so far.
public static void listen(IPEndPoint server_ip)
{
Console.WriteLine("In listen");
while (true)
{
try
{
byte[] received_bytes = udp_client.Receive(ref server_ip);
string received_data = Encoding.ASCII.GetString(received_bytes);
Record record = JsonConvert.DeserializeObject<Record>(received_data);
Form1.record_list.Add(record); //This is where I assume the problem spawns
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
public partial class Form1 : Form
{
public static BindingList<Record> record_list = new BindingList<Record> { };
public static DataGridViewCellStyle style = new DataGridViewCellStyle();
public Form1()
{
InitializeComponent();
Thread thread = new Thread(SCMClient.connect);
thread.IsBackground = true;
thread.Start();
FillData();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
foreach (DataGridViewRow row in dataGridView.Rows)
{
for (var i = 0; i < row.Cells.Count; i++)
{
Console.WriteLine(row.Cells[i].Value);
if (row.Cells[i].Value as string == "OK")
{
row.Cells[i].Style.BackColor = Color.Red;
Console.WriteLine("IF WAS TRUE");
}
}
}
}
I think the specific problem here is when I add Records to the Forms1.record_list. I'm not sure how to add items to that list without causing the Cross-thread error...
You MUST access your UI from your UI Thread only. But you can use the Control.Invoke method - a Form IS a Control - to ensure your code is run from the UI Thread.
Also, you have a static BindingList, but I assume you want to display the contents of that list in an specific form. You should make the BindingList an instance member instead... or get a reference to a valid Form. The Control.Invoke method is not static.
There are several ways to do so. I would do it like so:
First, create a method in your Form class that adds the record to the list.
public void AddRecord(Record r) {
if(this.InvokeRequired) {
this.Invoke(new MethodInvoker(() => this.AddRecord(r)));
} else {
this.record_list.Add(r);
}
}
Second, you need to have a reference to the form (in the next step, that is the theForm variable).
Then, in your listener method, invoke AddRecord method instead of adding the record in your BindingList directly.
public static void listen(IPEndPoint server_ip)
{
Console.WriteLine("In listen");
while (true)
{
try
{
byte[] received_bytes = udp_client.Receive(ref server_ip);
string received_data = Encoding.ASCII.GetString(received_bytes);
Record record = JsonConvert.DeserializeObject<Record>(received_data);
theForm.AddRecord(record); // You need a Form instance.
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
The below will work out in winforms. Have tried once. The below code helps to update the label in UI thread from another thread.
string _string = "Call from another thread";
this.Invoke((MethodInvoker)delegate {
label1.Text = _string;
});
You're looking for SynchronizationContext.Current, which works with both WinForms and WPF.
Note that you'll need to grab its value from the UI thread, since it's per-thread.

ObjectDisposedException after closing application on Timer

Im working on a Network application. First I wanted to make DataGridView to refresh its data on every second. My implementation:
public partial class Form1 : Form
{
BindingList<Wifi> Networks = new BindingList<Wifi>();
System.Timers.Timer NetworksRefreshThread;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
NetshScanner netshScanner = new NetshScanner();
NetworksRefreshThread = new System.Timers.Timer(1000);
dataGridView1.DataSource = Networks;
NetworksRefreshThread.Elapsed += delegate
{
BindingList<Wifi> tmp = new BindingList<Wifi>(netshScanner.StartAndParseOutput());
this.Invoke((MethodInvoker)delegate
{
Networks.Clear();
foreach (Wifi net in tmp)
{
Networks.Add(net);
}
});
};
}
After closing the form, I get ObjectDisposedException inside the this.Invoke. Any advice?
What's probably happening is that the timer has fired and is currently busy refreshing your DataGridView. This happens on a seperate thread.
Then when you are closing the form, the GUI thread starts destroying its objects.
After that, the timer is done retrieving its new data but has no object left to put it.
To solve this. Add a boolean to your class called 'Updating' or something similar. Then set its value when you are updating to true.
NetworksRefreshThread.Elapsed += delegate
{
Updating = true;
BindingList<Wifi> tmp = new BindingList<Wifi>(netshScanner.StartAndParseOutput());
this.Invoke((MethodInvoker)delegate
{
Networks.Clear();
foreach (Wifi net in tmp)
{
Networks.Add(net);
}
});
Updating = false;
};
Now create a new method which you bind to the Form closing down event. In this method, do a Thread.Sleep() while the timer is updating:
while (Updating) Thread.Sleep(100);

Multiple Forms in separate threads

I'm trying to run an ATM Simulation in C# with Windows Forms that can have more than one instance of an ATM machine transacting with a bank account simultaneously.
The idea is to use semaphores/locking to block critical code that may lead to race conditions.
My question is this:
How can I run two Forms simultaneously on separate threads? In particular, how does all of this fit in with the Application.Run() that's already there?
Here's my main class:
public class Bank
{
private Account[] ac = new Account[3];
private ATM atm;
public Bank()
{
ac[0] = new Account(300, 1111, 111111);
ac[1] = new Account(750, 2222, 222222);
ac[2] = new Account(3000, 3333, 333333);
Application.Run(new ATM(ac));
}
static void Main(string[] args)
{
new Bank();
}
}
...that I want to run two of these forms on separate threads...
public partial class ATM : Form
{
//local reference to the array of accounts
private Account[] ac;
//this is a reference to the account that is being used
private Account activeAccount = null;
private static int stepCount = 0;
private string buffer = "";
// the ATM constructor takes an array of account objects as a reference
public ATM(Account[] ac)
{
InitializeComponent(); //Sets up Form ATM GUI in ATM.Designer.cs
this.ac = ac;
}
...
I've tried using
Thread ATM2 = new Thread(new ThreadStart(/*What goes in here?*/));
But what method do I put in the ThreadStart constructor, since the ATM form is event-driven and there's no one method controlling it?
EDIT:
I've tried replacing Application.Run(new ATM(ac)); with
ATM atm1 = new ATM(ac);
ATM atm2 = new ATM(ac);
Thread ATM2_T = new Thread(new ThreadStart(atm1.Show));
Thread ATM1_T = new Thread(new ThreadStart(atm2.Show));
ATM1_T.Start();
ATM2_T.Start();
in the Bank constructor. Nothing is displayed and the program drops-off the end of the Main() function.
Here's what I think you need to do:
Thread ATM2 = new Thread(new ThreadStart(ThreadProc));
ATM2.Start();
It calls this method:
private void ThreadProc()
{
var frm = new ATM();
frm.ShowDialog();
}
The above is unsafe code
Please find the threadsafe code:
Thread ATM2 = new Thread(new ThreadStart(ThreadProc));
ATM2.Start();
It calls this method:
private void ThreadProc()
{
if(InvokeRequired)
{
this.Invoke(new Action(() => CreateAndShowForm()));
return;
}
CreateAndShowForm();
}
private void CreateAndShowForm()
{
var frm = new ATM();
frm.ShowDialog();
}
In Bank.Main(), try relpacing Application.Run(new ATM(acc)) with new ATM(acc).Show(). You can use the Form.Show() method as many times as you want. If I recall correctly, the application will close when all forms are closed (although I could be mistaken--try this with the VS debugger)

stop executing code after new form is loaded

Some method call this method which has this code:
Form frm = new Form();
frm.Show();
but i do not want to execute first method anymore after form is loaded. How can i prevent and stop loading code in forst form
Very unclear, I'm guessing that you want to make sure that only one instance of a form can be created. You do so by keeping track of the life of the instance. Like this:
private Form2 instance;
private void showForm2() {
if (instance == null) {
instance = new Form2();
instance.FormClosed += delegate { instance = null; };
instance.Show();
}
else {
instance.WindowState = FormWindowState.Normal;
instance.Focus();
}
}
Edit: question is very unclear so I give an answer based on my understanding of it...
to block execution after a form has been created, until such form is closed, try to use ShowDialog()
using(var frm = new Form1())
{
frm.ShowDialog();
// here your code is not executed until frm is closed...
//...
//...
}
Please pay attention that you do not want to create an object of type Form as that is the default base class and will not contain your controls...

Single instance form but not singleton

I cannot understand how this is possible. Please help!!
I have an app with a trayicon. I want a form to be show when the user double clicks the trayicon. I have a problem where it is possible to get 2 or more forms showing by quickly triple or quadruple clicking the trayicon. The reason I don't want a singleton is that I want the form to be released each time it is closed to save memory, maybe this is not a good idea?
I have a field called m_form1.
I have a method called ShowForm1;
I call the method ShowForm1 on the double-click of the TrayIcon.
private Form1 m_form1;
private void ShowForm1()
{
if (m_form1 == null)
{
Trace.WriteLine("*CREATE*" + Thread.CurrentThread.ManagedThreadId.ToString());
m_form1 = new Form1();
m_form1.FormClosed += new FormClosedEventHandler(m_form1_FormClosed);
m_form1.Show();
}
m_form1.BringToFront();
m_form1.Activate();
}
So when Form1 takes a while to construct, then it is possible to create 2 because m_form1 is still null when the second call arrives. Locking does not seem to work as it is the same thread both calls (I'm guessing the UI thread) ie the trace writes out *CREATE*1 twice (below).
[3560] *CREATE*1
[3560] *CREATE*1
Changing the code to include a lock statement does not help me.
private Form1 m_form1;
private object m_Locker = new object();
private void ShowForm1()
{
lock (m_Locker)
{
if (m_form1 == null)
{
Trace.WriteLine("****CREATE****" + Thread.CurrentThread.ManagedThreadId.ToString());
m_form1 = new Form1();
m_form1.FormClosed += new FormClosedEventHandler(m_form1_FormClosed);
m_form1.Show();
}
}
m_form1.BringToFront();
m_form1.Activate();
}
How should I handle this situation?
Thanks guys
Tim.
Have an additional boolean variable, "m_formUnderConstruction" which you test for before constructing the form, and which you set as soon as you've decided to construct it.
The re-entrancy makes all of this a little icky, unfortunately. I've removed the lock, as if this ever gets called from a different thread then you've got the nasty situation of trying to show a form from a different thread to the one it was constructed on.
private Form1 m_form1;
private bool m_underConstruction = false;
private void ShowForm1()
{
if (m_underConstruction)
{
// We're about to show it anyway
return;
}
m_underConstruction = true;
try
{
if (m_form1 == null)
{
m_form1 = new Form1();
m_form1.FormClosed += new FormClosedEventHandler(m_form1_FormClosed);
m_form1.Show();
}
}
finally
{
m_underConstruction = false;
}
m_form1.BringToFront();
m_form1.Activate();
}
Use Interlocked.Increment to change the nr of the tries. If it is 1, open the form, otherwise, don't. And use Interlocked.Decrement after the test or on form's close.
private int openedForms = 0;
private Form1 m_form1;
private void ShowForm1()
{
if (Interlocked.Increment(ref openedForms) = 1)
{
m_form1 = new Form1();
m_form1.FormClosed += new FormClosedEventHandler(m_form1_FormClosed);
m_form1.Show();
}
else
{
Interlocked.Decrement(ref openedForms);
}
if (m_form1 != null)
{
m_form1.BringToFront();
m_form1.Activate();
}
}
private void m_form1_FormClosed(object Sender, EventArgs args)
{
Interlocked.Decrement(ref openedForms);
}
Please see this, it handles all mouse event combinations for NotifyIcon as well as Form1.
More here: http://code.msdn.microsoft.com/TheNotifyIconExample

Categories