How to change window state of Form, on a different thread? - c#

Does anyone know how I can change the window state of a form, from another thread? This is the code I'm using:
private void button4_Click(object sender, EventArgs e)
{
string pathe = label1.Text;
string name = Path.GetFileName(pathe);
pathe = pathe.Replace(name, "");
string runpath = label2.Text;
Process process;
process = new Process();
process.EnableRaisingEvents = true;
process.Exited += new System.EventHandler(process_Exited);
process.StartInfo.FileName = #runpath;
process.StartInfo.WorkingDirectory = #pathe;
process.Start();
WindowState = FormWindowState.Minimized;
}
private void process_Exited(object sender, EventArgs e)
{
this.WindowState = FormWindowState.Normal;
}
It's meant to run a program and minimize, then return to the normal state once the program has closed. Although I get this error "Cross-thread operation not valid: Control 'Form1' accessed from a thread other than the thread it was created on." Any idea how to get this to work?

This will work in .NET 3.5:
Invoke(new Action(() => { this.WindowState = FormWindowState.Normal; }));
Or 2.0:
Invoke(new MethodInvoker(delegate { this.WindowState = FormWindowState.Normal; }));

Just search this string in StackOverflow "Cross-thread operation not valid" or Google. Please, don't be that lazy.

See What’s the difference between Invoke() and BeginInvoke() on this site. The "chosen" answer gives a good explanation of what you're supposed to do.
Long story short, you want different THREADS not making a new process entirely (or highly unlikely you want that), and you probably want to use Invoke() and not BeginInvoke() which is asynchronous.

Add this line of code to the Click event handler:
process.SynchronizingObject = this;

this will solve your problem add it in the form_load event
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;

Related

How to stop a C# Process that is long and does not loop

I have a specific situation where I have a process that calls a OS command and I need to stop or kill it. Let's say for example it is a continuous process. The process executed "myapplication.exe" for example. Should I not use the background worker and just wrap it in a thread. Then kill the thread? I also thought about sending a CTRL+C to cancel but not sure how to inject that into a process command. What would be the right path?
private void btnExecuteResponseCmd_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
//https://stackoverflow.com/questions/4732737/how-to-stop-backgroundworker-correctly
//works if it is looping and can read the cancel variable but not in this case
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
ProcessStartInfo pStartInfo = new ProcessStartInfo("myapplication.exe");
//https://social.msdn.microsoft.com/Forums/en-US/5880a108-4169-44a5-81f2-6a745438d486/redirecting-command-window-messages-to-rich-text-box
pStartInfo.CreateNoWindow = true;
pStartInfo.UseShellExecute = false;
pStartInfo.RedirectStandardInput = true;
pStartInfo.RedirectStandardOutput = true;
pStartInfo.RedirectStandardError = true;
process1.OutputDataReceived += new DataReceivedEventHandler(OutputHandler);
process1.ErrorDataReceived += new DataReceivedEventHandler(ErrorHandler);
process1.StartInfo = pStartInfo;
process1.SynchronizingObject = rbResponse;
process1.Start();
process1.BeginOutputReadLine();
process1.WaitForExit();
}
Ok, I found how to control the cancellation and looks like it kills the process just fine. I will have to explore if it releases all resources, and this is the best way to do it.
private void btnStopCommand_Click(object sender, EventArgs e)
{
process1.CancelOutputRead();
process1.Kill();
}

Form freezes when opened for second time

