I have three class, tow of them was UI class, in the mainForm class, I start a new form by execute
new LoginForm.ShowDialog();
in the LoginForm class, I write code about log in and log out, when the use loged in, I start a new thread to check if something need to be done,and update the databases; and here is the question, I don't know how to update a label that in the MainForm
I search this question and they told me I should to use Delegate.but it really puzzled me a lot cause they don't in a same class so I don't know how to use Delegate cross thread and cross different
Until now, my code is like this
MainForm.cs:
public partial class MainForm : Form
public delegate void testDelegate();
public MainForm()
{
InitializeComponent();
}
public void msg(string s)
{
label.Test = s;
}
}
LoginForm.cs:
JobDoer jD = new JobDoer();
Thread t2 = new Thread(new ThreadStart(jD.run));
t2.Start();
JobDoer:
public void run()
{
//tD();
tD = new MainForm.testDelegate(MainForm.msg);
//this.
Thread.Sleep(1000);
return;
}
what should I do next?
Taken from: Best Way to Invoke Any Cross-Threaded Code?
You also could use an extension method and lambdas to make your code much cleaner.
using System.ComponentModel;
public static class ISynchronizeInvokeExtensions
{
public static void InvokeEx<T>(this T #this, Action<T> action) where T : ISynchronizeInvoke
{
if (#this.InvokeRequired)
{
#this.Invoke(action, new object[] { #this });
}
else
{
action(#this);
}
}
}
So now you can use InvokeEx on any ISynchronizeInvoke and be able to access the properties and fields of implementing class.
this.InvokeEx(f => f.listView1.Items.Clear());
For this kind of thing theres the
System.ComponentModel.BackgroundWorker class.
https://learn.microsoft.com/en-us/dotnet/framework/winforms/controls/how-to-implement-a-form-that-uses-a-background-operation.
It handles the thread sync for you, plus it has a usefull prgress update event
Related
I am accesing another file by doing this:
public void startUpdateChecking()
{
UpdateHandler process = new UpdateHandler();
process.initialize(this);
}
The same class that 'startUpdateChecking' function is in I have this example functon aswell:
public void changeText(string Text)
{
label2.Text = Text;
}
Now in the UpdateHandler class I am doing this:
public Form form;
public void initialize(Form test)
{
Thread update = new Thread(checkForUpdates);
update.Start();
form = test;
edit();
}
public void edit() {
form.changeText("test");
}
But 'form' has no clue what changeText is for some reason, how would I make it so I can use functions from another class (Form2 class) WITHOUT the need for static function?
I tried doing:
Form2 form = new Form2();
And I could control and acces things from Form2, but this makes a new form instead of controlling the current one that is active (aka nothing visible happends using this).
Thanks in advance.
One way would be to use a delegate to pass the changeText method instead of passing the whole form. This should help separate the classes and I think would improve the design.
A quick way of doing this would use an action. Instead of passing in Form to initialize pass Action<Text>
The form side code would change to
public void startUpdateChecking()
{
UpdateHandler process = new UpdateHandler();
process.initialize((s) => {changeText(s);});
}
and the UpdateHandler side code would change to
public void initialize(Action<string> outputMethod)
{
Thread update = new Thread(checkForUpdates);
update.Start();
output= outputMethod;
edit();
}
public void edit() {
output("test");
}
Try to return a value from initialize and then pass that on to ChangeText.
like this:
public void startUpdateChecking()
{
UpdateHandler process = new UpdateHandler();
string Value1 = process.initialize(this);
ChangeText(Value1);
}
Initialize should then be a string, im not sure what the form is doing there, and what it has to do with the Thread update, thats something you probably know more about
public string initialize(string test)
{
Thread update = new Thread(checkForUpdates);
update.Start();
form = test;
return test;
}
Just don't try to call a function from out a class, best way is to return from a class and then call a function
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.
I've written a WPF WizardFramework which performs some actions in the background using some BackgroundWorker. While processing it can happen that I have to update an ObservableCollection which is bound to my UI.
For this case I've written a ThreadableObservableCollection, which provides threadsafe methods for Insert, Remove and RemoveAt. Though I'm using .NET 4.5 I was not able to get BindingOperations.EnableCollectionSynchronization working without many other invalid access exceptions. My Collection looks like:
public class ThreadableObservableCollection<T> : ObservableCollection<T>
{
private readonly Dispatcher _dispatcher;
public ThreadableObservableCollection()
{
_dispatcher = Dispatcher.CurrentDispatcher;
}
public void ThreadsafeInsert(int pos, T item, Action callback)
{
if (_dispatcher.CheckAccess())
{
Insert(pos, item);
callback();
}
else
{
_dispatcher.Invoke(() =>
{
Insert(pos, item);
callback();
});
}
}
[..]
}
This is working as expected, while I am using the wizard in my application. Now I'm using NUnit to write some integrationtests for the application.
There's a listener which waits for the WizardViewModel to finish it's work and looking for some pages which are injected in the Steps-Collection. After the asyncrone work is done I can use Validate to check the viewmodel state.
Unfortunately I'm using a ManualResetEvent to wait for the wizard to close. This looks like following:
public class WizardValidator : IValidator, IDisposable
{
private WizardViewModel _dialog;
private readonly ManualResetEvent _dialogClosed = new ManualResetEvent(false);
[..]
public void ListenTo(WizardViewModel dialog)
{
_dialog = dialog;
dialog.RequestClose += (sender, args) => _dialogClosed.Set();
dialog.StepsDefaultView.CurrentChanged += StepsDefaultViewOnCurrentChanged;
_dialogClosed.WaitOne();
}
[..]
}
Now there's a problem:
While the Application is running the UI Thread is not blocked, the Collection can be updated without any problems. But in my testcases the "main" Thread where I initialize the ViewModel (and because of that the Collections) is an AppDomainThread which is blocked by the testcode. Now my ThreadsafeInsert wants to update the collection but cannot use the AppDomain Thread.
But I have to wait for the wizard to finish, how can I solve this kind of deadlock? Or is there a more elegant solution for this one?
edit:
I worked around this problem with a check if there's a user interface, and only then I invoke on the Application-Thread, otherwise I change the collection intentionally on another thread. This does not prevent the exception, but it is not recognized from the test... the items are inserted nevertheless, only the NotifyCollectionChanged-Handler is not called (which is only used in the UI anyway).
if (Application.Current != null)
{
Application.Current.Dispatcher.Invoke(() =>
{
Steps.Insert(pos, step);
stepsView.MoveCurrentTo(step);
});
}
else
{
new Action(() => Steps.Insert(pos, step)).BeginInvoke(ar => stepsView.MoveCurrentToPosition(pos), null);
}
This is an ugly workaround and I am still interested in a clean solution.
Is there a way to use an alternate Dispatcher to create (e.g.) the whole ViewModel and use this to change my collection?
As I see the main problem that main thread is blocked and other operations are trying to be executed in main thread too? What about not to block main thread, like this:
// helper functions
public void DoEvents()
{
DispatcherFrame frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
new DispatcherOperationCallback(ExitFrame), frame);
Dispatcher.PushFrame(frame);
}
public object ExitFrame(object f)
{
((DispatcherFrame)f).Continue = false;
return null;
}
// in your code:
while(!_dialogClosed.WaitOne(200))
DoEvents();
If it will not help then I guess need to try some SynchronisationContext workarounds.
I think the problems boil down to the fact that you create ObservableCollection that is tied to Dispatcher object.
Involving Dispatcher object directly is almost never good idea(as you just witnessed). Instead I would suggest you to see how others have implemented ThreadSafeObservableCollection. This is a little example I put together, it should illustrate the point:
public class ThreadSafeObservableCollection<T> : ObservableCollection<T>
{
private readonly object _lock = new object();
public ThreadSafeObservableCollection()
{
BindingOperations.CollectionRegistering += CollectionRegistering;
}
protected override void InsertItem(int index, T item)
{
lock (_lock)
{
base.InsertItem(index, item);
}
}
private void CollectionRegistering(object sender, CollectionRegisteringEventArgs e)
{
if (e.Collection == this)
BindingOperations.EnableCollectionSynchronization(this, _lock);
}
}
I have the class called mainForm that it is main window of my program. I create a TextBox(this TextBox Logs program) object in this class and i want to write program status to it. I do this from mainForm and other object(by passing TextBox object to it) easily, But when i want to do that from another thread, it's complicated.
However, i am writing to TextBox by the thread that it runs the defined code in mainForm(using delegate).
My question is, How to write in the TextBox from thread that runs in another class?
public partial class mainForm : Form
{
TextBox log = new TextBox();
.
.
.
OtherClass o = new OtherClass(log);
}
class OtherClass
{
private TextBox log;
public otherClass(TextBox aLog)
{
log = aLog;
Thread thread = new Thrad(new ThreadStart(this.run));
thread.Start();
}
private void run()
{
log.Text = "Message";// I Can't Do This. Can I Use Delegate Here? How?
}
}
You can use Invoke/BeginInvoke:
log.BeginInvoke(
(Action)(() =>
{
log.Text = "Message";
}));
This allows the secondary thread to safely forward GUI changes to the GUI thread which is the only one that should apply them.
Another way using defined delegate - incidently Xt here can be reused for other methods as long as the signature is the same. Parameters can also be passed - (would then have parameters in the Xt delegate and Invoke of it would pass a coma separated list for each.
private void run()
{
XteChangeText();
}
private delegate void Xt();
private void XteChangeText()
{
if (log.InvokeRequired)
{
Invoke(new Xt(XteChangeText));
}
else
{
log.Text="Message";
}
}
I have a layered worker class that I'm trying to get progress reports from. What I have looks something like this:
public class Form1
{
private void Start_Click()
{
Controller controller = new Controller();
controller.RunProcess();
}
}
public class Controller
{
public void RunProcess()
{
Thread newThread = new Thread(new ThreadStart(DoEverything));
newThread.Start();
}
private void DoEverything()
{
// Commencing operation...
Class1 class1 = new Class1();
class1.DoStuff();
Class2 class2 = new Class2();
class2.DoMoreStuff();
}
}
public class Class1
{
public void DoStuff()
{
// Doing stuff
Thread.Sleep(1000);
// Want to report progress here
}
}
public class Class2
{
public void DoMoreStuff()
{
// Doing more stuff
Thread.Sleep(2000);
// Want to report progress here as well
}
}
I've used the BackgroundWorker class before, but I think I need something a bit more free form for something like this. I think I could use a delegate/event solution, but I'm not sure how to apply it here. Let's say I've got a few labels or something on Form1 that I want to be able to update with class1 and class2's progress, what's the best way to do that?
Using events is the most straightforward solution. When you subscribe to the event from the main thread, the handler should check the Control.IsInvokeRequired to know whether it must call itself again through Invoke(...) to get the message passed to the right thread.
John is correct. You want to utilize events and for that you'll need to use a delegate or delegates. This might give you some ideas.
http://www.yoda.arachsys.com/csharp/threads/winforms.shtml
If you do not want to block the processing threads during notification, you can use Control.BeginInvoke() for fire & forget behavior.
To decrease the number of calls and update progress on a regular interval, you may want to encapsulate the states of different operations in classes.
This way you can just write states to e.g. volatile fields - of presumably another, aggregate-state class - and use a timer on the GUI thread to re-read state and refresh labels accordingly.