Cross-threading C# [duplicate] - c#

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 6 years ago.
I have been learning before about threading but in C++. Now for the first time I'm trying to configure this code to work corectly but without CheckIllegal ... = false.
I have been trying to put delegates in there and lots of other stuff but the I getting the same problem. Both threads are entering the methof WriteInLog and I can't really see how to make this work.
Anyone has an idea and explanation?
namespace viseNitniRad
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private int firstNum = 0;
private int secondNum = 0;
public Thread firstThread;
public Thread secondThread;
public void WriteInLog(string message)
{
lock (textBox3)
{
textBox3.Text += message + Environment.NewLine;
}
}
private void CheckInput()
{
int pom = 0;
firstNum = int.Parse(textBox1.Text);
secondNum = int.Parse(textBox2.Text);
if (firstNum > secondNum) {
pom = secondNum;
secondNum = firstNum;
firstNum = pom; }
WriteInLog("Prvi broj: " + firstNum.ToString());
WriteInLog("Drugi broj: " + secondNum.ToString());
}
private void button1_Click(object sender, EventArgs e)
{
CheckInput();
}
public delegate void ThreadSum();
public delegate void ThreadUmn();
public void Threadsumm()
{
int suma = 0;
for (int i = firstNum; i < secondNum; i++)
suma += i;
WriteInLog("Suma= " + suma.ToString() + " kraj: " + DateTime.Now.ToString());
}
public void ThreadUmno()
{
int umnozak = 1;
for (int i = firstNum; i < secondNum; i++)
umnozak*= i;
WriteInLog("Umnozak= " + umnozak.ToString() + " kraj: " + DateTime.Now.ToString());
}
private void button2_Click(object sender, EventArgs e)
{
WriteInLog("Pocetak svih izracuna u: " + DateTime.Now.ToString());
firstThread = new Thread(new ThreadStart(Threadsumm));
secondThread = new Thread(new ThreadStart(ThreadUmno));
firstThread.Start();
secondThread.Start();
}
}
}

To access control from non-UI thread you have to use Control.Invoke method https://msdn.microsoft.com/en-us/library/a1hetckb(v=vs.110).aspx
Instead of Threads you can use BackgroundWorker
https://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx
Also:
WinForm Multithreading. Use backgroundWorker or not?

If anyone comes here and want to know exactly the answer on this question, here it is.
I didn't want to change anything so I learn something about invoke. Furthermore, here's the 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 viseNitniRad
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private int firstNum = 0;
private int secondNum = 0;
public Thread firstThread;
public Thread secondThread;
public void WriteInLog(string message)
{
if (this.textBox3.InvokeRequired)
{
ThreadSum ts = new ThreadSum(WriteInLog);
this.Invoke(ts, new object[] { message });
}
else
{
this.textBox3.Text += message + Environment.NewLine;
}
}
private void CheckInput()
{
int pom = 0;
firstNum = int.Parse(textBox1.Text);
secondNum = int.Parse(textBox2.Text);
if (firstNum > secondNum) {
pom = secondNum;
secondNum = firstNum;
firstNum = pom; }
WriteInLog("Prvi broj: " + firstNum.ToString());
WriteInLog("Drugi broj: " + secondNum.ToString());
}
private void button1_Click(object sender, EventArgs e)
{
CheckInput();
}
public delegate void ThreadSum(string message);
public delegate void ThreadUmn();
public void Threadsumm()
{
int suma = 0;
for (int i = firstNum; i < secondNum; i++)
suma += i;
WriteInLog("Suma= " + suma.ToString() + " kraj: " + DateTime.Now.ToString());
}
public void ThreadUmno()
{
int umnozak = 1;
for (int i = firstNum; i < secondNum; i++)
umnozak*= i;
WriteInLog("Umnozak= " + umnozak.ToString() + " kraj: " + DateTime.Now.ToString());
}
private void button2_Click(object sender, EventArgs e)
{
WriteInLog("Pocetak svih izracuna u: " + DateTime.Now.ToString());
firstThread = new Thread(new ThreadStart(Threadsumm));
secondThread = new Thread(new ThreadStart(ThreadUmno));
firstThread.Start();
secondThread.Start();
}
}
}
As you can see I made little change in function WriteInLog, where I deleted lock method and put an condition if invokeRequired to initialize Delegate to run the same method and than Invoke him, if not, just to update the same textbox3.
Thank you all! :)

Related

c# - my items are not moving from listbox1 to listbox2

