Reputation: 300
I'm currently stuck on how to update the text of my textbox whenever any class changes the text. Currently, only my main thread does so, and I have tried various methods (including a dispatch) to update the view from anywhere. My code looks like this:
XAML:
<TextBox x:Name ="textBoxResults" Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}"/>
XAML.CS:
public partial class MainWindow : Window
{
public ConsoleLog cl { get; set; }
public MainWindow()
{
InitializeComponent();
MainWindow_Load();
cl = new ConsoleLog();
DataContext = cl;
}
}
private void ButtonBeginTests_Click(object sender, RoutedEventArgs e)
{
new Thread(() =>
{
App.Current.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, new Action(() =>{
tc = new TestController(.., .., cl); //other args not important
tc.beginTest();
}));
}).Start();
}
ConsoleLog Class:
using System;
using System.ComponentModel;
namespace Test_DesktopApplication.src
{
public class ConsoleLog : INotifyPropertyChanged
{
private string text;
public string Text
{
get { return text; }
set
{
text = value;
OnPropertyChanged("Text");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public void addToLog(string text)
{
App.Current.Dispatcher.Invoke(new Action(delegate { this.Text += text; }));
}
}
A class calls "addToLog" multiple times during a separate thread of background processes. Any indication to what I could be doing wrong?
EDIT: I can actually get this to work by using this after every addToLog call:
App.Current.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, new ThreadStart(delegate { }));
however I don't know if this is a "good" fix.
EDIT 2:
I have updated the MainWindow class to show when this class is called, the class that calls the log is below:
public class Testcontroller
{
ConsoleLog cl;
public GEN2TestController(.., .., ConsoleLog console)
{
//other constructor things
cl = console;
}
public void beginTest(){
testList[0].result = unprogrammedCurrent_test(.., ..); //this is an example of what the test looks like..
cl.addToLog(TestList[0].result);
...//repeat for the rest of the test lists and tests..
...
...
}
The log will not update until all tests are done.
Upvotes: 1
Views: 148
Reputation: 16554
You code example is working fine!
EDIT: There should be no need to force an empty dispatcher cycle like that, this is the equivalent of the old WinForms Application.DoEvents() there must be something else in your code that is blocking the dispatcher (UI thread), perhaps you should share an example of how your background worker is constructed and initiated.
I added a button to your form:
<Button Margin="0,267,376,0" Click="Button_Click" Height="54" VerticalAlignment="Top" HorizontalAlignment="Right" Width="142"/>
And the button click logic in the code behind:
private void Button_Click(object sender, RoutedEventArgs e)
{
cl.addToLog(DateTime.Now.ToString() + Environment.NewLine);
}
I suspect that you issue then is how you are calling addToLog() Most likely you are calling a different instance of the object to the one that is set as the data context.
I have modified your example to include a Background worker that is initiated from the form, this is working quite well:
public partial class MainWindow : Window
{
private void Button_Click(object sender, RoutedEventArgs e)
{
if (worker.IsBusy)
worker.CancelAsync();
else
{
cl.Text = String.Empty;
worker.RunWorkerAsync();
}
}
public ConsoleLog cl { get; set; }
private BackgroundWorker worker = null;
public MainWindow()
{
InitializeComponent();
cl = new ConsoleLog();
DataContext = cl;
worker = new BackgroundWorker { WorkerSupportsCancellation = true };
worker.DoWork += Worker_DoWork;
worker.RunWorkerCompleted += (object sender, RunWorkerCompletedEventArgs e) => cl.addToLog($"{e.Result}");
}
private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
Random r = new Random();
while(true)
{
if ((sender as BackgroundWorker).CancellationPending) break;
cl.addToLog(DateTime.Now.ToString() + Environment.NewLine);
System.Threading.Thread.Sleep(r.Next(500, 3000));
}
e.Result = "Stop" + Environment.NewLine;
}
}
Upvotes: 1