WPF + PRISM - Cannot 'LoadModule' in separate thread - wpf

I have a WPF application in PRISM architecture.
I have a 'Login View' that is shown in the 'Main Region' when the app loads.
When the user presses 'Login' - I connect to a WCF service, authenticate the user, and get a list of roles for that user from the service.
Then - according to the user's roles - I load different modules, using the 'Module Manager'.
Problem is - I want all the work after the 'Login' button is pressed to be done in a separate thread, because it might take time to connect to the service etc, and I don't want the UI to be frozen.
But - if I put the code to 'connect, authenticate, get roles, load modules' in a separate thread - I get an exception when I call '_moduleManager.LoadModule' that says:
The calling thread must be STA, because many UI components require this.
How can I solve this ?
I have tried different solutions.
I have tried to set the new thread's 'Apartment State = STA' and it didn't help.
I thought about saving the 'Dispatcher' object in the constructor of the View-Model, and then do 'dispatcher.Invoke' when I call 'LoadModule', but that is bad design (View-Model should not use Dispatcher, and also it is bad for testing).
Any ideas how I can solve this ??
Only the 'LoadModule' gives me grief, all the other stuff works fine.
.
[Update] - Added Code Sample :
[Export]
public class LoginViewModel : NotificationObject
{
[ImportingConstructor]
public LoginViewModel(IRegionManager regionManager, IModuleManager moduleManager)
{
this.LoginCommand = new DelegateCommand(LoginExecute, LoginCanExecute);
this._regionManager = regionManager;
this._moduleManager = moduleManager;
}
private void LoginExecute()
{
IsBusy = true; // Set this to 'true' so controls go disabled
LoginStatus = ""; // Clear the 'login status' string
Thread loginThread = new Thread(new ThreadStart(LoginWork));
loginThread.SetApartmentState(ApartmentState.STA);
loginThread.Start();
}
private void LoginWork()
{
ParamsToGetRoles param = new ParamsToGetRoles
{
Username = Username,
InputtedPassword = Password
};
try
{
// Connect to the secure service, and request the user's roles
_clientSecure = new AuthenticationServiceClient("WSHttpBinding_MyService");
_clientSecure.ClientCredentials.UserName.UserName = param.Username;
_clientSecure.ClientCredentials.UserName.Password = param.InputtedPassword;
_clientSecure.ChannelFactory.Faulted += new EventHandler(ChannelFactory_Faulted);
var local = _clientSecure.ChannelFactory.CreateChannel();
_clientSecure.GetRolesCompleted += new EventHandler<GetRolesCompletedEventArgs>(clientSecure_GetRolesCompleted);
_clientSecure.GetRolesAsync(param);
}
catch (Exception ex)
{
Console.WriteLine("Exception : " + ex.Message.ToString());
}
}
void clientSecure_GetRolesCompleted(object sender, GetRolesCompletedEventArgs e)
{
if (e.Error == null)
{
_clientSecure.Close();
LoginSuccess(e.Result.UserRoles);
}
else
{
LoginFailure("Unable to authenticate");
}
_clientSecure = null;
}
private void LoginSuccess(List<UserTypeEnum> rolesOfAuthenticatedUser)
{
LoginStatus = "Success";
if (rolesOfAuthenticatedUser.Contains(UserTypeEnum.Administrator))
{
// This is what throws the exception !
// This is called by the 'EndInvoke' of the 'GetRoles' operation,
// Which was called in the 'LoginWork' function which was run on a separate thread !
_moduleManager.LoadModule(WellKnownModuleNames.ModuleAdmin);
}
NavigateToMainMenu();
this.IsBusy = false;
}
}