I am developing a Windows Forms application that access a WCF service. I ran into a great problem that I can't predict the reason of it. Even the Visual Studio debugger not showing any exception in the Output view. The scenario is like this, I have a custom user control that has a linkLabel on it. Whenever the link label is clicked, a form is opened and a class object is passed to it. The class definition of this object resides on WCF service on a remote server. Now the problem is that when I click the linkLabel, the form opens perfectly loading each of its component according to the class object passed to it. But when I close this form and click that linkLabel again, the form opens but immediately freezes after loading some elements. I tried many code variations. Edited many part of code that I think can affect. But none of them showed the difference. Since, I don't know where actually is the code has error, I am posting the linkLabel click code and functions that are called after it is clicked.
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
Enabled = false;
string temp = Title.Text;
Title.Text = "Opening...";
System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(openTopic));
t.Start();
Title.Text = temp;
Enabled = true;
}
void createTopicWindow()
{
TopicViewer t = new TopicViewer(t);
Invoke(new Action(() => t.Show()));
}
private void openTopic()
{
Invoke(new Action(() => createTopicWindow()));
}
The above is the edited code, since I was getting Cross thread exception before.
Following is the code of constructor of the form that is called when clicked the linkLabel:
try
{
InitializeComponent();
this.t = topic;
if (IsHandleCreated == false)
CreateHandle();
System.Threading.Thread th = new System.Threading.Thread(new System.Threading.ThreadStart(loadTopic));
th.Start();
Common.openedTopics.Add(this);
AddComment addComment1 = new AddComment();
addComment1.Topic = t;
addComment1.Dock = DockStyle.Fill;
panel5.Controls.Add(addComment1);
backgroundWorker1.RunWorkerAsync();
}
catch (Exception)
{ }
void loadTopic()
{
Invoke(new Action(()=>tHead = new TopicHeader()));
Global.SetControlPropertyThreadSafe(tHead,"Topic", t);
Global.SetControlPropertyThreadSafe(tHead,"Dock", DockStyle.Fill);
Invoke(new Action(()=>panel1.Controls.Add(tHead)));
Global.SetControlPropertyThreadSafe(this,"Text", t.Title + " - Topic Viewer");
if (t.Description.Trim().Length > 0)
{
Global.SetControlPropertyThreadSafe(webBrowser1, "DocumentText", t.Description);
}
else
{
Invoke(new Action(() => tabControl1.TabPages[0].Dispose()));
}
Global.SetControlPropertyThreadSafe(tabPage2, "Text", "Comments (" + client.getComCount(t.TopicID) + ") ");
}
TopicHeader is another small user control.
Please anyone tell me the solution to this?
If you are using .Net 4.5, then using async/await would be easiest solution. That way, you don't need any Invokes
async private void Form1_Load(object sender, EventArgs e)
{
string s = await Task<string>.Factory.StartNew(LongRunningTask,
TaskCreationOptions.LongRunning);
this.Text = s;
}
string LongRunningTask()
{
Thread.Sleep(10000);
return "------";
}
I can't give a direct answer to you question, but this may give a hold on.
public void Form_Load()
{
// do some stuff on the gui-thread
// i need to do something that takes a long time:
ThreadPool.QueueUserWorkItem((state) =>
{
// i'll execute it on the ThreadPool
// Long running code....
// update results in mainform on gui thread.
Invoke(new Action( delegate
{
// because the invoke will execute this on the gui-thread, you'll able to update controls.
// update my gui controls.
DataGrid.Source = myReceiveDataThing;
}));
}
}
You might expand the code, to check if the form is still valid.

how to work timer in background

i can run timer but it is hanging , when i run background i need timer to be run in background.
can anyone say me how to run timer in background.
My timer code is
btnIntraday.Enabled = false;
btnStartBackfill.Enabled = false;
btnStop.Enabled = true;
if (btnIntraday.Text == "Intraday")
{
timerIntraday.Interval = 5000;
timerIntraday.Enabled = true;
btnIntraday.Text = "Updating..";
}
else if (btnIntraday.Text == "Updating..")
{
timerIntraday.Enabled = false;
btnIntraday.Text = "Intraday";
}
and my background code is
btnIntraday.Enabled = false;
btnStartBackfill.Enabled = false;
btnStop.Enabled = true;
txtInterval.Text = ddTimeInterval.Value.ToString();
int inter = (int.Parse(txtInterval.Text)) * multiplyingFactorBackfill;
try
{
bgBackfillDCX.RunWorkerAsync();
}
catch (Exception ex)
{
}
can anyone please say me how to run timer in background.
Thanks in advance.
You may use a BackgroundWorker.
Handle the BackgroundWorker.DoWork to run your timer.
Handle the BackgroundWorker.ProgressChanged to handle timing events.
Handle the BackgroundWorker.RunWorkerCompleted to stop the timer.
I think what you are saying is that you want to set off the background code then have IT periodically do some work. If so you need a timer in the background code not the front end. Which means using a different timer class - in System.Timers, IIRC. System.Timers.Timer
If You insist on having the timer working at the front end (though I don't recommend it, I can understand it is easier in some scenarios), there is a way (note: I presume Windows Forms are used):
In the following code, there is:
a timer referring to an instance of System.Windows.Forms.Timer object
a notifyIcon referring to an instance System.Windows.Forms.NotifyIcon object
MainWindow_Resize method associated with Resize event of the form
notifyIcon_Click method associated with Click event of the notifyIcon
The resize method if-block is executed when user minimizes form (form is not visible anywhere in desktop and setting ShowInTaskBar to false hides it from task bar as well, so it is effectively hidden).
private void MainWindow_Resize(object sender, EventArgs e)
{
if (WindowState == FormWindowState.Minimized)
{
this.ShowInTaskbar = false;
notifyIcon.Visible = true;
}
}
When user clicks notification icon, the form's window is restored to its former size and position and the form is brought to user
private void notifyIcon_Click(object sender, EventArgs e)
{
notifyIcon.Visible = false;
this.WindowState = FormWindowState.Normal;
this.ShowInTaskbar = true;
this.BringToFront();
this.Activate();
}

Read from console process

I have a process, i can start, and hide working fine, but i want to read from the console program, when i runs, not after, i tried to run a timer, anbd read at the tick, but my program just crashes and when it not do, i get nothing at all.
startInfo= new ProcessStartInfo("cmd.exe");
startInfo.Arguments ="/C uus.exe "+ arg.ToString();
startInfo.RedirectStandardError = true;
startInfo.RedirectStandardOutput = true;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
this.timer1.Enabled=true;
this.listBox1.Items.Clear();
p= Process.Start(startInfo);
Application.DoEvents();
void Timer1Tick(object sender, EventArgs e)
{
string str="";
str=p.StandardOutput.ReadLine();
if(str != null)
{
this.Text=str.ToString();
this.listBox1.Items.Add(str);
}
Application.DoEvents();
}
So what do i do to solve this?
Update:
I tried bender suggestion
now My program don't crash anymore, but also don't recvie any data
proc.StartInfo.UseShellExecute=false;
proc.StartInfo.CreateNoWindow=true;
proc.StartInfo.RedirectStandardOutput=true;
proc.StartInfo.RedirectStandardError=true;
proc.StartInfo.FileName="uus.exe";
proc.StartInfo.Arguments=arg;
proc.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(SortOutputHandler);
proc.Start();
proc.BeginOutputReadLine();
void SortOutputHandler(object o,System.Diagnostics.DataReceivedEventArgs e)
{
string str="";
string str2="";
str=e.Data.ToString();
if(str!=null && str!="")
{
this.listBox1.Items.Add(str.ToString());
this.Text=str.ToString();
}
str2=proc.StandardOutput.ReadLine();
if(str2!=null && str2!="")
{
this.lsw1.Items.Add(str2.ToString());
}
}
hmm?
Update:
I have changed the handler, because i have being tell, it can't do it, that it wil be cross thread operation, usualyy i wille have get an error if it was.
private delegate void TextAdderDelegate(string str);
void TextAdder(string str)
{
if(this.lsw1.InvokeRequired==true)
{
Invoke(new TextAdderDelegate(TextAdder),new object[] {str});
}
else
{
this.lsw1.Items.Add(str);
}
}
void SortOutputHandler(object o,System.Diagnostics.DataReceivedEventArgs e)
{
string str="";
if(e!=null)
{
if(e.Data!=null)
{
str=e.Data.ToString();
}
}
TextAdder(str);
}
The problem is that you're running on one thread and trying to write using another. When you created your background thread using the Timer's tick event, it can't have frontend user input.
Perhaps if you explained the big picture of what you're trying to accomplish, we can better help you.
In the meantime, you might want to create threadsafe writes. This article will help you to understand the problem and solution to writing to form controls on different threads.
You may create the Process instance explicitly (e.g. new Process)and use the OutputDataReceived event, the method BeginOutputReadLine() and, when finished CancelOutputRead() for that.
The event OutputDataReceived will be repeatedly called asynchronously from a different thread as soon output data is available.
I assume you get an 'thread cross exception', this may be caused because you're updating your form controls on an other thread then the UI thread.

WPF application in a loop, how to not have the whole application freeze?

I am having fun with WPF and got a problem. I have googled and found this website that has the same problem of me but without any working solution.
The problem is that I have a button that do some processing of data (around 30 sec). I want to have the button to disable and to have log writing in a text box... the problem is that it doesn't disable and it doesn't wrote any thing on the textbox until the processing is completely done.
Any idea?
private void button1_Click(object sender, RoutedEventArgs e)
{
this.button1.IsEnabled = false;
//Long stuff here
txtLog.AppendText(Environment.NewLine + "Blabla");
//End long stuff here
this.button1.IsEnabled = true;
}
As others have said, use the BackgroundWorker or some other method of doing work asychronously.
You can declare it under your Window, initialize it somewhere like the Loaded event, and use it in the Click event. Here's your method, modified to use BackgroundWorker, assuming you've declared it under the Window as _bw:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_bw = new BackgroundWorker();
_bw.DoWork += new DoWorkEventHandler((o, args) =>
{
//Long stuff here
this.Dispatcher.Invoke((Action)(() => txtLog.AppendText(Environment.NewLine + "Blabla")));
});
_bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler((o, args) =>
{
//End long stuff here
this.Dispatcher.Invoke((Action)(() => this.button1.IsEnabled = true));
});
}
private void button1_Click(object sender, RoutedEventArgs e)
{
this.button1.IsEnabled = false;
_bw.RunWorkerAsync();
}
Note that anything that modifies your UI from another thread must be done within a Dispatcher.Invoke or Dispatcher.BeginInvoke call, WPF does not allow you to get or set DependencyProperty values from any thread but the one where the object was created (more about this here).
If you wanted to read from txtLog instead of modifying it, the code would be the same:
//Long stuff here
this.Dispatcher.Invoke((Action)(() =>
{
string myLogText = txtLog.Text;
myLogText = myLogText + Environment.NewLine + "Blabla";
txtLog.Text = myLogText;
}));
That operation is being performed on the UI thread. This means that it will block the Windows message pump from processing until it has completed. no pump = no UI updates. You should launch the job on another thread. I don't know WPF, but in C# I would use either the Thread or BackgroundWorker classes.
do it async. create a backgroundworker process to handle the data and the application will continue to respond. MSDN Resources on the Class. Since WPF is using C# (or VB.net) you can still use the same types of threading objects. I've used the background worker successfully in a WPF app myself.

Categories