Reputation: 8995
I recently made a post about this but I think my last question was not well written and I did get a answer but I want to see if there is a simpler solution as I found the last one confusing. This time I have tried to write it as clear as possible.
I have the following code for a WPF application here is the code
Code from MainWindow.xaml.cs
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
Thread[] threads = new Thread[3];
for (int i = 0; i < 3; i++)
{
int index = i;
threads[i] = new Thread(new ThreadStart(test));
threads[i].SetApartmentState(ApartmentState.STA);
threads[i].IsBackground = true;
threads[i].Start();
}
}
public void test()
{
OutputWindow outputwindow = new OutputWindow();
outputwindow.Show();
System.Windows.Threading.Dispatcher.Run();
outputwindow.textBox1.Text = "writing";
//some more stuff done
//some more stuff done
//some more stuff done
outputwindow.textBox1.Text = "\nmore writing";
//some more stuff done
//some more stuff done
//some more stuff done
outputwindow.textBox1.Text = "\nmore writing";
}
How can I make textBox1.Text actually get updated as test() is being executed?
Regards!
EDIT
Thanks for your answers, but I was unable to make it work yet.
This is a network application so I think a textbox would be more suitable than data binding since I want to print timeouts, pings, and a lot more info throughout the program to make sure everything is going as planned.
I tried some of your answers but I was unable to make it work. This is the last example which I tried and it doesn't work
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
Thread[] threads = new Thread[3];
for (int i = 0; i < 3; i++)
{
int index = i;
threads[i] = new Thread(new ThreadStart(test));
threads[i].SetApartmentState(ApartmentState.STA);
threads[i].IsBackground = true;
threads[i].Start();
}
}
public void test()
{
OutputWindow outputwindow = new OutputWindow();
outputwindow.Show();
System.Windows.Threading.Dispatcher.Run();
outputwindow.textBox1.Text = "writing";
//some more stuff done
//some more stuff done
//some more stuff done
outputwindow.textBox1.Text = "\nmore writing";
//some more stuff done
//some more stuff done
//some more stuff done
Action action = () => outputwindow.textBox1.Text = "\nmore writing";
Dispatcher.Invoke(action);
}
Upvotes: 0
Views: 586
Reputation: 218
I've answered your last question and then I saw this one. I’ve concluded you wanted another opinion so you asked again and I didn’t mean to answer on this but I see you haven't gone far so I'll give it another try.
Problem with your code sample is that you call Dispatcher.Run(). The problem is that you really must call Dispatcher.Run to keep your window with text box alive and responsive but inside that Dispatcher.Run method is infinite loop. You are stuck on Dispatcher.Run line of code until the Dispatcher is shut downed and window is closed. Only then will your code continue and execute those outputWindow.textBox1.Text set statements but now it will be too late. Solution to your problem is separation of outputWindow from your „work code“.
The simplest modification to your code would be to put your „work code“ in another thread so you wouldn't be blocked with Dispatcher.Run(). Here's how should your sample code look (since you prefer creating the outputWindow in the same method).
// all good here
public MainWindow()
{
InitializeComponent();
}
// also all good here
private void button1_Click(object sender, RoutedEventArgs e)
{
Thread[] threads = new Thread[3];
for (int i = 0; i < 3; i++)
{
int index = i;
threads[i] = new Thread(new ThreadStart(test));
threads[i].SetApartmentState(ApartmentState.STA);
threads[i].IsBackground = true;
threads[i].Start();
}
}
// here's the change
public void test()
{
OutputWindow outputwindow = new OutputWindow();
outputwindow.Show();
// here you create another thread which will execute your work code (the one you had after the Dispatcher.run statement
Thread workThread = new Thread(new ParameterizedThreadStart(workMethod));
workThread.IsBackground = true;
// start the work thread BUT transfer the reference to outputwindow
workThread.Start(outputwindow);
System.Windows.Threading.Dispatcher.Run();
// no more code here; it has been transferd to workMethod which runs in another thread
}
public void workMethod(object threadParam)
{
OutputWindow outputwindow = (OutputWindow)threadParam;
// those are little ugly but you must go through dispatcher because you are now in a different thread than your outputwindow
outputwindow.Dispatcher.BeginInvoke((Action)(() => { outputwindow.textBox1.Text = "writing"; }), System.Windows.Threading.DispatcherPriority.Normal);
//some more stuff done
//some more stuff done
//some more stuff done
outputwindow.Dispatcher.BeginInvoke((Action)(() => { outputwindow.textBox1.Text = "\nmore writing"; }), System.Windows.Threading.DispatcherPriority.Normal);
//some more stuff done
//some more stuff done
//some more stuff done
outputwindow.Dispatcher.BeginInvoke((Action)(() => { outputwindow.textBox1.Text = "\nmore writing"; }), System.Windows.Threading.DispatcherPriority.Normal);
// and finally close the outputWindow
outputwindow.Dispatcher.InvokeShutdown();
}
Upvotes: 1
Reputation: 359
As everyone else is writing here you need to invoke if you want to change on UI objects. But have you given it a thaught to maybe use data binding instead? It's a great way to work with WPF to actually bind the data instead (in your case a string then).
There are some tutorial here: http://www.wpftutorial.net/DataBindingOverview.html
In the long run you even would like to look on MVVM pattern but it's at least a start to start to use data binding.
Upvotes: 0
Reputation: 178780
The problem is that the updates won't be handled until the current method exits. You'll need to pump the message pump in order to have them apply before the current method exits. See here for an example of how to do so:
public static void DoEvents(this Application application)
{
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, new VoidHandler(() => { }));
}
private delegate void VoidHandler();
That said, I think your design is wanting and could be greatly improved by the use of more standard mechanisms such as the BackgroundWorker
.
Upvotes: 0
Reputation: 180
It have to use invoke with dispather to update controls. You can refer links as below.
http://www.switchonthecode.com/tutorials/working-with-the-wpf-dispatcher http://www.albahari.com/threading/part2.aspx#_Rich_Client_Applications
Upvotes: 0