You should attach the debugger and inspect the threads window with a breakpoint set at clientSecure_GetRolesCompleted. I'm pretty sure it is not being called from the loginThread: while LoginWork does run in the loginThread, it then adds an eventhandler to the completion event of an async operation. Async = runs in yet another thread.
So what probably happens:
LoginExecute executes in the UI thread
starts a seperate thread B to run LoginWork
calls GetRolesAsync so start a thread C (which is not STA) to get the roles
thread C eventually calls 'clientSecure_GetRolesCompleted', not thread B
So, you do not need a seperate thread for LoginWork since the actual work is already done as an async operation. To get around the loading issue, either try to make the 'get roles' thread STA, or better, use a dispatcher so LoginSuccess gets invoked on the UI thread.

Related

WPF Command execution sequencing

I have a WPF application that uses MVVM pattern. I have controls in the window that are bound to properties in the ViewModel. I have a Play button that is bound to the Play() method via an ICommand interface implementation. As the Play() method steps through, I first change some properties to alter the UI to show the user that the app is working:
IsPlaying = true;
IsNotPlaying = false;
DurationTimer.Start();
Status = $"Playing: {_playingUrl}";
FilePreview?.FileNameSet(_playingUrl, "");
FilePreview?.FilePlayStart();
When the Play button is pressed it should disable the Play button via the IsPlaying property and enable the Stop button via the IsNotPlaying property. Also, the DurationTimer should start (which displays a timer) and the Status property. These are intended, as said, to show the user that things are happening since FilePreview?.FilePlayStart(); is a blocking method and the UI locks up while processing.
However, when the Play button is pressed the UI immediately locks and, then, once the FilePlayStart() method finishes its processing, it releases and the other items become effective.
Am I missing something?
WPF, like most of UI frameworks, updates the UI only from one single Thread (any attempt to update the UI from another Thread will raise a System.InvalidOperationException).
Now, since the UI Thread is busy executing your method, it cannot update the UI at the same time.
WPF works with "bulks" of code. Once a bulk is executed, the UI takes care of all the updates. AT THE END of the execution, not in the middle.
So, if in the Execute method (or any other method executed on the UI Thread) you set 40 times "can execute = true", "can execute = false", "can execute = true", "can execute = false", actually you won't see the Button being unabled and disabled 40 times. Instead, when the method exits, THEN the UI is updated with the last value.
So, how to solve this? The Execute method should be asynchronous.
Something like:
public class Command : ICommand
{
//ICommad implementation and other stuffs
//...
public async void Execute(object parameter)
{
await DoExecute(parameter);
}
private async Task DoExecute(object parameter)
{
//do something asynchronously...
}
}
In your specific case, FilePreview?.FilePlayStart(); should be asynchronous, and you should pass this method to the Command.
You can write a general Command:
public class Command : ICommand
{
//ICommad implementation and other stuffs
//...
//pass the execution in the constructor
public Command(Func<object, Task> execution)
{
_execution = execution;
}
private Func<object, Task> _execution;
public async void Execute(object parameter)
{
await _execution(parameter);
}
private async Task DoExecute(object parameter)
{
//do something asynchronously... like await Task.Delay(2000);
}
}
You can then use it this way in the owner of the Command:
MyCommand = new Command(async parameter =>
{
IsPlaying = true;
IsNotPlaying = false;
await FilePreview?.FilePlayStartAsync();
});
As soon as the await part is entered, the execution pass to another Thread, and the current Thread (that is the UI Thread) is free to update the UI, and you will see that the Buttons are enabled/disabled as you want.
If an async version of the method is not available, you can write:
MyCommand = new Command(async parameter =>
{
IsPlaying = true;
IsNotPlaying = false;
await Task.Run(() => FilePreview?.FilePlayStart());
});
You can't perform long running operations on the UI thread, as it will block the dispatcher until it is done processing.
In cases like this, just use async/await to free the dispatcher and allow message pumping to continue.
private async void PlayCommand()
{
IsPlaying = true;
IsNotPlaying = false;
DurationTimer.Start();
Status = $"Playing: {_playingUrl}";
await Task.Run(()=>
{
FilePreview?.FileNameSet(_playingUrl, "");
FilePreview?.FilePlayStart();
});
}

