I'm trying to take a console application and build a gui for it. I've got the main functions of my GUI working and added the console application files to my new project. I changed the namespace of the files to match that of the winform application and moved the main function of the console application into the Program class defining it as a function taking a CSV path argument which it gets from the form.
So it appears that program it functioning but Im having trouble getting the form to update. There are some Console.WriteLine() functions that I want to write to the toolStripStatusLabel and others to the richTextBox.
I'm new to C# and the main program wasnt written by me but Im trying to build on it.
Program.cs
Form1 Frm1 = new Form1();
Frm1.UpdateStatusBar("Sorted jobs by EDD....");
Frm1.Refresh();
Form1.cs
public void UpdateStatusBar(string status)
{
Form1 Frm1 = new Form1();
Frm1.toolStripStatusLabel1.Text = status;
}
Pastebin Program.cs
See line 92.
Pastebin Form1.cs
See line 88.
As said by #Thinhbk, creating a new Form1 each time you are wanting to update the tool strip is not the way to to go. The other problem you have is that all of the processing is running in the same thread (I looked at the stuff you posted on Pastebin), meaning that you won't see the progress updates until the end anyway.
To get this working, I first modifed the signature of the public void AX1Program(string path) method to this:
public void AX1Program(Form1 form1, ManualResetEvent resetEvent, string path)
Passing in the form means we can access the UpdateStatusBar method and resetEvent is used to signal when the thread is done. The body of AX1Program changes to this:
try
{
...
//Do work as per normal
...
form1.UpdateStatusBar("some new status");
}
catch (Exception)
{
//Any exception handling/logging you may need.
}
finally
{
//Indicate that we are done.
resetEvent.Set();
}
Now, to invoke the AX1Program method, you currently have some code (it's in a couple of places in your Writebutton_Click method):
Program Pgm1 = new Program();
Pgm1.AX1Program(CSVtextBox.Text);
We want to invoke this asynchronously, so we would instead go:
RunAX1Program(CSVtextBox.Text);
Which invokes the following two methods:
private void RunAX1Program(string text)
{
ManualResetEvent resetEvent = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(RunAX1ProgramDelegate,
new object[] { resetEvent, text });
}
private void RunAX1ProgramDelegate(object state)
{
object[] stateArray = (object[])state;
ManualResetEvent resetEvent = (ManualResetEvent)stateArray[0];
string text = (string)stateArray[1];
Program program = new Program();
program.AX1Program(this, resetEvent, text);
//Wait until the event is signalled from AX1Program.
resetEvent.WaitOne();
}
Because we are now wanting to update the tool strip from another thread, the UpdateStatusBar method will need to look like this so that we can safely modify the controls:
public void UpdateStatusBar(string status)
{
if (this.InvokeRequired)
{
this.BeginInvoke((MethodInvoker)delegate
{
UpdateStatusBar(status);
});
}
else
{
toolStripStatusLabel1.Text = status;
}
}
You could then use a similar pattern to update your rich text box.
I think, the problem is that: you're trying to create new Form1 anytime trying to UpdateStatusBar().
You may fix this by:
public void UpdateStatusBar(string status)
{
this.toolStripStatusLabel1.Text = status;
}
Related
I am trying to start a winForm from a thread, but when i do so, the form show but none of the labels are loaded ( the background where they should be is white ) and the form is frozen.
I've tried it with some other winForms that i know that work just fine and it still doesn't seem to work ? Has anyone encountered this problem ?
I know the question is vague but there isn't really any specific code that I could give to help understand the problem.
That is because the Message Loop runs on UI thread only. And when a control or window is created in any other thread, it cannot access that message loop. And thus, cannot process user input.
To solve this, try creating a window from UI thread and create a thread from that window to do whatever you want to do in different thread.
UI thread is supposed to be one.
Then, I suggest you to open your form calling a method of your original form thread, like in the example below:
(To test it just create an empty form called MainForm and paste this code in it)
public delegate void OpenFormDelegate(string txt);
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
var button1 = new Button();
button1.Text = "Run for 5 secs and open new window";
button1.Dock = DockStyle.Top;
this.Controls.Add(button1);
button1.Click += new EventHandler(button1_Click);
}
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(Run));
t.Start();
}
public void Run()
{
Thread.Sleep(5000); // sleep for 5 seconds
this.BeginInvoke(new OpenFormDelegate(OpenNewForm), "Hello World !");
}
public void OpenNewForm(string text)
{
Form f = new Form();
f.Text = text;
f.Show();
}
}
It is related to thread access, when the new form is created it will not be able to access the UI thread. Use the main thread to create the form and new thread to process the infomation.
I have a form called 'Home' and a class called 'Server', which is threaded. Home has a TextBox that I'd like to append as things happen in Server. What is easiest thread safe way to append the text box that's not in the same class? I see a lot of talk on this subject but nothing seems to answer my question. The other solutions also contain a lot of code for something that seems simple.
I start my thread form Home like so:
public void StartThread()
{
Server s = new Server();
Thread t = new Thread(s.DoWork);
t.Start();
}
class Server
{
public void DoWork()
{
while (!_shouldStop)
{
StartServer();
}
}
public void RequestStop()
{
_shouldStop = true;
}
private volatile bool _shouldStop;
internal void StartServer()
{
try
{
// Server Stuff
// Something happens here and I want to append a string to the text box in Home.
}
catch (Exception)
{
//Exception, append text box with some other string.
}
}
Have the form create a Progress<string> instace,
Attach a handler to the progress changed event that updates the UI appropriately.
Have the worker accept an IProgress<string> instance that it reports progress to.
The Progress class will handle marshaling the code to the UI thread.
This ensures proper separation of UI from non-UI code.
I'm not sure if this is what you mean (Preforming cross threaded operations I assume?) but...
textBox1.Invoke(new MethodInvoker(delegate { textBox1.AppendText("Hi"); }));
This would be to append some text to your textbox safely, you would just have to add the if statement and such.
I'm developing a user interface for a program, and something very strange is happening.
I have a text view, 2 buttons and a progress bar. I redirectioned the output on the console to my text view. so wen I click the buttons I should receive output messages. in the beginning it was fine, but then I used some longer routines, I'm trying to log in into a web service and use web-requests.
my code works almost as It was supposed to work, I can log in and make my web requests just fine. but because the answers can become slow I created some output messages, and there my problem started.. My interface wont update until all the code I created on my event handler end's running. and when that code ends executing, I receive all the output messages all at once. I cant even move the window while the program is running..
I´m programing on c# for my first time, I had to use it because I need to use dll's.. and this kind of problem never happened before. I usually use Java.
It's like the code isn't running on the right order and it doesn´t make sense to me.. because I know my code is right because it runs on the console, and it runs while the program isn't responding..
I cant seem to understand this, should I make my events handling using threads?
class MainClass
{
public static void Main (string[] args)
{
Application.Init ();
UIMain win = new UIMain ();
win.ShowAll ();
Application.Run ();
}
}
public partial class UIMain : Gtk.Window
{
public UIMain () :
base (Gtk.WindowType.Toplevel)
{
System.Windows.Forms.Application.EnableVisualStyles ();
this.Build ();
Console.SetOut (new ControlWritter(this.textview1));
}
protected void OnButton2Clicked (object sender, EventArgs e)
{
if (entry1.Text.Equals(String.Empty) || entry2.Text.Equals(String.Empty)) {
Console.WriteLine("random output");
}
ConstantesSetup.autoSetup ();
button1.Sensitive = true;
if (!ConstantesSetup.var1) {
ConstantesSetup.routine6 ();
ConstantesSetup.routine5 ();
ConstantesSetup.routine4 ();
ConstantesSetup.routine3 ();
ConstantesSetup.routine2 ();
ConstantesSetup.var1 = true;
}
}
protected void OnButton1Clicked (object sender, EventArgs e)
{
switch (ConstantesSetup.erp) {
case "ERP":
eti_scp.autoSync (this);
break;
}
}
}
I'm sorry for the lack of code, but I don't even know were to start looking for the problem..
thanks for your time ;)
You are blocking the UI thread with long running synchronous operations. You need to run these long running operations asynchronously so that the button click event handler can return right away while your tasks run in the background.
There are several options for running tasks asynchronously but one simple option is using a BackgroundWorker. In your event handler you could do something like:
var worker = new BackgroundWorker();
worker.DoWork += (o, args) =>
{
//call long running processes here
};
worker.RunWorkerAsync();
The BackgroundWorker will also dispatch these operations onto the UI thread for you so you can update controls in the form inside the DoWork callback method.
Im pretty new to C# but I have been playing around with it to learn. So far I have a nice app that does things like starts up the screen saver and controls the windows system volume.
However I'm having some trouble and I'm not sure what is wrong. I have already gone through a bunch of similar questions on the site but I don't think any of them apply to my specific case.
So I wanted to control the app from the web. My plan is to have the app check a webpage on my site for a command every couple seconds. Depending on what the site returns the app will do different things(mute, vol up, etc.) and reset the command on the website to blank. The website command part is all done in PHP and is quite simple, there is no problem with that part of it.
I created a button that calls a function that checks the page and performs the action. It worked fine. But when I tried to make it automatically check the site I'm getting errors. I'm pretty sure it is because I moved the check and perform action to a new thread. So lets move on to the code. These are not the full files but what I think you need to know to help. If you need anything more just let me know.
Form1.cs
public Form1(Boolean init = true)
{
if (init)
{
InitializeComponent(); //initialize UI
startWebMonitor(); //starts web monitor thread for remote web commands
}
}
private void startWebMonitor()
{
Thread t = new Thread(WebMonitor.doWork);
t.Start();
}
public IntPtr getWindowHandle()
{
return this.Handle;
}
WebMonitor.cs
public static void doWork()
{
while(true)
{
checkForUpdate();
Thread.Sleep(1000);
}
}
private static void checkForUpdate()
{
lastCommand = getLastCommand();
if (lastCommand.Equals(""))
{
//No Update
}
else
{
processCommand(lastCommand);
}
}
public static void processCommand(String command)
{
if(command.Equals("mute"))
{
VolumeCtrl.mute(Program.form1.getWindowHandle());
}
HTTPGet req2 = new HTTPGet();
req2.Request("http://mywebsite.com/commands.php?do=clearcommand");
}
VolumeCtrl.cs
private static IntPtr getWindowHandle()
{
Form1 form1 = new Form1(false); //false = do not initialize UI again
return form1.getWindowHandle();
}
public static void mute(IntPtr handle)
{
SendMessageW(getWindowHandle(), WM_APPCOMMAND, getWindowHandle(), (IntPtr)APPCOMMAND_VOLUME_MUTE);
}
Alright so basically the mute function requires the window handle in order to work. But when I try to get the window handle from the new thread it throws the error:
Cross-thread operation not valid: Control 'Form1' accessed from a
thread other than the thread it was created on.
So how do I get around this? I have read other answers on here saying you need to use Invoke or Delegate but I'm not sure how those work or where to use them. I tried to follow one of the answers but I got even more errors.
In this case, write invoke operation inside of method. Then you can access both from control's thread and other threads.
delegate IntPtr GetWindowHandleDelegate();
private IntPtr GetWindowHandle() {
if (this.InvokeRequired) {
return (IntPtr)this.Invoke((GetWindowHandleDelegate)delegate() {
return GetWindowHandle();
});
}
return this.Handle;
}
or, if you want to avoid delegate hell, you could write in more few code with built-in delegate and lambda.
private IntPtr GetWindowHandle() {
if (this.InvokeRequired)
return (IntPtr)this.Invoke((Func<IntPtr>)(GetWindowHandle));
return this.Handle;
}
Try this code it will surely help you
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += (s, e) => { };
bw.RunWorkerCompleted += (s, e) =>
{
//Call you windowsform method or anything which you want to do
};
bw.RunWorkerAsync();
I know there are a lot of similar questions out there and I have read through a lot of them. Unfortunately, I still couldn't solve my problem after reading through them - however I am relatively new to C#. According to docs the problem of not being thread safe results in an InvalidOperationException when using the debugger. This is not the case in my problem.
I've recreated the problem with a simple raw test class to concentrate on my problem.
The main form is supposed to show a kind of a progress dialog.
public partial class ImportStatusDialog : Form
{
public ImportStatusDialog()
{
InitializeComponent();
}
public void updateFileStatus(string path)
{
t_filename.Text = path;
}
public void updatePrintStatus()
{
t_printed.Text = "sent to printer";
}
public void updateImportStatus(string clientName)
{
t_client.Text = clientName;
}
public void updateArchiveStatus()
{
t_archived.Text = "archived";
}
}
When that code is called without any Invoke() from the main form:
private void button1_Click(object sender, EventArgs e)
{
ImportStatusDialog nDialog = new ImportStatusDialog();
nDialog.Show();
nDialog.updateFileStatus("test");
Thread.Sleep(1000);
nDialog.updateImportStatus("TestClient");
Thread.Sleep(1000);
nDialog.updatePrintStatus();
Thread.Sleep(1000);
nDialog.updateArchiveStatus();
Thread.Sleep(1000);
nDialog.Close();
}
And even when I call it like this:
private void button3_Click(object sender, EventArgs e)
{
ImportStatusDialog nDialog = new ImportStatusDialog();
nDialog.Show();
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
nDialog.updateFileStatus("Test");
});
}
else
{
nDialog.updateFileStatus("Test");
}
Thread.Sleep(1000);
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
nDialog.updatePrintStatus();
});
}
else
{
nDialog.updatePrintStatus();
}
Thread.Sleep(1000);
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
nDialog.updateImportStatus("cName");
});
}
else
{
nDialog.updateImportStatus("cName");
}
Thread.Sleep(1000);
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
nDialog.updateArchiveStatus();
});
}
else
{
nDialog.updateArchiveStatus();
}
Thread.Sleep(1000);
nDialog.Close();
}
the dialog which looks like this in the designer (in my example)
will be displayed like that:
When I use ShowDialog() instead of Show() the dialog displays correnctly, but as the API Doc points out
You can use this method to display a modal dialog box in your
application. When this method is called, the code following it is not
executed until after the dialog box is closed
which is not what I want, especially as it would mean that the dialog updating would only happen after it has been closed again.
What am I doing wrong here? This seems to be a trivial problem and yet the solution evades me. Please keep in mind that I am new to C# GUI programming.
Also, I would like to ask what would be the right place for using the Invoke()? Do Would you use it in the main form when calling the dialog methods or rather in the dialog update methods itself?
It doesn't look like you're using multiple threads here, so the invoke stuff is not required. Invoke is only needed for cross-thread calls - you are creating multiple forms, but all your code is running in the same thread.
If you're doing your work in the main UI thread (as implied by the code being in a button click event), then just call Application.DoEvents() periodically to allow the progress form to refresh.
A better solution would be to use a BackgroundWorker for your work, and have it report its progress periodically.
Then you can update the progress form in the BackgroundWorker's ProgressChanged event (which will be executed in the main thread, so you still don't have to invoke anything).