This question already has answers here:
WinForm Application UI Hangs during Long-Running Operation
(3 answers)
Closed 6 years ago.
if I start my C# Windows Form Application, buttons hangs, because it is endless cycle. I want to see the change of variable value from button2 click into infinite loop of button1 through the global variable
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 XX_5
{
public partial class Form1 : Form
{
private int g;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
int i = 0;
for (;;)
{
textBox1.AppendText("ID: [" + i++ + "] Variable value: [" + g + "]\n");
}
}
private void button2_Click(object sender, EventArgs e)
{
g = 1;
}
}
}
If you keep the infinite-loop in the button-click event handler, your application hangs definitely, since windows can't get / dispatch /process messages any more.
You can fix like the following:
private void button1_Click(object sender, EventArgs e)
{
int i = 0;
textBox1.AppendText("ID: [" + i++ + "] Variable value: [" + g + "]\n");
}
private void button2_Click(object sender, EventArgs e)
{
// int g = 1; // here you declare a local variable
g = 1; // use the member variable instead
}
Take a look at the Timer control (under the Components tab in the toolbox). You can put your code (without the for loop) in there and it will run every x milliseconds with the benefit that it will not hang. You will need to define your i variable at the form level when you do this. Your timer can then access this 'global variable'.
Something like this...
public partial class Form1 : Form
{
private int i = 0;
private int g = 0;
public Form1()
{
InitializeComponent();
}
private void button2_Click(object sender, EventArgs e)
{
g = 1;
}
private void timer1_Tick(object sender, EventArgs e)
{
textBox1.AppendText("ID: [" + i++ + "] Variable value: [" + g + "]\n");
}
private void Form1_Load(object sender, EventArgs e)
{
timer1.Enabled = true;
}
}
Related
maybe i need a timer ?
i want that before the image is saved or after saved but to display the images one by one.
now it's just doing the loop so it's not showing the designer at all until the loop will end.
using Accord.Video.FFMPEG;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Net.Mime.MediaTypeNames;
namespace Extract_Frames
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
using (var vFReader = new VideoFileReader())
{
vFReader.Open(#"C:\Users\Chocolade 1972\Downloads\MyVid.mp4");
for (int i = 0; i < vFReader.FrameCount; i++)
{
Bitmap bmpBaseOriginal = vFReader.ReadVideoFrame();
//bmpBaseOriginal.Save(#"d:\frames\frame" + i + ".bmp");
pictureBox1.Image = bmpBaseOriginal;
//bmpBaseOriginal.Dispose();
}
vFReader.Close();
}
}
}
}
It's working for while but after some images it's throwing exception on the line :
pictureBox1.Image = bmpBaseOriginal;
the exception say the object is in use.
System.InvalidOperationException: 'Object is currently in use
Your code should probably be something like this:
private readonly Queue<Image> images = new Queue<Image>();
private void Form1_Load(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
using (var vFReader = new VideoFileReader())
{
vFReader.Open(#"C:\Users\Chocolade 1972\Downloads\MyVid.mp4");
for (var i = 0; i < vFReader.FrameCount; i++)
{
images.Enqueue(vFReader.ReadVideoFrame());
}
// Not sure that this would be required as it might happen implicitly at the end of the 'using' block.
vFReader.Close();
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
pictureBox1.Image = images.Dequeue();
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
pictureBox1.Image.Dispose();
var image = images.Dequeue();
pictureBox1.Image = image;
if (images.Count == 0)
{
timer1.Stop();
}
}
All the Images are added to a queue and then dequeued one by one. The first one is displayed as soon as the loading has completed and the rest will be displayed at regular intervals. This code displayed each Image once and then destroys and discards it. If you want to display them all again when you reach the last then you can use a different type of collection and wrap back to the beginning when you get to the end.
EDIT:
I may have misunderstood a little what you were asking for. This code should display the Images as they're generated:
private void Form1_Load(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
using (var vFReader = new VideoFileReader())
{
vFReader.Open(#"C:\Users\Chocolade 1972\Downloads\MyVid.mp4");
for (var i = 0; i < vFReader.FrameCount; i++)
{
backgroundWorker1.ReportProgress(0, vFReader.ReadVideoFrame());
}
// Not sure that this would be required as it might happen implicitly at the end of the 'using' block.
vFReader.Close();
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pictureBox1.Image?.Dispose();
pictureBox1.Image = (Image)e.UserState;
}
You can ditch the Timer and the Queue. My only concern is that this may lead to an OutOfMemoryException. If it does, you can call explicitly call GC.Collect intermittently, e.g. every 100 frames.
I have the following code:
protected void Button1_Click(object sender, EventArgs e)
{
count1++;
}
I want the counter count1 to increase every time someone clicks on the button. However, so far it only counts the first click, and not any other following, so it's always stuck on 1. Outside, I have declared it and set its initial value to 0. Where is the problem? (I use visual studio 2015)
Edit: This is the whole code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class Statistics : System.Web.UI.Page
{
int count1 = 0, count2 = 0;
string minima = "Καλησπέρα";
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
TextBox1.Text = minima;
Label1.Text = "Αυτή την στιγμή χρησιμοποιούν τον ιστότοπο " + Convert.ToString(Application["UserCount"]) + " χρήστες";
}
}
protected void Button1_Click(object sender, EventArgs e)
{
TextBox2.Text = TextBox1.Text;
count1++;
Label2.Text = "Κλικ από προηγούμενους χρήστες: " + Convert.ToString(count1);
Label3.Text = "Τρέχοντα κλικ: " + Convert.ToString(count1 + count2);
}
}
(yes I know count2 is not changing its value at any point, the code is still in progress regarding that, as it is there for another reason so let's ignore it for now)
Your counters are reseting every postback
try this
static int count1,count2;
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
TextBox1.Text = minima;
Label1.Text = "Αυτή την στιγμή χρησιμοποιούν τον ιστότοπο " + Convert.ToString(Application["UserCount"]) + " χρήστες";
count1 = 0;
count2 = 0;
}
}
I know this questions gets asked a bit (at least from what I found here so far), but I can't really wrap my head around it. Already tried it with the example from msdn but still no succes. Here is what I'm trying to do: I have a USB-Counter connected to a TLL-ruler. I want to read the value constantly in a loop and write the readout to a textbox without blocking the main UI. I know from other questions that I should use Invoke or Backgroundworker, but have not really found an example which I understood and could use to adjust my code accordingly. The code without any modification, to keep it simple, is as follows:
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.Runtime.InteropServices;
using System.Windows.Forms;
namespace USB_Counter
{
public partial class Form1 : Form
{
[DllImport("HS_UC.dll", EntryPoint = "HS_UC_Close")] //+further DLL imports (driver for USBCounter)
public static extern int HS_UC_Close(int CounterNo);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e) //initialize COunter
{
int A = HS_UC_Init(1, 3);
string B = Convert.ToString(A);
MessageBox.Show(B); //to check if there is no error
}
private void button2_Click(object sender, EventArgs e)
{
HS_UC_SetDistCode(1, 20, 40, 4, 0, 0); //set Reference
HS_UC_SetRefMode(1, 1);
}
private void button3_Click(object sender, EventArgs e)
{
int a = 1;
int A = 0; //variable that takes counter value (the one I want)
int B = 0; //variable that takes counter status
do
{
HS_UC_GetCounter(1, ref A, ref B);
decimal C = (Convert.ToDecimal(A) / 100);
textBox1.Text = "Das ist: " + C;
textBox1.Update();
} while (a == 1);
}
}
}
Now this works as intendet, but as mentioned it blocks the main UI thread (obviously). If anyone found a similar question with some helpful tips to get started with this multithreading topic or any helpful tips regarding my question directly, that would be greatly appreciated.
Best regards from Berlin,
Chris
Update: got it working with following attempt:
private void Counter_Read()
{
int a = 1;
do
{
int A = 0;
int B = 0;
HS_UC_GetCounter(1, ref A, ref B);
decimal C = (Convert.ToDecimal(A) / 100);
UpdateTextBox(C);
} while (a == 1);
}
public void UpdateTextBox(decimal C)
{
if (InvokeRequired)
{
this.Invoke(new Action<decimal>(UpdateTextBox), new object[] { C });
return;
}
textBox1.Text = "Das ist: " + C;
textBox1.Update();
}
private void button3_Click(object sender, EventArgs e)
{
System.Threading.Thread t = new System.Threading.Thread(() => Counter_Read());
t.Start();
}
From that I get a decimal output which i constantly updating and still am able to utilize the other buttons.
outsource the loop code into a method. Inside the method you will need to use BeginInvoke to write to the TextBox
private void DoTheLoop()
{
int a = 1;
int A = 0; //variable that takes counter value (the one I want)
int B = 0; //variable that takes counter status
do
{
HS_UC_GetCounter(1, ref A, ref B);
decimal C = (Convert.ToDecimal(A) / 100);
textBox1.BeginInvoke(new Action(()=>{textBox1.Text = "Das ist: " + C;}));
} while (a == 1);
}
First version using a normal Thread:
Create a Thread and start it with the new method when the button3 is clicked
private void button3_Click(object sender, EventArgs e)
{
System.Threading.Thread t = new System.Threading.Thread(()=>DoTheLoop());
t.Start();
}
This should not block your GUI and the textbox will show the values
Second Version using a BackgroundWorker:
Create a BackgroundWorker and register the DoWork event:
System.ComponentModel.BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
private void Form1_Load(object sender, EventArgs e)
{
worker.DoWork += Worker_DoWork;
}
inside the eventhandler call the same method DoTheLoop():
private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
DoTheLoop();
}
start the worker in the button click event:
private void button1_Click(object sender, EventArgs e)
{
worker.RunWorkerAsync();
}
Same result in the end :)
You may want to take a look a this link MSDN.
However, a quick tip would be to register a method for the DoWork event and then execute the RunAsynchronously method.
What the application should do
This application should take the input of time (seconds, minutes and hours) and shutdown the computer after that time. It should also update the text box with how long left until the computer has shut down.
What the application actually does
I had an issue that I 'fixed' where the called ac across threads weren't safe, so I fixed it and I don't get that error now. However, the updateThread doesn't update and print the time left; and the text box doesn't get "test" appended to it. The UI also becomes Not Responding. Any help would be much appreciated.
Also, if you see anything else that could be done better, please comment and explain. Thanks!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ShutdownPC
{
public partial class Form1 : Form
{
int inputHours;
int inputMinutes;
int inputSeconds;
Thread sleepingThread;
Thread updatingThread;
NotifyIcon shutdownPCIcon;
Icon defaultIcon;
public Form1()
{
InitializeComponent();
defaultIcon = new Icon("defaultIcon.ico");
shutdownPCIcon = new NotifyIcon();
shutdownPCIcon.Icon = defaultIcon;
shutdownPCIcon.Visible = true;
MenuItem progNameMenuItem = new MenuItem("ShutdownPC by Conor");
MenuItem breakMenuItem = new MenuItem("-");
MenuItem quitMenuItem = new MenuItem("Quit");
ContextMenu contextMenu = new ContextMenu();
contextMenu.MenuItems.Add(progNameMenuItem);
contextMenu.MenuItems.Add(breakMenuItem);
contextMenu.MenuItems.Add(quitMenuItem);
shutdownPCIcon.ContextMenu = contextMenu;
shutdownPCIcon.Text = "ShutdownPC";
quitMenuItem.Click += QuitMenuItem_Click;
}
private void QuitMenuItem_Click(object sender, EventArgs e)
{
shutdownPCIcon.Dispose();
sleepingThread.Abort();
updatingThread.Abort();
this.Close();
}
public void sleepThread()
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(sleepThread));
}
else {
textBox1.Enabled = false;
textBox2.Enabled = false;
textBox3.Enabled = false;
button1.Enabled = false;
int totalMilliseconds = ((inputHours * 3600) + (inputMinutes * 60) + inputSeconds) * 1000;
Thread.Sleep(totalMilliseconds);
//Process.Start("shutdown", "/s /t 0");
richTextBox1.AppendText(String.Format("test"));
}
}
public void updateThread()
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(updateThread));
}
else {
int totalSeconds = (inputHours * 3600) + (inputMinutes * 60) + inputSeconds;
while (totalSeconds > 0)
{
TimeSpan time = TimeSpan.FromSeconds(totalSeconds);
string timeOutput = time.ToString(#"hh\:mm\:ss");
richTextBox1.AppendText(String.Format(timeOutput));
Thread.Sleep(1000);
richTextBox1.Clear();
totalSeconds--;
}
}
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
inputHours = Convert.ToInt32(textBox1.Text);
inputHours = int.Parse(textBox1.Text);
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
inputMinutes = Convert.ToInt32(textBox2.Text);
inputMinutes = int.Parse(textBox2.Text);
}
private void textBox3_TextChanged(object sender, EventArgs e)
{
inputSeconds = Convert.ToInt32(textBox3.Text);
inputSeconds = int.Parse(textBox3.Text);
}
private void button1_Click(object sender, EventArgs e)
{
updatingThread = new Thread(new ThreadStart(updateThread));
updatingThread.Start();
sleepingThread = new Thread(new ThreadStart(sleepThread));
sleepingThread.Start();
}
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
}
}
}
Using Invoke in the beginning of method that runs in separate thread is bad idea, because all code runs in GUI thread and lock it.
You should Invoke only GUI updating code!!!
Im trying to make a cookie maker!
it works great and all but every time i click on the cookie, it takes it about 2 clicks to add just one to the cookie count. I want to make the counting system go faster, since if i click 2 times a second, it will only count as one atm...
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 FormalCookie
{
public partial class Form1 : Form
{
int cnum = 0;
int add = 1;
public Form1()
{
InitializeComponent();
label1.Text = ("Price For Mouse:" + " 50");
textBox1.Text = ("Cookies:" + " " + cnum);
mouse.Enabled = false;
for (int i = 0; i< add; i++)
{
if (add > 1 && add % 2 == 0)
{
cnum += 1 * add;
}
Thread.Sleep(1000);
}
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
//Mouse
if (cnum >= add * add * 50)
{
cnum -= (add * 100);
add+=2;
}
}
private void button2_Click(object sender, EventArgs e)
{
Application.Restart();
}
private void button3_Click(object sender, EventArgs e)
{
Application.Exit();
}
private void pictureBox1_Click(object sender, EventArgs e)
{
if (cnum >= (add * add * 50))
{
mouse.Enabled = true;
}
else
{
mouse.Enabled = false;
}
cnum += (add * add);
textBox1.Text = ("Cookies:" + " " + cnum);
label1.Text = ("Price For Mouse:" + " " + (add * add * 50));
}
private void label1_Click(object sender, EventArgs e)
{
}
private void pictureBox1_Click(object sender, MouseEventArgs e)
{
}
}
}