How to ensure wcf service client finishs his works in silverlight?

I use wcf service client to submit changes of data for a silverlight project. The correlative codes like this:
public class DispatcherCollection : UpdatableCollection<DocumentDispatcher>
{
public override void SubmitChanges()
{
DocumentServiceClient client = new DocumentServiceClient();
client.NewDocumentCompleted += (s, e) =>
{
// (s as DocumentServiceClient).CloseAsync();
// do something
};
client.UpdateColumnCompleted += (s, e) =>
{
// (s as DocumentServiceClient).CloseAsync();
// do something
};
client.RemoveDocumentCompleted += (s, e) =>
{
// (s as DocumentServiceClient).CloseAsync();
// do something
};
foreach (DocumentDispatcher d in this)
{
if (d.IsNew)
{
// d=>object[] data
client.NewDocumentAsync(data);
d.IsNew=false;
}
else
{
foreach (string propertyName in d.modifiedProperties)
{
client.UpdateColumnAsync(d.ID, GetPropertyValue(propertyName));
}
dd.ClearModifications();
}
}
foreach (DocumentDispatcher dd in removedItems)
{
client.RemoveDocumentAsync(dd.ID);
}
removedItems.Clear();
}
}
Class UpdatableCollection derives from ObserableCollection, and I implemtent logics in class DocumentDispatcher and UpdatableCollection to buffer the changes of data such as new created, property modified and removed. I use SubmitChanges method to submit all changes to server.
Now I am stuck:
1. I am at a loss when to close the client after a bunlde fo async calls. I don't know which callback is the last one.
2. What will happen when a user closes the IE immediately right after clicking the save button (it seems to be done because it runs async but in fact the updating threads are industriously running.)?
You can keep a counter or use an isbusy function to monitor the callbacks from your Async calls - to make sure they all finished.
If the user fires off a request to the WCF service, the WCF service will complete but there will be no call back - as the application will be closed.
I think that there is no wait handle for silverlight asynchornized call brings inconvenience. Here is my experence. I want to check and submit modifications of data which are not expicitly submitted when browser is closing. I have implemented codes in App_Exit like this:
private void Application_Exit(object sender, EventArgs e)
{
Document doc = EDPViewModel.CurrentViewModel.Document;
if (doc != null) new ServiceClient().SubmitChangesAsync(doc);
}
provided that in the SubmitChangesAsync method, not submitted modifications of doc are found out and submitted. Therefore, because of the asynchronized running features, while the service invoking is being sent, the application is yet immediately closed. And that will dispose related resouces of the application, including Service Invoking Tasks. So the codes above work not. I hope so eagerly that somewhere exists a mechanism, which can export a wait handle from silverlight asynchronized call, so that I can update the above codes whith this:
private void Application_Exit(object sender, EventArgs e)
{
Document doc = EDPViewModel.CurrentViewModel.Document;
if (doc != null)
{
Task t = new TaskFactory().StartNew(() => new ServiceClient().SubmitChangesAsync(doc));
t.Wait();
}
}
With wait operation I can really be sure that all modifications are really definitely submitted. So is there any similar pattern that can be used in silverlight?
It's for me a good news, as you put it, that calls could work like the mode "requesting and forgetting". So I needn' to worry too much about data losing during submitting.
To ensure all service calls are sent out before application is closed, I think, counter is a simple and effient idea. I will try to implement it in my project.
Thank you for your help!

MessageBox.Show early in App startup causes app to terminate

