Simply put, what is a good way to send the progress of some process back to a form from within a class outside the scope of the form?
EG: I have a Input object, a filepath is sent into the constructor of this object and it parses the file. I want to show the progress of reading in the lines of this file back to the user who is in a form outside the scope of the currently running method. Ultimately I will be displaying overall progress and per file progress.
Add an event to your class that the form can subscribe to. UpdateProgress or something like that. Your class would then raise the event every so often to let the form display the progress.
The best approach here would be a BackgroundWorker. It has a specal event ReportProgress (that handles the Invoke logic for you).
It is an easy way to do multi-threading. Follow a good example when implementing the RunWorkerCompleted event.
Related
I'm having some problems with my form and the business class:
The form does not open until the business class has finished it's work. Every time I call Form1 in Main(), Form1 calls two methods:
InitializeComponents();
testConnection();
testConnection calls the business class and sets the properties of the Form according to the properties of the business class. Like that:
Pingger pingger = new Pingger();
ipLabel.Text = pingger.getLocalIP();
I do not do just these operations, I also do others like the result of a ping. However, Form1 takes a long time to open and when it's open Form1 shows the results. What do I have to do, create Form1, open it, and then after 3 seconds start to process the business class?
What you want is an event raised by your form control. To determine if there is such an event you simply Google the class (ie Form). On the msdn page every property, method and event is listed accompanied with what they do.
What you want is the Shown event. You can add an event to this and then handle whatever it is you want to do. See link below.
http://msdn.microsoft.com/en-us/library/system.windows.forms.form(v=vs.110).aspx
You can subscribe to the Shown event for the form and call your testConnection() method from there.
InitializeComponents();
Shown += (s,e) => testConnection();
You could also use multi-threading to "launch" the testConnection() method. (but then you have the added complexity of making sure the results are in before you rely on them, thread safety, etc...)
I wonder you want to show the form just before the connection is tested?
The idea could be using multi-threading like the Wonderbird suggestion, using some like this:
InitializeComponent();
new Thread(testConnection).Start();
Hi I have a problem when I am working on my Windows Phone silverlight C# application.
So I want to do something like this
Press a button on page one. The button click handler calls a async method MakeRequest in other class to retrieve data. The async method will fire a event DataReadyEvent and has the result wrapped as DataEventArgs.The handler of this event will be in Page2. So after add a handler to this event, I navigate to Page2 from current page.
I want to retrieve data by a event handler in Page 2 code behind and update that on UI. But the event handler is static (so that I can add it by using Page2.handler_method_name in page1 code without creating a new instance of the page.). Since the handler method is static, I cannot use Dispatcher.Invoke and get back to the UI thread and update UI.
So in this case, anyone has any idea to it? I just want to call a async method in page1, and update result to UI in page2. Thank you
Here is an idea: don't make it static. Don't try to create problems for yourself by breaking simple OOP rules like encapsulation, etc and by finding some crazy workarounds around the framework you work with.
When you are in such a situation you should stop, look back and think because it is an indication that you do something completely wrong. Don't try to push it even further by finding hacks and workarounds. Rather you should refactor and reuse the correct paradigm.
For example, if you want to display the result on Page2, then there IS a Page2 ALREADY. So there IS an instance of it. Why do you want to use static handler then?
Probably because you don't have a reference to this page. That's fine, normally you shouldn't.
But when you finish your computation you can publish an event saying "hey, here is the task done". At that point you shouldn't care who is interested in this result, that's not the worker's concern.
Which means that the logic of the computation itself should probably be moved out from Page1. Really, pages concern is dome presentation logic, nothing more.
Page1 should make a request that some computation needs to be done. And here will be an external component (perhaps something in your ViewModel) to actually make it happen.
So when the result is ready to be consumed, you can simply push it into a ViewModel (update some observable properties or collections, etc), so if there is any UI (or many of them, or other components) interested in this data it will be automatically notified and the data will be displayed.
But please don't try to hack around, it will lead you to bigger pain in the future.
My form creates a backgroundworker that every 6 secs checks something. Result is 1-100 and I want to display this.
But if i do it straight forward i get som cross-thread errors so I after some research delegates is the way to do it.
ive created inside the Form1 class:
public delegate void SetProgressbarValueDelegate(int val);
but how do i "connect it" to actually update the progressbar?
Thanks
Assuming your are using WinForms, The BackgroundWorker class raises a ProgressChanged event which will automatically be marshalled back onto your UI thread. You should make updates to your User Interface within your handler for this event.
There are some simple examples of how to use BackgroundWorker within the MSDN documentation.
I have a WinForm set up and a process that loops until a button is pressed on the form.
When I try to run my code, the form does not even display. I suspect this is because the code gets stuck in the loop and doesn't get far enough to display the WinForm. How can I get the form to display and the loop to run after that point?
If you're looping because you need to do something with the GUI periodically while waiting for input, I suggest using a Timer control and its Tick event.
If you want to do non-GUI things while waiting, a more traditional timer is better suited to the task,
You should probably run the loop in a background thread. The BackgroundWorker class makes this pretty easy to do.
Don't do that.
Windows Forms (like most modern user interface development toolkits) is an event-driven framework. You should never use a loop that "waits" for something to happen; instead you want to use an event that triggers something to happen.
Essentially what's happening is this: WinForms has a loop running a message pump that listens for events from Windows and triggers C# events in response to them. Your code is executing on the same thread as that message pump (it has to, since in WinForms only one thread is allowed to touch any given control). So if you put that thread into a loop, the WinForms code that should be pumping messages isn't, and your user interface appears to hang, since it isn't responding to any messages from Windows. (If you keep clicking it, you will fill up the message queue and get a dialog box that says "This application has stopped responding, do you want to terminate?" or something like that.)
The correct solution is to do one of the following:
Use a Timer
Use a BackgroundWorker
Use a ThreadPool
Another solution that would work, but is not a good idea is:
Use Application.DoEvents() -- but please don't actually do this
Your form loading is freezing because the UI of a windows form runs in a single thread. And the logic that you put on the Load event of this form is running on that thread.
You can run your loop on a separate thread easily by using a BackgroundWorker component on your windows form. On the event DoWork of your background worker, you place the code that has the loop that should run without block your UI. On the Form.Load event, you can start the background worker component by calling the method RunWorkerAsync. On the event handler of your button, you place a code to stop the background worker by calling CancelAsync method.
The article How to: Implement a Form That Uses a Background Operation shows exactly how to accomplish it.
About your comment on not being able to update the Text of a TextBox from a your background worker component. It happens because it is not allowed to modify the state of a windows forms control from a different thread (your background worker code is running on a separated thread) MSDN documentation says:
Access to Windows Forms controls is not inherently thread safe. If you have two or more threads manipulating the state of a control, it is possible to force the control into an inconsistent state. Other thread-related bugs are possible, such as race conditions and deadlocks. It is important to make sure that access to your controls is performed in a thread-safe way.
A sample of how you can update the state of your windows forms controls from your background thread will be similar to the one below (assuming that the new value is already stored on a String variable named text):
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
I borrowed this code snipped from How to: Make Thread-Safe Calls to Windows Forms Controls article. It can provide you more information about how to deal with multi-threaded windows forms.
You can use the form load event to trigger the start of the loop.
So it would handle the event Me.Load
However is it necessary for your loop to be happening inside of the UI?
This happens because your loop is keeping the window function from processing messages, such as those that tell it to repaint itself. Place a call to Application.DoEvents() inside of your loop to allow the UI to continue to function.
However, you need to ask yourself why you're looping like this in the first place. If you're, say, copying a bunch of files, this might make sense. For most tasks, though, responding to a timer tick should do the trick and won't block the UI.
You should run your loop in a background thread using the BackgroundWorker component.
Remember that the background thread cannot directly interact with the UI controls.
To report the progress on the UI, you should call the BackgroundWorker's ReportProgress method in the background thread, and handle the ProgressChanged event to update the UI.
You can call the CancelAsync method when the Button is clicked, and loop until the CancellationPending property is true.
I need to validate a control input on losing focus. Normally I'd use the Validating event. However this process involves checking the entered data against a local database of over 280,000 postal codes. I'd like for this validation to occur asynchronously since there is no requirement for the user to wait for it before they can enter the remaining form data.
My first thought was to encapsulate the validation logic in its own method, bind a delegate to it and use BeginInvoke() and EndInvoke inside the control validation event since no possible result of the validation will require Cancel=True (they will simply change the control forecolor).
Is there any better method?
You might want to look at a BackgroundWorker
You might find a BackgroundWorker a good method of doing this.
One thing to consider is the user trying to submit the form before your validation has finished running.