this is how my GUI looks like
I was working on a project for uni
what it does is reading a text-file
put it in listbox1
then the second button should take the students who succeed to listbox2
but whenever I press the button I get an error
I tried rly hard to search everywhere, but couldn't find the problem
it's just not writing it in listbox2 for no reason
anyone know what should I do?
what do I do to make it work???
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.IO;
namespace second_Project
{
public partial class FSS : Form
{
private void FSS_Load(object sender, EventArgs e)
{
}
public FSS()
{
InitializeComponent();
}
public class StdScore
{
public int id;
public string name;
public double xam, Score, Pract;
public string content;
public string[] xx;
}
OpenFileDialog ofd = new OpenFileDialog();
private void button1_Click(object sender, EventArgs e)
{
StdScore StdScore = new StdScore();
ofd.Filter = "TXT|*.txt";
if (ofd.ShowDialog() == DialogResult.OK)
{
StreamReader sr = new StreamReader(ofd.FileName);
while (!sr.EndOfStream)
{
StdScore.content = sr.ReadLine();
string[] info = StdScore.content.Split(' ');
StdScore.xx = new string[info.Length];
listBox1.Items.Add(StdScore.content);
}
sr.Close();
}
}
private void button2_Click(object sender, EventArgs e)
{
StdScore StdScore = new StdScore();
StdScore.xam = 0;
StdScore.Pract = 0;
StdScore.Score = 0;
StdScore.xam += int.Parse(StdScore.xx[2]);
StdScore.Pract += int.Parse(StdScore.xx[3]);
StdScore.Score = StdScore.xam * 0.7 + StdScore.Pract * 0.3;
if (StdScore.xam >= 40 && StdScore.Pract >= 40 && StdScore.Score >= 60)
{
StdScore.xam = (StdScore.xam * 70) / 100;
StdScore.Pract = (StdScore.Pract * 30) / 100;
StdScore.Score = StdScore.xam + StdScore.Pract;
listBox2.Items.Add(StdScore.xx[0] + " " + StdScore.xx[1] + " " + StdScore.xx[2] + " " + StdScore.xx[3] + " " + StdScore.Score + " " + "Succes");
}
}
}
}
When reading the file, you have listBox1.Items.Add(StdScore.content);. This simply adds a string to the ListBox. You should create instances of StdScore INSIDE the while loop and add those instances directly to the ListBox. It's a very bad practice to name your variable the exact same as the class itself. I would expect to see something more like StdScore curScore = new StdScore();. Now you can use curScore and it's clear this is an instance of the class and not the class itself, and you're not trying to access a static member. For class StdScore, you can override the ToString() method to control what is displayed in the ListBox.
So here is StdScore with the ToString() override:
public class StdScore
{
public int id;
public string name;
public double xam, Score, Pract;
public string content;
public string[] xx;
public override string ToString()
{
return content;
}
}
Next, reading the file:
while (!sr.EndOfStream)
{
StdScore curScore = new StdScore();
curScore.content = sr.ReadLine();
curScore.xx = curScore.content.Split(' ');
listBox1.Items.Add(curScore);
}
On to button2 where you want to move students who succeed over to listBox2. Here you need to ITERATE over all the stored StdScore instances that are within listBox1:
private void button2_Click(object sender, EventArgs e)
{
foreach(StdScore curScore in listBox1.Items)
{
curScore.xam = int.Parse(curScore.xx[2]);
curScore.Pract = int.Parse(curScore.xx[3]);
curScore.Score = curScore.xam * 0.7 + curScore.Pract * 0.3;
if (curScore.xam >= 40 && curScore.Pract >= 40 && curScore.Score >= 60)
{
curScore.xam = (curScore.xam * 70) / 100;
curScore.Pract = (curScore.Pract * 30) / 100;
curScore.Score = curScore.xam + curScore.Pract;
string success = curScore.xx[0] + " " + curScore.xx[1] + " " +
curScore.xx[2] + " " + curScore.xx[3] + " " + curScore.Score + " Success";
listBox2.Items.Add(success);
}
}
}
Note that from an Object Oriented Programming perspective, none of this code is correct. The code can be much improved. I simply took your existing code and changed it to "make it work"...at least I think it will; if not, it should give you some good ideas on where to make changes.

freezing form after clicking button

