i'm currently working on my personal Wait Dialog implementation, wich supports task progress update and task cancellation. ATM it is something like:
public partial class WaitDialog : Form
{
WaitDialog()
{
InitializeComponent();
}
public static WaitDialog Instance
{
get { return WaitDialogCreator.uniqueInstance; }
}
public DialogResult ShowDialog(Form owner, string message)
{
Instance.lblWaitMessage.Text = message;
return Instance.ShowDialog(owner);
}
public DialogResult ShowDialog(Form owner, BackgroundWorker worker)
{
...
}
public DialogResult ShowDialog(Form owner, string message, BackgroundWorker worker)
{
...
}
private class WaitDialogCreator
{
static WaitDialogCreator() { }
internal static readonly WaitDialog uniqueInstance = new WaitDialog();
}
}
In my ShowDialog() method I can pass a worker object parameter, so that i can set some properties/handlers that depends on its properies, such as the type of progress bar used (marquee if it reports progress changes, continuous otherwise), the possibility to cancel the task (according to WorkerSupportsCancellation prop), etc. The method looks like this:
public DialogResult ShowDialog(Form owner, BackgroundWorker worker)
{
if (worker == null)
{
throw new ArgumentNullException("worker", "A non-null worker must be provided.");
}
else
{
Instance.btnCancel.Enabled = worker.WorkerSupportsCancellation;
//This handler close the dialog
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(onWorkerWorkComplete);
if (worker.WorkerReportsProgress)
{
Instance.pbProgress.Style = ProgressBarStyle.Continuous;
//Update the progress bar
worker.ProgressChanged += new ProgressChangedEventHandler(onWorkerProgressChanged);
}
if (worker.WorkerSupportsCancellation)
{
Instance.btnCancel.Click += (sender, e) => { worker.CancelAsync(); };
}
}
return Instance.ShowDialog(owner);
}
I would access the wait dialog thru a controller on my parent form in this way:
public Controller(Form window)
{
this.window = window;
this.waitDialog = WaitDialog.Instance;
}
...
public void ShowWaitDialog(BackgroundWorker worker)
{
if (worker == null)
{
this.ShowWaitDialog();
}
else
{
window.BeginInvoke((MethodInvoker)delegate() { waitDialog.ShowDialog(window, worker); });
}
}
Maybe that's a very noobish question, but here it is: is it correct to apply (as I do) the Singleton Pattern in this case, or shoud i opt for normal instance creation, given that WaitDialog class ends will normally handle more than a BackGroundWorker in its lifecycle?
The thing that makes me wonder is that I can (and i will) modify WaitDialog's single instance properties each time I pass a new BackGroundWorker in my call to ShowDialog(Form, BackGroundWorker).
Is it a correct behavior, according to the pattern? Are there other path i can take for a better implementation? I am open to any suggestion.
I would creating a new instance every time.
The reason I would not use a singleton is because the form does not have any meaning beyond the use of one specific wait operation. Singleton patterns are used when you want to setup an instance of a class only once and re-use that instance over and over, with its specific settings.
No, it's a bad idea. The Form class was very much designed as a single-use class. Once a form object is disposed it is dead and cannot be revived. You'll get an ObjectDisposedException when you try to display it again. To prevent this, you'll have to intercept the FormClosing event and stop the default processing. You could call Hide() and set e.Cancel = true. But now you've got the hassle of killing it when you really want to get rid of it.
But perhaps more convincingly, you should only ever cache objects that are very expensive to create but don't take a lot of resources. The Form class is the exact opposite. Creating it is cheap but it takes a very large amount of both managed and unmanaged resources. Especially the latter, a window is a very costly OS object. It may look like a Form is expensive to create but what you see is the cycles that are burned on painting the form. You'll burn the exact same number of cycles when you show a hidden form.
Related
I'm coding 'Waiting for login' in the app.
public struct Member
{
public bool IsOpened, IsLogIn;
public string Title, Name;
}
private static void WaitForLogin(ref Member member)
{
while (member.IsOpened)
{
if (IsLoggIn() == true)
{
member.Title = "Welcome to App Centrel";
member.Name = "Omer";
member.IsLogIn = true;
break;
}
System.Threading.Thread.Sleep(1000);
}
}
Using the non-freezing Task, Thread, Async Etc... codes, Time problem occurs due to the ref, out in the parameter.
How does the method not freeze until finished using out, ref parameter?
External app is the not logged in. started freezing in my UI.
I wanted to change the 'mem' variable the time when logged in in the IsLoggIn() method
Once again, why you are using struct? Why not like this?
public class Member
{
public bool IsOpened { get; set; }
public bool IsLogIn{ get; set; }
public string Title{ get; set; }
public string Name{ get; set; }
}
private static async Task WaitForLogin(Member member)
{
while (member.IsOpened)
{
if (IsLoggIn() == true)
{
member.Title = "Welcome to App Centrel";
member.Name = "Omer";
member.IsLogIn = true;
break;
}
await Task.Delay(1000);
}
}
Then you should be able to use it like this
private async void button1_Click(object sender, EventArgs e)
{
Member asD = new Member();
asD.IsOpened = true;
await WaitForLogin(asD);
MessageBox.Show("Logged In. Starting Methods");
}
Thread.Sleep(1000) will suspend the current (UI) thread. This is rarely a good idea, especially on the UI thread. Also note that your example code is probably not safe. I would assume that you are modifying the IsOpened field from another thread, and this is not safe without at least marking the field as volatile. But just use a full lock if you are unsure about the level of synchronization needed.
I do not see that the ref has any real effect on the UI freezing. But I would in general recommend against mutable structs. Just use a class with properties instead.
The simplest possible workaround would be to replace the sleep with a Task.Delay and mark the method as async. This will internally be similar to starting a timer that checks for the login. But be careful with async void functions, since they can lose exception if you are not careful. Prefer to return a task for async functions, unless it has to be void, like an event-handler.
A better solution will be to let whatever component doing the login to send an event. This might be a regular event, or it might be thread-safe event, or a waithandle that may be triggered from a separate process if named. You can also use a task to signal the completion of some login process. But it is difficult to tell exactly how this should be done without more information about the internals of the system.
Regardless of the option you pick, you should probably show a modal UI dialog while waiting for login, to prevent any other interaction with the UI while waiting, while still allowing the UI to be responsive.
For example using a task to signal login and winforms for the UI
public static Task ShowDialogUntillLogin(Task loginTask){
var myForm = new MyForm();
loginTask.ContinueWith(t => myForm.Invoke(() => myForm.DialogResult = DialogResult.OK));
myForm.ShowDialog();
return loginTask;
}
This should show the form until the task is set as complete. When this happens the dialog will be closed and the method returns. Since it blocks inside the ShowDialog method UI messages will still be processed, and the UI remain responsive, but the user can only do stuff in MyForm.
I found a suggested solution to the cross-thread exception issue in another posting and have used it my code below, but I am finding it erratic at best. My tests indicate that it's correct operation is at the mercy of a race condition so I was hoping someone might be able to point out the obvious or provide a more robust solution.
I have a program with two simple forms, where the sole purpose of Form1 is to open Form2. Form2 only contains a RichTextBox and has code that starts a thread that accesses that RichTextBox in an arbitrary way. When execution reaches the InvokeRequired property it is usually (but not always) false, so it goes directly to the RichTextBox access where a cross-thread exception is generated. However, when I call Thread.Sleep(...) just before testing the InvokeRequired property it appears to work properly. However, I hate to use Sleep for this purpose simply because it seems like a kluge that may not always work. Is there a reliable, yet reasonable, way to do cross-thread communication? Thanks.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var form2 = new Form2();
form2.ShowDialog();
}
}
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
Thread myThread = new Thread(new ThreadStart(myStartingMethod));
myThread.Start();
}
void myStartingMethod()
{
Test("Hello world!\n");
}
private delegate void myCallback(string text);
private void Test(string text)
{
// If I put Thread.Sleep(...something...) here it works better.
if (myRichTextBox.InvokeRequired)
{
myCallback d = new myCallback(Test);
Invoke(d, new Object[] { text });
}
else
{
// Cross-thread exception usually occurs, but not always.
int x = myRichTextBox.TextLength;
}
}
}
I want to access a static WebBrowser from different threads.
Here is my sample code:
public partial class MainFrame : Form
{
public static WebBrowser webBrowser = new WebBrowser();
public MainFrame()
{
InitializeComponent();
}
}
class Job
{
public void Process()
{
MainFrame.webBrowser.Navigate("http://www.google.com");
while (MainFrame.webBrowser.ReadyState != WebBrowserReadyState.Complete)
{
Thread.Sleep(1000);
Application.DoEvents();
}
}
}
For simplicity suppose I have 2 threads. Thread 1 calls Process() function and waits for it to finish, so at this stage webBrowser should be in Complete WebBrowserReadyState mode.
10 seconds after thread 1 is finished, Thread 2 calls Process() function. At this moment If i debug my code and put a BreakPoint at the first line of Process() function and watch MainFrame.webBrowser variable I would see this:
In another words it's somehow inaccessible. Anyone knows any solution to this issue?
Additional info: 10 seconds after thread 1 is
finished, If I call Thread 1 again, then everything looks fine.
You cannot directly call WebBrowser control's methods or properties from a thread the control was not created on. You'd need to proxy such calls into the control's parent thread. One way of doing this is with BeginInvoke, but it is asynchronous.
If you really need to do it synchronously, you could use SynchronizationContext.Send, like this:
public partial class MainFrame : Form
{
public static WebBrowser webBrowser = new WebBrowser();
public static System.Threading.SynchronizationContext mainThreadContext = System.Threading.SynchronizationContext.Current;
public MainFrame()
{
InitializeComponent();
}
}
class Job
{
public void Process()
{
mainThreadContext.Send(delegate
{
MainFrame.webBrowser.Navigate("http://www.google.com");
}, null);
bool ready = false;
while (!ready)
{
mainThreadContext.Send(delegate
{
ready = MainFrame.webBrowser.ReadyState != WebBrowserReadyState.Complete;
}, null);
Thread.Sleep(1000);
// if you don't have any UI on this thread, DoEvent is redundant
Application.DoEvents();
}
}
}
Anyway, the above code doesn't looks like a good design to me. What are you trying to achieve? There might be a better way of do it. Perhaps, you can just use WebBrowser.DocumentCompleted event?
I have a C# 2.0 application with a form that uses a class that contains a thread.
In the thread function, rather than call the event handler directly, it is invoked. The effect is that the owning form does not need to call InvokeRequired/BeginInvoke to update its controls.
public class Foo
{
private Control owner_;
Thread thread_;
public event EventHandler<EventArgs> FooEvent;
public Foo(Control owner)
{
owner_ = owner;
thread_ = new Thread(FooThread);
thread_.Start();
}
private void FooThread()
{
Thread.Sleep(1000);
for (;;)
{
// Invoke performed in the thread
owner_.Invoke((EventHandler<EventArgs>)InternalFooEvent,
new object[] { this, new EventArgs() });
Thread.Sleep(10);
}
}
private void InternalFooEvent(object sender, EventArgs e)
{
EventHandler<EventArgs> evt = FooEvent;
if (evt != null)
evt(sender, e);
}
}
public partial class Form1 : Form
{
private Foo foo_;
public Form1()
{
InitializeComponent();
foo_ = new Foo(this);
foo_.FooEvent += OnFooEvent;
}
private void OnFooEvent(object sender, EventArgs e)
{
// does not need to call InvokeRequired/BeginInvoke()
label_.Text = "hello";
}
}
This is obviously contrary to the method used by Microsoft APIs that use background threads like System.Timers.Timer and System.Io.Ports.SerialPort. Is there anything inherently wrong with this method? Is it dangerous in some way?
Thanks,
PaulH
Edit: also, what if the form did not subscribe to the event right away? Would it clog the Form's message queue with events the form wasn't interested in?
This is a threadsafe call, the method will be processed in the thread of the form.
Nothing wrong with it when looking at it from a conceptual perspective.
Timers are more elegant for such tasks, though. However, it could be that a timer with an interval of 10ms slows down the GUI, that's probably why Invoke was used.
You do not need a call to InvokeRequired, since it is clear that the Control is in an other thread. Also, BeginInvoke only needs to be called when you want to call a method asynchronously, which obviously isn't the case here.
Regarding your edit:
No, the message queue will not be clogged. No event will be fired if no handler has been registered. Take another look at your code ;)
I have a class that basically stores files in amazon s3.
Here is what it looks like (simplified)
public class S3FileStore
{
public void PutFile(string ID, Stream content)
{
//do stuff
}
}
In my client app, I want to be able to call:
var s3 = new() S3FileStore();
s3.PutFile ("myId", File.OpenRead(#"C:\myFile1"));
s3.PutFile ("myId", File.OpenRead(#"C:\myFile2"));
s3.PutFile ("myId", File.OpenRead(#"C:\myFile3"));
I want this to be an asynchronous operation - I want the S3FileStore to handle this (i don't want my caller to have to execute PutFile asynchronously so to speak) but, i want to be able to trap exceptions / tell if the operation completed for each file.
I've looked at event based async calls, especially this:
http://blogs.windowsclient.net/rendle/archive/2008/11/04/functional-shortcuts-2-event-based-asynchronous-pattern.aspx
However, I can't see how to call my PutFile (void) method?
Are there any better examples?
Look at the solution for this question: Adding cancel ability and exception handling to async code . Hope it helps.
The BackgroundWorker base class might be worth a look, and also the Thread Pool:
ThreadPool.QueueUserWorkItem(delegate
{
s3.PutFile ("myId", File.OpenRead(#"C:\myFile1"));
});
This is basically what you would do with the Action/BeginInvoke pattern. With BeginInvoke, you additionally receive an IAsyncResult on which you can call .WaitOne() to block the current thread until the operation finished, in case you need that. You would trigger a new BeginInvoke for every file you'd like to save.
If you need to do this frequently, a more sophisticated version could be to use a Queue in combination with the BackgroundWorker, e.g.:
public sealed class S3StoreLikePutFileWorker<TYourData> : BackgroundWorker
{
private AutoResetEvent WakeUpEvent = new AutoResetEvent(false);
private Queue<TYourData> DataQueue = new Queue<TYourData>();
private volatile bool StopWork = false;
public void PutFile(TYourData dataToWrite)
{
DataQueue.Enqueue(dataToWrite);
WakeUpEvent.Set();
}
public void Close()
{
StopWork = true;
WakeUpEvent.Set();
}
private override void OnDoWork(DoWorkEventArgs e)
{
do
{
// sleep until there is something to do
WakeUpEvent.WaitOne();
if(StopWork) break;
// Write data, if available
while(DataQueue.Count > 0)
{
TYourData yourDataToWrite = DataQueue.Dequeue();
// write data to file
}
}
while(!StopWork);
}
}
Depending on how much complexity you need.
The BackgroundWorker supports progress feedback (set WorkerReportsProgress = true; in the constructor), and you can also add a custom event to report errors, if that is necessary:
// create a custom EventArgs class that provides the information you need
public sealed class MyEventArgs : EventArgs {
// Add information about the file
}
// ... define the event in the worker class ...
public event EventHandler<MyEventArgs> ErrorOccured;
// ... call it in the worker class (if needed) ...
if(ErrorOccured != null) ErrorOccured(this, new MyEventArgs(/*...*/));