Ensure GUI isn't locked up by incoming messages - c#

I have a UserControl that was built following the MVVM pattern with an exposed function for other apps to send "commands" for the control to do. The commands in this case are strings. I'm trying to find a way to stop the GUI from hanging when a lot of commands are being sent in a short period. Each command should wait for the last one to finish.
Most of these commands do work on a 3rd party map control that is displayed in the main control's view.
The flow goes like this:
App sends command string to control.
Control calls a parse function to parse the string.
After parsing is complete, a certain class is called depending on the command.
Stuff happens i.e. create a model, update ObservableCollection, update the map control, etc.
Here's an example:
The usercontrol:
///The code behind for the control
public partial class MainControl : UserControl
{
public MainControl()
{
InitializeComponent();
}
//Other apps call this function
public void ExecuteCommand(string command)
{
CommandParser.StartParse(command);
}
}
Class to parse the commands:
//Handles parsing a string command and calling the right class
public static class CommandParser
{
public static void StartParse(string command)
{
//parses the command into a string array to hold different parts
DoCommand(parsedCommand);
}
private static void DoCommand(string[] command)
{
switch(command[0])
{
case "addpoint":
AddCommand.AddObj(command);
break;
case "createstyle":
CreateCommand.CreateObj(command);
break;
}
}
}
Two classes that take the parsed command and do something:
//Adds objects to the third party map control
public static class AddCommand
{
public static void AddObj(string[] command)
{
//Adds a point to the third party map control
MapControl.AddPoint(new Point(90, -90)); //just an example
}
}
//Creates model objects to add to observablecollections in viewmodels
public static class CreateCommand
{
public static void CreateObj(string[] command)
{
//create a model
//get the correct viewmodel
viewModel.StylesCollection.Add(styleModel); //StylesCollection is an ObservableCollection
}
}
Very basic example but should show the flow of everything. So imagine getting a a few thousands commands; Creating a model is fast, but because the map control (which is part of the GUI) is being updated every time, or an ObservableCollection (that has a control's itemsource bound to it) is being modified, the GUI hangs when receiving and doing all these commands.

In (the probably unlikely) case that there is a considerable amount of work that can be done off the UI thread, you may implement multi threading. A very basic way of doing this would be as so.
First, create a new thread to run:
var task = new Thread(YourTask);
task.Start();
Then in the thread method where the calculations are done, delegate the result to the UI thread by calling Dispatcher.Invoke. Make sure you don't call Invoke too often (e.g. not more than 10 times per second), as this will again block the UI thread.
public void YourTask()
{
// do calculations and get results
Application.Current.Dispatcher.Invoke(
new Action(() =>
{
// update the UI
}));
}

Related

C# string becoming empty

Relatively new to C# and coding in general (first post here). I have a WinForms local application where some information is displayed to the user in a ReadOnly(true) RichTextBox. Almost all my classes need to send information to that RichTextBox. To simplify this process, I created a method inside a static class that uses a locked delegate to send the information to that RichTextBox. Here is a sample:
static class MyClass
{
public delegate void MessageReceivedEventHandler(string message);
public static event MessageReceivedEventHandler messageReceivedEventHandler;
public static void MessageBox(string message)
{
lock (messageReceivedEventHandler)
{
//Thread.Sleep(20);
messageReceivedEventHandler?.Invoke(message);
}
}
}
partial class MyForm : Form
{
public MyForm()
{
MyClass.messageReceivedEventHandler += OnMessageReceived;
}
private void OnMessageReceived(string message)
{
richTextBox1.Text = richTextBox1.Text.Insert(0, $" {message}\n");
}
private void Button1_click()
{
MyClass.MessageBox("This should be working!");
//Add more work here...
}
}
The code above would simply print "This should be working!" inside the RichtTextbox.
The problem is the text from richTextBox1 sometimes becoming empty. This issue seems to appear when the MessageBox method is being called in rapid succession. My assumption was that since I have diffent Tasks running at the same time (in other parts of my code), it probably is two Tasks attempting to use the same static ressource, hence the use of Lock. But I still have the issue.
Adding the Thread.Sleep(20) seems to fix the problem, but that is far from elegant/robust. It starts breaking up again when the time inside Sleep is <10ms.
Edit 1:
To clarify what I mean by "string becoming empty", it means the text from richTextBox1 is == "" at some points, which should not happen since the code is always inserting the text, not replacing it. The OnMessageReceived method is the only place where action is taken on the RichTextBox text.
Edit 2:
I saw many questions related to the other tasks running. First, yes it is a multi-threaded application. The only relation between those tasks and my main form is the "print" function I wrote above. To give more context, this application is used to control the position of stepper motors relative to an electrical signal. When doing so, I need to print important information in my main form. This is why losing the information in my RichTextBox (where I print the information) is an issue. The possible reason of why I am losing the text inside that RichTextBox should be the focus of this thread.
Keep in mind that this is a personnal side project, and not a large scale application.
Thanks,
Laurent
There are multiple problems in your code.
First, you should not lock on a public object, since that allows other threads to lock on the same object, risking interlocking your threads. Second, your symptoms suggest multiple threads are trying to access the ressources. Rather than depending on complex thread locking code, you'd rather schedule UI operations on the UI context, which will allow calling adding message from background tasks.
The best way to do that is to that is by using Control.BeginInvoke()
You can't copy your form instance everywhere, so we'll expose a static method. You could make the class a singleton, but if you need multiple instances that won't work. I'll give a more versatile example. When the static method is called, you don't have access to the form instance anymore, so we'll use IOC pattern with an event and delegate.
Let's make a private static event that all instances will register a callback to in the constructor. When the static method raises the static event, all instances callback will be called. The callback will schedule a modification of its text box.
partial class MyForm : Form
{
private class MessageWriteRequestedEventArgs : EventArgs
{
public string Message { get; }
public MessageWriteRequestedEventArgs(string message)
{
Message = message;
}
}
private static event EventHandler<MessageWriteRequestedEventArgs> MessageWriteRequested;
public MyForm()
{
MessageWriteRequested += OnMessageWriteRequested;
}
public static void WriteMessage(string message)
{
MessageWriteRequested?.Invoke(this, new MessageWriteRequestedEventArgs(message));
}
private void OnMessageWriteRequested(object sender, MessageWriteRequestedEventArgs e)
{
richTextBox1.BeginInvoke(() => WriteMessageSafe(e.message));
}
private void WriteMessageSafe(string message)
{
richTextBox1.Text = richTextBox1.Text.Insert(0, $" {message}\n");
}
private void Button1_click()
{
// you're on ui context, you're safe to access local ui resources
WriteMessageSafe("This should be working!");
// if you have multiple MyForm instances, you need to use the event
WriteMessage("Broadcasting my tralala");
}
}
If you need to write to the textbox from anywhere else :
// do stuff
MyForm.WriteMessage("Ho Ho Ho !");
.NET already includes a class for reporting progress (or any other information) from an asynchronous operation in a thread-safe manner, Progress< T>. It doesn't need locking and even better, it decouples the sender and receiver. Many long-running BCL operations accept an IProgress<T> parameter to report progress.
You haven't explained what's going on in the form, or what task is reporting the data. Assuming the producer is another method in the same form, you could create a Progress<T> instance in the same method that starts the async operation, eg :
async void Button1_Click()
{
var progress=new Progress<string>(ReportMessage);
ReportMessage("Starting");
await Task.Run(()=>SomeLongOp(progress));
ReportMessage("Finished");
}
void SomeLongOp(IProgress<string> progress)
{
for(int i=0;i<1000000;i++)
{
...
progress.Report($"Message {i}");
...
}
}
void ReportMessage(string message)
{
richTextBox1.Text = richTextBox1.Text.Insert(0, $" {message}\n");
}
By using IProgress< T>, the SomeLongOp method isn't tied to a specific form or global instance. It could easily be a method on another class
Publishing lots of messages
Let's say you have a lot of workers, doing a lot of things, eg monitoring a lot of devices, and want all of them to publish messages to the same Log textbox or RTF box. Progress< T> "simply" executes the reporting delegate or event handler on its original sync context. It doesn't have an asynchronous Report method, nor can it queue messages. In a really high-traffic environment, the synchronization switch can delay all workers.
The built-in answer to this is to use one of the pub/sub classes like ActionBlock< T> or a Channel.
An ActionBlock< T> processes the messages in its input queue in order, using a worker task that runs on the ThreadPool by default. This can be changed by specifying a different TaskScheduler in its execution options. By default, its input queue is unbounded.
One could use an ActionBlock to receive messages from multiple workers and display them on a textbox. The block can be created in the constructor, and passed to all workers as an ITargetBlock<T> interface :
ActionBlock<string> _logBlock;
public MyForm()
{
var options=new ExecutionDataFlowBlockOptions {
TaskScheduler=TaskScheduler.FromCurrentSynchronizationContext();
};
_block=new ActionBlock<string>(ReportMessage,options);
}
Now the fun begins. If the workers are created by the form itself, the workers can publish to the block directly :
public async void Start100Workers_Click(...)
{
var workers=Enumerable.Range(0,100)
.Select(id=>DoWork(id,_block));
await Task.WhenAll(workers);
}
async Task DoWork(int id,ITargetBlock<string> logBlock)
{
.....
await logBlock.SendAsync(message);
...
}
Or the block could be exposed through a public property, so other classes/forms in the application can post to it.
public ITargetBlock<string> LogBlock=>_block;
I'm going to show a simple way to do what I think you're after.
I started with a .NET Core 3.1 Win forms application. I added a rich text control to the form. I added a button to the form.
I added a TaskCompletionSource as a instance property - this will be used to control the tasks acting as workers which you described.
CancellationTokenSource sharedCancel = new CancellationTokenSource();
I created an interface to represent something that accepts messages as you described:
public interface IMyMessageSink
{
Task ReceiveMessage(string message);
}
I made my form support this interface.
public partial class Form1 : Form, IMyMessageSink
The ReceiveMessage method looks like this:
public Task ReceiveMessage(string message)
{
if(this.sharedCancel == null || this.sharedCancel.IsCancellationRequested)
return Task.FromResult(0);
this.Invoke(new Action<Form1>((s) => this.richTextBox1.Text = this.richTextBox1.Text.Insert(0, $"{message}\n")), this);
return Task.FromResult(0);
}
You'll see the Invoke handles the synchronization back to the UI thread.
This should probably use BeginInvoke and then convert the APM to async tasks which you can read about here. But for an SO answer the above simple code will suffice.
Also note there's no error handling. You'll want to add that to your generator and to the button handler.
Next I created a class to represent something that creates messages. This class takes the interface created and the cancellation token. It looks like this:
public class MyMessageGenerator
{
CancellationToken cancel;
IMyMessageSink sink;
public MyMessageGenerator(CancellationToken cancel, IMyMessageSink sink)
{
this.cancel = cancel;
this.sink = sink;
}
public async Task GenerateUntilCanceled()
{
try
{
while (!this.cancel.IsCancellationRequested)
{
await sink.ReceiveMessage(this.GetHashCode().ToString());
await Task.Delay(5000, this.cancel);
}
}
catch (OperationCanceledException)
{ }
}
}
In the button handler we create the message generators.
async void button1_Click(object sender, EventArgs e)
{
if (null == this.sharedCancel)
return;
await Task.Run(() => new MyMessageGenerator(this.sharedCancel.Token, this).GenerateUntilCanceled());
}
Finally I added an override for the form closing event:
protected override void OnClosing(CancelEventArgs e)
{
if (null != this.sharedCancel)
{
this.sharedCancel.Cancel();
this.sharedCancel.Dispose();
this.sharedCancel = null;
}
base.OnClosing(e);
}
If the application becomes larger and more complex you would likely benefit by adding services exposed using a DI container. You can read about adding DI to a winforms app here.

Implementing a Windows form that changes during run-time based on a Listener class

I am currently designing a GUI for a Leap Motion device (although my problem could arise with any type of listener device).
I am running my application in a Windows Form with the following code:
public partial class FormRecord : Form
{
public FormRecord()
{
InitializeComponent();
}
public void UpdateGUI(Frame frame)
{
//get information from captured frame
//update the GUI (specifically labels)
customLabel0.Text = someValueFromFrame.ToString();
}
//this method code was provided by Leap Motion on their website
public static void RunMain()
{
// Create a sample listener and controller
SampleListener listener = new SampleListener();
Controller controller = new Controller();
//Controller is included in Leap.dll (from SDK) and is an instance of controller
// Have the sample listener receive events from the controller
controller.AddListener(listener);
Console.WriteLine("Press Enter to quit...");
Console.ReadLine();
// Remove the sample listener when done
controller.RemoveListener(listener);
controller.Dispose();
}
//This class was also provided in the Leap Motion SDK
class SampleListener : Listener
{
private Object thisLock = new Object();
private void SafeWriteLine(String line)
{
lock (thisLock)
{
Console.WriteLine(line);
}
}
public override void OnInit(Controller controller)
{
SafeWriteLine("Initialized");
}
public override void OnConnect(Controller controller)
{
SafeWriteLine("Connected");
}
public override void OnDisconnect(Controller controller)
{
//Note: not dispatched when running in a debugger.
SafeWriteLine("Disconnected");
}
public override void OnExit(Controller controller)
{
SafeWriteLine("Exited");
}
public override void OnFrame(Controller controller) //
//public void OnFrame(Controller controller)
{
SafeWriteLine("Framed");
//Do something useful with the captured frame
// get the current frame
Frame frame = controller.Frame();
/* This is where my problem occurs */
// attempt to use information from this form and call UpdateGUI(Frame frame)
}
}
}
Basically, the problem is arising because of the difference in static vs. non-static nature of the two classes. For example, my application (Windows Form) is non-static because it is a form and you must be able to create several instances by nature. However, that is why I cannot simply call:
Form aForm = new Form();
aForm.UpdateGUI();
^^This creates a new form. I want to be able to fun the UpdateGUI() method on the original form.
I have also tried
guiForm = ActiveForm;
and calling the label from there
ActiveForm.customlbl0.Text = valueToDisplay;
More generally, I am trying to update a Windows Form based on data received from a Listener. This is causing me problems because I am having to struggle between static and non-static members. I would make it all one class, however I need to implement both form and Listener for the two components. How can I update a display with real-time data from a listener?
Edit: The listener is an implementation from the SDK file "LeapCSharp.Net4.0dll"
I would make the constructor of SampleListener take an instance of your form class. When you create the Listener pass in a reference to the form and store it in a private field. That way you can call methods on it from inside the listener.
Another option would be to make your SampleListener class raise events for each event. You can then hook the events from inside your form.
How about something like:
public partial class FormRecord : Form
{
Controller controller;
public FormRecord()
{
InitializeComponent();
controller = new Controller();
}
public void UpdateGUI()
{
Frame frame = controller.frame();
//get information from captured frame
//update the GUI (specifically labels)
customLabel0.Text = someValueFromFrame.ToString();
}
If you use listeners be aware that each listener callback is called on its own thread. You usually cannot update objects accessed directly by a GUI framework (like Windows Forms, etc) from any thread other than the main thread. (I don't know the best way to do cross-thread communication in Windows forms, though.)

Call methods in WPF window from main thread

Use case
I'm developing a small application in C# that is called by another application to retrieve data from the Internet. It runs as a process on its own, but almost all of the interaction with it, is managed by the calling application. Therefor it does not have a GUI. However I'd like to add a progress bar using WPF that is shown during certain data retrievals that could take up to a minute. It's fairly easy to make an estimate of how much work is done and how much is left and therefor I find a progress bar suitable.
Research done
I have a fair understanding of threading after reading large parts of Albahari's pdf on threading (http://www.albahari.info/threading/threading.pdf). I have also read through a lot of posts on SO and MSDN in this matter. Most posts suggest the use of a background worker for the time consuming data retrieval while keeping the GUI in the main thread and therefor suggest solutions using a background worker. That feels awkward in this scenario though, where the main task is data retrieval and not GUI interaction.
I've spend a bunch of hours trying to make sense of different tutorials and forum posts while trying to conform them to my problem, but I have not succeeded and now I'm pretty much back to square one. Basically I'd like to end up with the following two classes outlined below:
ProgressBarWindow
public partial class ProgressBarWindow : Window
{
public ProgressBarWindow()
{
InitializeComponent();
}
public void setValue(int value)
{
// This function should be available from the main thread
}
}
Querier
Public class Querier
{
public List<Item> getItems()
{
// call ProgressBarWindow.setValue(0);
...
// call ProgressBarWindow.setValue(100);
// call ProgressBarWindow.Close();
}
}
It's my understanding that UI must run under single threads and therefor my ProgressBarWindow object could not be instantiated in a new thread while at the same time be available to the main thread (kind of).
Dispatcher.BeginInvoke appears to be my savior here but so far I haven't been able to figure out what should go into the Querier class and what to go in the ProgressBarWindow class. How can I make the two threads interact with the same instance of ProgressBarWindow?
Please ask if you need more details and I will try to clarify.
You can use the Progress class to update the UI with the current progress of a long running operation.
First create an instance of Progress in your UI:
Progress<int> progress = new Progress<int>(currentProgress =>
{
progressBar.Value = currentProgress;
//todo do other stuff
});
Then pass it to the long running process:
public List<Item> getItems(IProgress<int> progress)
{
progress.Report(0);
//todo do something
progress.Report(100);
}
Here is a generic function which i generally use:
public static void Invoke(this UIElement element,Action action)
{
element.Dispatcher.Invoke(action, null);
}
And to use it, simply call:
this.Invoke(() => ProgressBarWindow.SetValue(0));
So, in the getItems() function, you would have something along the lines of:
public List<Item> getItems()
{
ProgressBarWindow wnd;
MainWindow.Invoke(() => wnd = new ProgressBarWindow())
MainWindow.Invoke(() => wnd.SetValue(0))
...
MainWindow.Invoke(() => wnd.SetValue(100))
MainWindow.Invoke(() => wnd.Close())
}
Make sure you always have a way to get to the main window is anything (the one running from either App.xml, or App.Run(...). You can then issue any GUI actions through it (even if you have to create a new Loader window for example, as long as it's done within the main thread)
App.xaml
public partial class App : Application
{
private void Application_Startup_1(object sender, StartupEventArgs e)
{
Task.Factory.StartNew<List<int>>(() => Querier.GetItems());
}
}
ProgressBarWindow.xaml.cs
public partial class ProgressWindow : Window
{
public ProgressWindow()
{
InitializeComponent();
Querier.Start +=()=> Visibility = Visibility.Visible;
Querier.Stop += () => Visibility = Visibility.Collapsed;
Querier.ReportProgress +=OnReportProgress;
}
public void OnReportProgress(int value)
{
txtBox.Text = value.ToString();
}
}
ProgressBarWindow.xaml
<Grid>
<TextBox x:Name="txtBox"></TextBox>
</Grid>
Querier
public class Querier
{
public static event Action Start;
public static event Action Stop;
public static event Action<int> ReportProgress;
public static List<int> GetItems()
{
if (Start != null)
App.Current.Dispatcher.BeginInvoke(Start,null);
for (int index = 0; index <= 10; index++)
{
Thread.Sleep(200);
if (ReportProgress != null)
App.Current.Dispatcher.BeginInvoke(ReportProgress, index*10);
}
if (Stop != null)
App.Current.Dispatcher.BeginInvoke(Stop, null);
return Enumerable.Range(1, 100).ToList();
}
}
I am just trying to give an idea hope this will help.

Thread contained inside class

I'm writing a simple Windows forms application to get me into the swing of things with Threads. So far what I have is working, but what I would like to do is contain it all in a seperate class rather than directly in my forms code.
I have a background thread that starts and retrieves data from a database. I then display that data in to a listbox.
private delegate void UpdateListValues(List<ListBoxItem> itemList);
private void form_main_Shown(object sender, EventArgs e)
{
// Set the loading text.
list_selection.Items.Add(ListHelpers.LoadingItem());
// Start the data access on a seperate thread.
Thread worker = new Thread(GetInvoicingData);
worker.IsBackground = true;
worker.Start();
}
private void GetInvoicingData()
{
// Query database
List<ListBoxItem> values = DAC.GetInvoicingAccounts();
// Display results
BeginInvoke(new UpdateListValues(DisplayList), new object[] { values });
}
private void DisplayList(List<ListBoxItem> itemList)
{
// Display each result
list_selection.Items.Clear();
for (int i = 0; i < itemList.Count; i++)
{
list_selection.Items.Add(itemList[i]);
}
}
The problem is that in the DisplayList method, I won't be able to access the list box (list_selection) because it's part of the form class. Does anyone have any suggestions on how I can do this.
Also, I'm new to threading so feel free to tell me I'm doing it absolutely wrong. I just used the example from http://www.codeproject.com/Articles/23517/How-to-Properly-Handle-Cross-thread-Events-and-Upd to get me to where I am now.
Thanks
How about something like this:
// Added the form's class declaration to highlight separation of thread code into a separate class, but may not be exactly the same as yours depending on naming
public class Form1 : Form
{
private readonly DataRetriever _dataRetriever;
private void form_main_Shown(object sender, EventArgs e)
{
// Set the loading text.
list_selection.Items.Add(ListHelpers.LoadingItem());
// Create the DataRetriever, and provide it with a delegate to DisplayList for returning data
_dataRetriever = new DataRetriever(DisplayList);
// Start retrieving data on a separate thread...
_dataRetriever.GetData();
}
private void DisplayList(List<ListBoxItem> itemList)
{
if (InvokeRequired)
{
// Ensure the update occurs on the UI thread
Invoke((Action)(() => DisplayList(itemList)));
return;
}
// Display each result
list_selection.Items.Clear();
foreach (var item in itemList)
{
list_selection.Items.Add(item);
}
}
}
// Separate class to hold thread code
public class DataRetriever
{
public delegate void UpdateCallbackDelegate(List<ListBoxItem> itemList);
private readonly UpdateCallbackDelegate _updateCallback;
public DataRetriever(UpdateCallbackDelegate updateCallback)
{
_updateCallback = updateCallback;
}
public void GetData()
{
var thread = new Thread(GetInvoicingData)
{
IsBackground = true
};
thread.Start();
}
private void GetInvoicingData()
{
// Not sure whether "DAC" is a static class, if it needs to be constructed
// in the DataRetriever's constructor, or passed to it as a parameter
_updateCallback(DAC.GetInvoicingAccounts());
}
}
As you can see, all the thread code is now in a separate class DataRetriever, and a delegate provided when constructing it to enable the retrieved data to be passed back to the form once the retrieval is complete. The method that handles the callback ensures that the call is marshalled to the UI thread to prevent cross-thread exceptions.
I would like to point out that this is not presented as the "best" way to do this, but merely as an answer to the question (how to separating threading code into a separate class). As others have mentioned, there are already mechanisms in place to do this sort of thing (e.g. BackgroundWorker). Some complexity has been omitted for clarity. For example, in the implementation presented here, if you were to call GetData() multiple times (with each call occurring before the previous ones have returned their data), you would have multiple queries occurring simultaneously, and as they are running asynchronously, may return their data in an arbitrary order. This may or may not be an issue in your case.

Update UI from a different thread in a different class

I have the main form class which contains a list box I want to change. The box is populated with items created in a time-consuming method. Right now it looks like this (inventing an example by hand, might not be valid C#):
List<string> strings = StaticClassHelper.GetStrings(inputString);
foreach(string s in strings)
{
listBox1.Add(s);
}
//meanwhile, in a different class, in a different file...
public static List<string> GetStrings(inputString)
{
List<string> result = new List<string>();
foreach(string s in inputString.Split('c'))
{
result.Add(s.Reverse());
Thread.Sleep(1000);
}
return result;
}
What I would like to do instead is regularly update the list box as new strings are found. The other answers I found work when the thread method is in the same class, so you can set up an event handler. What do I do here?
Here is how I like to do this, I create a method on the form like this:
public void AddItemToList(string Item)
{
if(InvokeRequired)
Invoke(new Action<string>(AddItemToList), Item);
else
listBox1.Add(Item);
}
I prefer invoke in this case to make sure the items are added synchronously, otherwise they can get out of order. If you don't care about the order then you can use BeginInvoke which will be a tad faster. Since this method is public, you can all it from any class in your application as long as you can get a reference to your form.
Another advantage of this is that you can call it from either your UI thread or a non-UI thread and it takes care of deciding whether or not it needs Invokeing. This way your callers don't need to be aware of which thread they are running on.
UPDATE
To address your comment about how to get a reference to the Form, typically in a Windows Forms app your Program.cs file looks something like this:
static class Program
{
static void Main()
{
MyForm form = new MyForm();
Application.Run(form);
}
}
This is typically what I would do, particularly in the case of a "Single Form" application:
static class Program
{
public static MyForm MainWindow;
static void Main()
{
mainWindow = new MyForm();
Application.Run(form);
}
}
And then you can access it pretty much anywhere with:
Program.MainWindow.AddToList(...);
The class containing the ListBox needs to expose a method to add a string - since this method might be called on a different thread, it needs to use
listBox1.Invoke( ...)
to create a thread-safe calling mechanism
Would it be possible for you to rewrite GetStrings as an Iterator? Then in your UI you could start a background thread which iterates over the results of GetStrings, updating the listbox each time. Something like:
public static System.Collections.IEnumerable GetStrings(inputString)
{
foreach(string s in inputString.Split('c'))
{
yield return s.Reverse();
Thread.Sleep(1000);
}
}
And in the UI (Assuming C# 4):
Task.Factory.StartNew(() =>
{
foreach (string s in StaticClassHelper.GetStrings(inputString))
{
string toAdd = s;
listBox1.Invoke(new Action(() => listBox1.Add(toAdd)));
}
}
Probably cleaner ways to go about it, but this should get you what you're looking for.

Categories

Resources