I have a problem regarding to payment button.
I don't encounter an error before building, but after building and clicking the payment button it hangs. I think its because of lblTotalPrice.text, but I don't know how to fix it.
public partial class Form1 : Form
{
int totalCost;
public double holDer;
public Form1()
{
InitializeComponent();
this.cbo1.Items.AddRange(new object[] { "Lechon Kawali - 200", "Bicol Express - 300"
,"Adobo - 350" });
}
private void btnAdd_Click(object sender, EventArgs e)
{
lb1.Items.Add(cbo1.SelectedItem);
lb1.SelectedIndex = lb1.SelectedIndex ;
int z = 0;
if (cbo1.SelectedIndex == 0)
{
z = z + 1;
}
if (cbo1.SelectedIndex == 1)
{
z = z + 2;
}
if (cbo1.SelectedIndex == 2)
{
z = z + 3;
}
switch(z)
{
case 1:
totalCost = totalCost + 200;
break;
case 2:
totalCost = totalCost + 300;
break;
case 3:
totalCost = totalCost + 350;
break;
}
lblSubTotalCost.Text = ("Php " + totalCost.ToString());
}
private void btnDelete_Click(object sender, EventArgs e)
{
int deleteCost = 0;
int itemCost = 0;
foreach (int selectedIndex in lb1.SelectedIndices)
{
itemCost = int.Parse(lb1.Items[selectedIndex].ToString().Split('-')[1]);
deleteCost += itemCost; lb1.Items.RemoveAt(selectedIndex);
}
totalCost = totalCost - deleteCost;
lblSubTotalCost.Text = ("Php " + totalCost.ToString());
lb1.Items.Remove(lb1.SelectedItem);
if (lb1.Items.Count > 0)
{
lb1.SelectedIndex = 0;
}
else
MessageBox.Show("No orders");
}
private void lblVAT_TextChanged(object sender, EventArgs e)
{
Add();
}
private void lblSubTotalCost_TextChanged(object sender, EventArgs e)
{
multiply();
Add();
}
public void multiply()
{
int a;
double b = 0.12;
bool Valid = int.TryParse(totalCost.ToString(), out a);
if (Valid)
lblVAT.Text = (a * b).ToString();
else
lblVAT.Text = "No VAT entered";
}
private void lbTotalPrice_TextChanged(object sender, EventArgs e)
{
Add();
}
public void Add()
{
int a;
int b;
bool AValid = int.TryParse(totalCost.ToString(), out a);
bool BValid = int.TryParse(lblVAT.Text, out b);
if (AValid && BValid)
{
lblTotalPrice.Text = ("Php " + (a + b).ToString());
}
}
private void btnPayment_Click(object sender, EventArgs e)
{
holDer = double.Parse(tbPayment.Text) - double.Parse(lblTotalPrice.Text);
MessageBox.Show("Change: " + holDer.ToString());
}
}
As you stated in your comments, your problem it is not that your application hangs, but the problem is that you are getting a Input string was not in a correct format Exception.
That it seems is from this block:
private void btnPayment_Click(object sender, EventArgs e)
{
holDer = double.Parse(tbPayment.Text) - double.Parse(lblTotalPrice.Text);
MessageBox.Show("Change: " + holDer.ToString());
}
As it is a not very complex code, your problem seems to be when you are casting your textboxes to Double. Be sure that you are using the correct separator for double values and that you not have any strange characters in tbPayment
Try using TryParse method:
double res;
if(double.TryParse(tbPayment.Text, out res))
{
holDer = res - double.Parse(lblTotalPrice.Text);
MessageBox.Show("Change: " + holDer.ToString());
}
else
{
MessageBox.Show("Input a correct format");
}
I suspect it's because the lblTotalPrice_TextChanged event-handler calls Add() which then changes the lblTotalPrice.Text property, causing the event to fire again, ad -infinitum?

C# bowling calculator. Array of classes returning null