As part of my App's startup procedure, it checks data integrity, and if it finds a problem it pops up a message to the user telling them that it might take a while to repair things.
I'm showing the message using MessageBox.Show. Because the data check is done from a worker thread, I'm switching over to the UI thread to make that call, and then setting a ManualResetEvent to tell the worker thread when the user has acknowledged the message.
I kick off the data check/load very early in the app's lifecycle from the constructor in the main Application class, by spinning off a worker thread (using the ThreadPool).
When I run with the debugger, and the message is displayed, the app just waits for input. When I run without the debugger, the app terminates after displaying the dialog for 10 seconds.
That 10 seconds is a big clue - it tells me that the OS thinks the app took too long to initialize (the OS kills apps that take too long to start up).
I think that my MessageBox.Show is blocking the UI thread before the App.RootFrameNavigating has a chance to be invoked.
My questions:
Does my diagnosis sound right?
I'd prefer to kick off my data load early, because it is almost entirely IO, except for this Message Box, and the sooner I can get my Model loaded, the better, but do you normally delay your data load until later in the app lifecycle?
Any other ideas/suggestions? I can't guarantee which page will be the start page, because the app could be resuming to any page. I'm also thinking of having the MessageBox.Show delay itself until the app has initialized, perhaps polling away for a flag set by App.RootFrameNavigating - does that make sense?
I think your problem is a result of kicking off the worker thread in the Application constructor. You should use the appropriate life-cycle event, in this case: PhoneApplicationService.Activated Event
So, the solution I've come up with is to still kick off the data load in a worker-thread from the Application's constructor, but in my PhoneService's class ShowDialog method that I invoke to invoke MessageBox.Show, I check to see if the initial navigation has occurred:
private readonly ManualResetEvent _appInitialized = new ManualResetEvent(false);
public void AppInitialized()
{
_appInitialized.Set();
}
public void ShowDialog(string caption, string text, Action<MessageBoxResult> callback, MessageBoxButton button = MessageBoxButton.OKCancel)
{
_appInitialized.WaitOne();
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
var result = MessageBox.Show(text, caption, button);
if (callback != null)
{
callback(result);
}
});
}
Then in my Application class:
private bool _firstNavigate = true;
private void RootFrameNavigating(object sender, NavigatingCancelEventArgs e)
{
if (_firstNavigate)
{
_firstNavigate = false;
var navigationService = (NavigationService) sender;
navigationService.Navigated += NavigationServiceNavigated;
}
....
private void NavigationServiceNavigated(object sender, NavigationEventArgs e)
{
var navigationService = (NavigationService)sender;
navigationService.Navigated -= NavigationServiceNavigated;
PhoneServices.Current.AppInitialized();
}
Anyone see any issues with this approach? Anyone come up with a better way?

The calling thread cannot access this object because a different thread owns it

i have a problem whenever i Refresh the prograss bar i get the error The calling thread cannot access this object because a different thread owns it
how can i remove it
shashank
backgroundWorker12 = new BackgroundWorker();
timer1.Enabled = true;
//cancel any async processes running for the background worker
//backgroundWorker1.CancelAsync();
backgroundWorker12.DoWork += (s, args) =>
{
BackgroundWorker worker2 = s as BackgroundWorker;
worker2.WorkerReportsProgress = true;
float percentageDone = 20f;
//check if the user status and update the password in xml
CheckUseridPwd();
//call the function to sync the wall chart data
//call the function to sync event relate data
percentageDone = 100f;
ValidateLogin2(txtUserID.Text.Trim(), txtPassword.Password.Trim(), -1);
worker2.ReportProgress((int)percentageDone);
};`
This bit looks like it's using UI controls from the wrong thread:
ValidateLogin2(txtUserID.Text.Trim(), txtPassword.Password.Trim(), -1);
I suggest you capture the user and password in local string variables above the code which adds the event handler - you can use those captured variables within your delegate. That way everything should be on the right thread:
backgroundWorker12 = new BackgroundWorker();
timer1.Enabled = true;
string user = txtUserID.Text.Trim();
string password = txtPassword.Password.Trim();
backgroundWorker12.DoWork += (s, args) =>
{
// ... same code as before up to here
ValidateLogin2(user, password, -1);
worker2.ReportProgress((int)percentageDone);
};
See if you can use the RunWorkerCompleted event of the BackgroundWorker, since you're accessing the UI only after progress is 100% i.e. done..
Then you wouldn't have to worry about thread-affinity of WPF UI controls - since the event handler is invoked again on the right/ UI Thread.
The other option (if you need to access the UI controls before the work is complete) is to cache the object
returned by Dispatcher.CurrentDispatcher on the UI Thread before the work starts and then use object.Invoke to marshal to the right thread from the thread pool thread that is executing your DoWork handler. See some code here.
Have you tried invoking ValidateLogin2
you can either do it directly from your code shown, or in ValidateLogin2 check if the method itself requires invoking. If not, go ahead and validate, but if it does, then have it invoke itself
void ValidateLogin2(...)
{
if (this.InvokeRequired)
{
//Invokes itself if required
BeginInvoke(new MethodInvoker(delegate(){ValidateLogin2(...);}));
}
else
{
//validate login here
}
}

Invoke or BeginInvoke cannot be called on a control until the window handle has been created

I have a SafeInvoke Control extension method similar to the one Greg D discusses here (minus the IsHandleCreated check).
I am calling it from a System.Windows.Forms.Form as follows:
public void Show(string text) {
label.SafeInvoke(()=>label.Text = text);
this.Show();
this.Refresh();
}
Sometimes (this call can come from a variety of threads) this results in the following error:
System.InvalidOperationException occurred
Message= "Invoke or BeginInvoke cannot be called on a control until the window handle has been created."
Source= "System.Windows.Forms"
StackTrace:
at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous)
at System.Windows.Forms.Control.Invoke(Delegate method, Object[] args)
at System.Windows.Forms.Control.Invoke(Delegate method)
at DriverInterface2.UI.WinForms.Dialogs.FormExtensions.SafeInvoke[T](T control, Action`1 action)
in C:\code\DriverInterface2\DriverInterface2.UI.WinForms\Dialogs\FormExtensions.cs:line 16
What is going on and how do I fix it? I know as much as it is not a problem of form creation, since sometimes it will work once and fail the next time so what could the problem be?
PS. I really really am awful at WinForms, does anyone know a good series of articles that explains the whole model and how to work with it?
It's possible that you're creating your controls on the wrong thread. Consider the following documentation from MSDN:
This means that InvokeRequired can
return false if Invoke is not required
(the call occurs on the same thread),
or if the control was created on a
different thread but the control's
handle has not yet been created.
In the case where the control's handle
has not yet been created, you should
not simply call properties, methods,
or events on the control. This might
cause the control's handle to be
created on the background thread,
isolating the control on a thread
without a message pump and making the
application unstable.
You can protect against this case by
also checking the value of
IsHandleCreated when InvokeRequired
returns false on a background thread.
If the control handle has not yet been
created, you must wait until it has
been created before calling Invoke or
BeginInvoke. Typically, this happens
only if a background thread is created
in the constructor of the primary form
for the application (as in
Application.Run(new MainForm()),
before the form has been shown or
Application.Run has been called.
Let's see what this means for you. (This would be easier to reason about if we saw your implementation of SafeInvoke also)
Assuming your implementation is identical to the referenced one with the exception of the check against IsHandleCreated, let's follow the logic:
public static void SafeInvoke(this Control uiElement, Action updater, bool forceSynchronous)
{
if (uiElement == null)
{
throw new ArgumentNullException("uiElement");
}
if (uiElement.InvokeRequired)
{
if (forceSynchronous)
{
uiElement.Invoke((Action)delegate { SafeInvoke(uiElement, updater, forceSynchronous); });
}
else
{
uiElement.BeginInvoke((Action)delegate { SafeInvoke(uiElement, updater, forceSynchronous); });
}
}
else
{
if (uiElement.IsDisposed)
{
throw new ObjectDisposedException("Control is already disposed.");
}
updater();
}
}
Consider the case where we're calling SafeInvoke from the non-gui thread for a control whose handle has not been created.
uiElement is not null, so we check uiElement.InvokeRequired. Per the MSDN docs (bolded) InvokeRequired will return false because, even though it was created on a different thread, the handle hasn't been created! This sends us to the else condition where we check IsDisposed or immediately proceed to call the submitted action... from the background thread!
At this point, all bets are off re: that control because its handle has been created on a thread that doesn't have a message pump for it, as mentioned in the second paragraph. Perhaps this is the case you're encountering?
I found the InvokeRequired not reliable, so I simply use
if (!this.IsHandleCreated)
{
this.CreateHandle();
}
Here is my answer to a similar question:
I think (not yet entirely sure) that
this is because InvokeRequired will
always return false if the control has
not yet been loaded/shown. I have done
a workaround which seems to work for
the moment, which is to simple
reference the handle of the associated
control in its creator, like so:
var x = this.Handle;
(See
http://ikriv.com/en/prog/info/dotnet/MysteriousHang.html)
The method in the post you link to calls Invoke/BeginInvoke before checking if the control's handle has been created in the case where it's being called from a thread that didn't create the control.
So you'll get the exception when your method is called from a thread other than the one that created the control. This can happen from remoting events or queued work user items...
EDIT
If you check InvokeRequired and HandleCreated before calling invoke you shouldn't get that exception.
If you're going to use a Control from another thread before showing or doing other things with the Control, consider forcing the creation of its handle within the constructor. This is done using the CreateHandle function.
In a multi-threaded project, where the "controller" logic isn't in a WinForm, this function is instrumental in Control constructors for avoiding this error.
Add this before you call method invoke:
while (!this.IsHandleCreated)
System.Threading.Thread.Sleep(100)
Reference the handle of the associated control in its creator, like so:
Note: Be wary of this solution.If a control has a handle it is much slower to do things like set the size and location of it. This makes InitializeComponent much slower. A better solution is to not background anything before the control has a handle.
var that = this; // this is a form
(new Thread(()=> {
var action= new Action(() => {
something
}));
if(!that.IsDisposed)
{
if(that.IsHandleCreated)
{
//if (that.InvokeRequired)
that.BeginInvoke(action);
//else
// action.Invoke();
}
else
that.HandleCreated+=(sender,event) => {
action.Invoke();
};
}
})).Start();
I had this problem with this kind of simple form:
public partial class MyForm : Form
{
public MyForm()
{
Load += new EventHandler(Form1_Load);
}
private void Form1_Load(Object sender, EventArgs e)
{
InitializeComponent();
}
internal void UpdateLabel(string s)
{
Invoke(new Action(() => { label1.Text = s; }));
}
}
Then for n other async threads I was using new MyForm().UpdateLabel(text) to try and call the UI thread, but the constructor gives no handle to the UI thread instance, so other threads get other instance handles, which are either Object reference not set to an instance of an object or Invoke or BeginInvoke cannot be called on a control until the window handle has been created. To solve this I used a static object to hold the UI handle:
public partial class MyForm : Form
{
private static MyForm _mf;
public MyForm()
{
Load += new EventHandler(Form1_Load);
}
private void Form1_Load(Object sender, EventArgs e)
{
InitializeComponent();
_mf = this;
}
internal void UpdateLabel(string s)
{
_mf.Invoke((MethodInvoker) delegate { _mf.label1.Text = s; });
}
}
I guess it's working fine, so far...
What about this :
public static bool SafeInvoke( this Control control, MethodInvoker method )
{
if( control != null && ! control.IsDisposed && control.IsHandleCreated && control.FindForm().IsHandleCreated )
{
if( control.InvokeRequired )
{
control.Invoke( method );
}
else
{
method();
}
return true;
}
else return false;
}

Resources