I am working on a project in c# visual studio in which i am attempting to make a bowling calculator. i have made a class for frames which contains a throw1 and throw2. I first fill the array of classes with one button and calculate score after game completion. when i go to calculate score it tells me i have null values.below is my form code the class is just
class frames
{
public int intThrow1;
public int intThrow2;
}
the main form is
public partial class Form1 : Form
{
frames[] frame = new frames[11];
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
int intFrst = int.Parse(textBox1.Text);
int intScnd = int.Parse(textBox2.Text);
if (lbxScorecard.Items.Count <= 21)
{
int intIndx = 0;
if (intFrst == 10)
{
frame[intIndx] = new frames();
frame[intIndx].intThrow1 = intFrst;
frame[intIndx].intThrow2 = 0;
lbxScorecard.Items.Add(frame[intIndx].intThrow1);
lbxScorecard.Items.Add(frame[intIndx].intThrow2);
intIndx += 2;
}
else
{
frame[intIndx] = new frames();
frame[intIndx].intThrow1 = intFrst;
frame[intIndx].intThrow2 = intScnd;
lbxScorecard.Items.Add(frame[intIndx].intThrow1);
lbxScorecard.Items.Add(frame[intIndx].intThrow2);
intIndx++;
}
}
else
{
MessageBox.Show("max throws");
}
}
private void button2_Click(object sender, EventArgs e)
{
int intScore = 0;
for (int index = 0; index <= 11; index++)
{
if (frame[index].intThrow1 == 10 && index < 9) // here is where it throws a null exception stating the index may be empty
{
if (frame[index + 1].intThrow1 != 10)
{
intScore = intScore + frame[index].intThrow1 + frame[index + 1].intThrow1 + frame[index + 1].intThrow2;
}
else if (frame[index].intThrow1 == 10)
{
intScore = intScore + frame[index].intThrow1 + frame[index + 1].intThrow1 + frame[index + 2].intThrow1;
}
}
else if (frame[index].intThrow1 + frame[index].intThrow2 == 10 && index < 9) //it was throwing the same exception here until i added && index<9 in the if statements.
{
intScore = intScore + frame[index].intThrow1 + frame[index].intThrow2 + frame[index + 1].intThrow1;
}
else
{
intScore = intScore + frame[index].intThrow1 + frame[index].intThrow2;
}
}
MessageBox.Show(intScore.ToString());
}
}
Problem is:
frames[] frame = new frames[11];
You also need to instantiate each element of the array like you did in the button1_Click function:
frame[intIndx] = new frames();
Also note that you have a class variable frame and the same one in button2_Click which is probably a bad idea.

Class is not returning an object

it's not recognizing my lstSectionNumber (Tickets.TicketForm.lastSectionNumber is inaccessible due to its protection level). Hopefully I explained everything clearly, I'm just recently started learning C#, any help would be appreciated. Thank you in advance.
public class TicketOrder
{
private static int premiumTicket = 40;
private static int basicTicket = 20;
private static int serviceCharge = 2;
private TicketForm form = new TicketForm();
public double CalcTicketCost(int section, double quantity)
{
double amount = 0;
if (int.Parse(form.lstSectionNumber.SelectedItem.Value) <= 150)
{
amount = (quantity*premiumTicket) + (quantity*serviceCharge);
return amount;
}
else
{
amount = (quantity*basicTicket) + (quantity*serviceCharge);
}
return amount;
}
}
Code-behind:
public partial class TicketForm : System.Web.UI.Page
{
int myInt;
public const double premiumTicket = 40;
public const double basicTicket = 20;
public const double serviceCharge = 2;
TicketUser user = new TicketUser();
TicketOrder order = new TicketOrder();
protected void Page_Load(object sender, EventArgs e)
{
// This disables the unobtrusive validation in order to see the result
this.UnobtrusiveValidationMode = System.Web.UI.UnobtrusiveValidationMode.None;
// This will keep my five numbers in my list box without adding more with a refresh of a page
if (!IsPostBack)
{
Random RandomSection = new Random();
for (int i = 0; i < 5; i++)
{
// Created a list box with five random numbers from 1 to 300
myInt = RandomSection.Next(1, 300);
lstSectionNumber.Items.Add(myInt.ToString());
}
}
}
protected void btnSubmit_Click(object sender, EventArgs e)
{
// I also added TextMode="Number"to my <asp:TextBox ID="txtNumberOfTickets"
// runat="server" Height="18px" Width="134px"></asp:TextBox> code in order
// to get rid of an error incase I left my txtNumberOfTicket empty
int section = Convert.ToInt32(lstSectionNumber.SelectedValue);
double ticketQuantity = Convert.ToInt32(txtNumberOfTickets.Text);
//This label displays users first and last names, number of tickers, seat section number and ticket price
lblPrice.Text = txtFirstName.Text + " " +
txtLastName.Text + ", " +
txtNumberOfTickets.Text + " " +
"Tickets in Section:" + " " +
lstSectionNumber.SelectedValue + "," +
" Total Cost is: " +
order.CalcTicketCost(section, ticketQuantity).ToString("C");
}
protected void btnCreateAccount_Click(object sender, EventArgs e)
{
user.firstName = txtFirstName.Text;
user.lastName = txtLastName.Text;
user.username = txtUserName.Text;
lblCreateAccount.Text = user.CreateAccount();
}
}
Simply give your constructor an empty body
change
public TicketOrder()
to
public TicketOrder() { }
TO solve your other problem, you are trying to make a NEW page, but asp.NET has already made a page for this page load and populated the values for you. You should Pass the current page object into your TicketOrder constructor and save instead.
Like so:
public TicketOrder(TicketForm form)
{
this.form = form;
}
and change in TicketOrder
private TicketForm form = new TicketForm();
to
private TicketForm form;
and change in TicketForm
TicketOrder order = new TicketOrder();
to
TicketOrder order;
public TicketForm()
{
this.order = new TicketOrder(this);
}
Well, I figured it out.
if (section <= 150)
{ amount = (quantity * premiumTicket) + (quantity * serviceCharge);
return amount;

How can I correct the error "accessed from a thread other than the thread it was created on"?

This following code gives me the error below . I think I need "InvokeRequired" . But I don't understand how can I use?
Cross-thread operation not valid: Control 'listBox1' accessed from a thread other than the thread it was created on.
The code:
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace WindowsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected static DataSet dataset = null;
private void Form1_Load(object sender, EventArgs e)
{
}
private void timer1_Tick(object sender, EventArgs e)
{
SimulationFrameWork.MCSDirector run = new SimulationFrameWork.MCSDirector();
DataSet ds = run.Get();
if (ds.Tables[0].Rows.Count > 0)
{
for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
{
if (ds.Tables[0].Rows[i]["result"].ToString() == "0")
{
dataset = run.Get(int.Parse(ds.Tables[0].Rows[i]["ID"].ToString()));
WorkerObject worker =
new WorkerObject(
int.Parse(dataset.Tables[0].Rows[i]["ID"].ToString()),
int.Parse(dataset.Tables[0].Rows[i]["Iteration"].ToString()),
listBox1, timer1);
Thread thread1 = new Thread(new ThreadStart(worker.start));
thread1.Start();
}
}
}
}
}
public class WorkerObject
{
private int id;
private int nmax;
private ListBox list1;
private System.Windows.Forms.Timer timer1;
public WorkerObject(int _id, int _nmax, ListBox _list1,
System.Windows.Forms.Timer _timer1)
{
id = _id;
nmax = _nmax;
list1 = _list1;
timer1 = _timer1;
}
public void start()
{
timer1.Stop();
int i, idaire, j;
double pi = 0.0, x, y;
Random rnd = new Random();
for (i = 0; i < 100; i++)
{
idaire = 0;
for (j = 0; j < nmax; j++)
{
x = rnd.Next(1, 10000) / (double)10000;
y = rnd.Next(1, 10000) / (double)10000;
if (Math.Pow(x, 2) + Math.Pow(y, 2) <= 1.0)
idaire += 1;
}
pi = 4 * (double)idaire / (double)nmax;
nmax *= 10;
list1.Items.Add(
"Iterasyon:" +
nmax.ToString() +
" ----->" +
pi.ToString() +
"\n");
System.Threading.Thread.Sleep(100);
}
SimulationFrameWork.MCSDirector run = new SimulationFrameWork.MCSDirector();
run.Update(id, pi);
list1.Items.Add("\n\n islem bitti...");
}
}
}
This should get you around it
private delegate void stringDelegate(string s);
private void AddItem(string s)
{
if (list1.InvokeRequired)
{
stringDelegate sd = new stringDelegate(AddItem);
this.Invoke(sd, new object[] { s });
}
else
{
list1.Items.Add(s);
}
}
Just call AddItem and this will invoke the add using a delegate if it is required otherwise it will just add the item directly to the box.
OneSHOT
Just encapsulate adding the text to the listbox to another method:
private void timer1_Tick(object sender, EventArgs e)
{
// ...
AddTextToListBox("\n\n işlem bitti...");
}
private void AddTextToListBox(string text)
{
if(list1.InvokeRequired)
{
list1.Invoke(new MethodInvoker(AddTextToListBox), new object[] { text });
return;
}
list1.Items.Add(text);
}
Can also use lambda notation. So, instead of:
formControl.Field = newValue; //Causes error
Try:
Invoke(new Action(() =>
{
formControl.Field = newValue; //No error
}));
Add this code before starting the thread:
//kolay gelsin kardeş )
CheckForIllegalCrossThreadCalls = false;
thread1.Start();

Categories

Resources