FromCurrentSynchronizationContext block throwing nullreferenceexception on Windows Form close - winforms

I am developing a windows form application. Following is the code segment that is executed on a button click event.
var taskExecute= Task<Datatable>.Run(() =>
{
return = GetDt(); // time consuming method
})
.ContinueWith(task=>
{
this.datagridVal.Datasource = task.Result;
}, TaskScheduler.FromCurrentSynchronizationContext());
this works fine. But If I start the thread and then immediately click on the cross(x) button of the form then the following line of code throws NullReferenceException (most probably because the form has been already disposed and so is the datagrid control)
this.datagridVal.Datasource = task.Result;
How can I modify the code so that this block of code does not throw any exception if the form is closed when the thread is running? Also please suggest if there is any better way to handle the scenario using

If the problem indeed comes from the fact that the form is disposed, then you could handle that with this.IsDisposed:
var taskExecute= Task<Datatable>.Run(() =>
{
return = GetDt(); // time consuming method
})
.ContinueWith(task=>
{
if (!this.IsDisposed)
{
this.datagridVal.Datasource = task.Result;
}
}, TaskScheduler.FromCurrentSynchronizationContext());

Related

WPF: How to make Calls to Dispatcher.Invoke() Synchronous?

In my MVVM based Wix Managed Bootstrapper application, while handling different events, I'm trying to show the user a view to get some input. It looks like Burn events are executing Asynchronously because using Dispatcher.Invoke(), it is skipping or passing by the view and hitting the last event, i.e not waiting for this input task to finish.
Here is the event handler which needs to finish before hitting last one:
Please note that when MessageBox.Show is popped, it waits until we close it. While debugging, I see it actually switched to MissingSourceView and loaded MissingSourceViewModel, but then while skipping it, and executes ApplyComplete();
BootstrapperApplication.ResolveSource += (sender, e) => {
System.Windows.Forms.MessageBox.Show("Inside ResolveSource");
WixBootstrapperData.CurrentDispatcher.Invoke(((Action)(() =>
{
WixBootstrapperData.CurrentViewModel = new MissingSourceViewModel(WixBootstrapperData, InfoMessage);
})));
};
BootstrapperApplication.ApplyComplete += (sender, e) =>
{
WixBootstrapperData.BootstrapperApplicationModel.FinalResult = e.Status;
WixBootstrapperData.CurrentDispatcher.Invoke((Action)(() =>
{
InfoMessage = AppResource.MsgFinished;
WixBootstrapperData.CurrentViewModel = new FinishViewModel(WixBootstrapperData, InfoMessage);
}
));
};
I guess, I should use await and async with ResolveSource(), but I face errors like:
Error CS1061 'BaseViewModel' does not contain a definition for
'GetAwaiter' and no extension method 'GetAwaiter' accepting a first
argument of type 'Task' could be found (are you missing a using
directive or an assembly reference?)
Any idea how to make it wait for finishing inside ResolveSource() and then jump to wherever it wants?
Use this, and please tell if this solves your problem.
WixBootstrapperData.CurrentDispatcher.Invoke(((Action)(() =>
{
Task.Factory.StartNew(() => {
WixBootstrapperData.CurrentViewModel = new MissingSourceViewModel(WixBootstrapperData, InfoMessage);
}).RunSynchronously();
})));

async await not waiting

Tried to find something similar and read all the answers given but couldn`t find something that will explain it to me.
Here is a sample code for opening a dialog popup (WPF). I want after the ShowOverlayView turns to True, that the UI will be accessible (this is why the async-await) and the program to wait until it is finished when the user clicks "Close".
Small clarification:
ShowOverlayViewModel sets a boolean to true/false for the Visibility property of a ContentControl. Since this is the case then I have nothing to wait for "the regular way".
Currently when the view is being "visible" the MessageBox is immediately shown.
Seems like it doesn`t wait for the AutoResetEvent.
Small update: It seems to be relevant specific to the MessageBox. I tried to change the Message property after the await code line and it occurred only after the are.Set(). I would still love to know why did the MessageBox act as it did.
private void CommandAction()
{
ShowOptionsDialog();
MessageBox.Show("");
}
private async void ShowOptionsDialog()
{
var are = new AutoResetEvent(false);
var viewmodel = new DialogPopupViewModel();
viewmodel.Intialize("some title", "some message", DialogPopupViewModel.YesNoCancelButtons);
SetOverlayViewModel(viewmodel);
viewmodel.SetCloseViewAction(() =>
{
HideOverlayView();
are.Set();
});
ShowOverlayView = true;
await Task.Factory.StartNew(() =>
{
are.WaitOne();
//return viewmodel.DialogResult;
});
//return DialogResultEnum.Cancel;
}
Thanks for the help
Classic async-void bug. Research what async void does and why it's bad practice. Since ShowOptionsDialog() does not return a task that is awaited execution continues immediately. Change the return type to Task and await the result of the method call.
You can replace the event with a TaskCompletionSource<object> and say await myTcs.Task. A TCS is a more TPL-friendly event.

MVVM Light message being received rarely, otherwise never received

I am having a strange issue with MVVM Light messaging. I am using it through my application and it has worked fine. I am now having a problem where my message is received roughly 10% of the time I try to send it. I'm assuming it gets shot off in to oblivion the other 90% of the time and I receive no errors and my application goes on as if nothing happened.
What I have is a context menu in a view that has a collection of statuses. Each user has a status and you can change it via this context menu. Every time the status changes I need to broadcast a message to another view model. This is the message that fails. Here is some code, feel free to ask me more questions if this is not clear enough for you.
This is located in my constructor, fired from a context menu item click:
this.UpdateUserStatus = new RelayCommand<UserStatusVM>(x =>
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
UpdateStatus(x);
}));
UpdateStatus method from relay command:
private void UpdateStatus(UserStatusVM status)
{
var userStatusEventId = _userService.CreateUserEvent(status.Id, CurrentUser.Id);
//This returns a business object that I would like to send
//over the wire to the other view model
var userEvent = _userService.GetUserEvent(userStatusEventId);
if (userEvent != null)
{
Messenger.Default.Send<UserEvent>(userEvent, "Broadcast");
}
//These fire off a collection changed event
CurrentUser.StatusId = status.Id;
CurrentUser.Status = status.Name;
CurrentUser.UpdatedDate = DateTime.Now;
CurrentUser.IsOnDuty = UserStatuses.IsOnDuty(status.Id);
}
and the message reception in the other view model:
Messenger.Default.Register<UserEvent>(this, "Broadcast", (userEvent) =>
{
proxy.Invoke("NewUserEvent", userEvent);
});

WPF - Task.Run(() => window.ShowDialog) fails

I have to implement busy indication and progress reporting. The constraint is, that I have to use the provided Control Library, which offers a Window for progress reporting.
The following code works fine, but does not block the UI, which in some times is required.
private async void StartLongRunningTask2Sync() {
var wndHandle = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
if (wndHandle == null)
{
return;
}
IntPtr windowHandle = new WindowInteropHelper(wndHandle).Handle;
var progressWindow = new ProgressBarCustomized(windowHandle)
{
Value = 0, CanCancel = true, CanRetry = false, Thumbnail = null, IsIndeterminate = true
};
progressWindow.Show();
await Task.Run(() => this.longRunningTaskComponent.DoLongRunningTask(this.taskIterations, this.iterationSleepTime));
progressWindow.Close();
}
The following code, which blocks the UI would work so far that the dialog is opened, but the long running task never gets executed until the dialog is closed again:
private async void StartLongRunningTask2Sync() {
var wndHandle = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
if (wndHandle == null)
{
return;
}
IntPtr windowHandle = new WindowInteropHelper(wndHandle).Handle;
var progressWindow = new ProgressBarCustomized(windowHandle)
{
Value = 0, CanCancel = true, CanRetry = false, Thumbnail = null, IsIndeterminate = true
};
progressWindow.ShowDialog();
await Task.Run(() => this.longRunningTaskComponent.DoLongRunningTask(this.taskIterations, this.iterationSleepTime));
progressWindow.Close();
}
So I tried with this approach:
private async void StartLongRunningTask2Sync() {
var wndHandle = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
if (wndHandle == null)
{
return;
}
IntPtr windowHandle = new WindowInteropHelper(wndHandle).Handle;
var progressWindow = new ProgressBarCustomized(windowHandle)
{
Value = 0, CanCancel = true, CanRetry = false, Thumbnail = null, IsIndeterminate = true
};
Task.Run(() => progressWindow.ShowDialog());
await Task.Run(() => this.longRunningTaskComponent.DoLongRunningTask(this.taskIterations, this.iterationSleepTime));
progressWindow.Close();
}
When doing this, I get the following error:
The calling thread cannot access this object because a different thread owns it.
After investigation of the custom progress window I found out, that the call "base.ShowDialog()" throws this error.
Is there a way to do what I like or do I have to do this with a totally different approach?
Best regards
UPDATE:
Yes, I have searched for this error and yes, I have tried several approaches with Dispatcher.Invoke() etc...
So the "real" question:
How can I show a blocking Window when a long running task is running and closing it after the long running task has finished and, eventually, inform the window about the progress of the action. The solution should (preferably) work with the MVVM pattern and not rely on (too much) code behind.
So the "real" question: How can I show a blocking Window when a long running task is running and closing it after the long running task has finished and, eventually, inform the window about the progress of the action.
You've already got most of the pieces; you just need to put them together.
How can I show a blocking Window
All UI should go on a single GUI thread. This isn't strictly necessary, but it's a great simplifier and works for the vast, vast majority of applications. A "blocking Window" is known in the UI world as a "modal dialog", and you show one by calling ShowDialog.
// Start the long-running operation
var task = LongRunningOperationAsync();
// Show the dialog
progressWindow.ShowDialog();
// Retrieve results / propagate exceptions
var results = await task;
closing it after the long running task has finished
For this, you need to wire up the completion of the task to close the window. This is pretty straightforward to do using async/await:
async Task DoOperationAsync(ProgressWindow progressWindow)
{
try
{
await LongRunningOperationAsync();
}
finally
{
progressWindow.Close();
}
}
// Start the long-running operation
var task = DoOperationAsync(progressWindow);
// Show the dialog
progressWindow.ShowDialog();
// Retrieve results / propagate exceptions
var results = await task;
inform the window about the progress of the action
Assuming your operation is using the standard IProgress<T> interface for reporting progress:
async Task DoOperationAsync(Window progressWindow, IProgress<int> progress)
{
try
{
await LongRunningOperationAsync(progress);
}
finally
{
progressWindow.Close();
}
}
var progressWindowVM = ...;
var progress = new Progress<int>(value =>
{
progressWindowVM.Progress = value;
});
var task = DoOperationAsync(progressWindow, progress);
progressWindow.ShowDialog();
var results = await task;
Another common use case to consider is the cancelling of the operation if the user closes the progress dialog themselves. Again, this is straightfoward if your operation is already using the standard CancellationToken:
async Task DoOperationAsync(Window progressWindow, CancellationToken token, IProgress<int> progress)
{
try
{
await LongRunningOperationAsync(token, progress);
}
catch (OperationCanceledException) { }
finally
{
progressWindow.Close();
}
}
var progressWindowVM = ...;
var progress = new Progress<int>(value =>
{
progressWindowVM.Progress = value;
});
var cts = new CancellationTokenSource();
progressWindow.Closed += (_, __) => cts.Cancel();
var task = DoOperationAsync(progressWindow, cts.Token, progress);
progressWindow.ShowDialog();
var results = await task;
The solution should (preferably) work with the MVVM pattern and not rely on (too much) code behind.
MVVM works great within a single window. As soon as you start trying to data-bind window-level actions and attributes, a lot of it falls apart. This is not due to MVVM being a poor pattern, but rather just that a lot of MVVM frameworks do not handle this well.
The example code above only uses data binding to report progress to the progress dialog. If your MVVM framework can data-bind the showing/hiding of a modal window, then you could use my NotifyTaskCompletion type to drive that. Also, some frameworks have a more elegant (MVVM) way to handle Window.Closed, but the details depend on your framework.
The calling thread cannot access this object because a different thread owns it.
This is a very common error and if you had searched online, you would have found a very simple explanation.
You cannot manipulate UI objects on a non UI thread.
The solution is simple. Don't attempt to open a dialog Window on a non UI thread.
Perhaps if you can clarify what your actual question is (by editing your question, not by commenting), then I can help further?
I think I have found a nearly working solution here:
Create MVVM Background Tasks with Progress Reporting
The only thing I have to get around with is the deactivation of the main window when showing the dialog.

Disable screen while loading data

In my wpf application, I have a menu. When I click on one of the elements of the menu, I change my screen data, which is quite a long process.
I tried to disable the main window when I do such a loading, using this method :
private void SetNavigation(MainContentTypeEnum enumVal, int id, ICheckState vm)
{
var parent = Window.GetWindow(this);
var tmpCursor = parent.Cursor;
parent.Cursor = Cursors.Wait;
parent.IsEnabled = false;
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += (o, args) =>
{
try
{
Dispatcher d = args.Argument as Dispatcher;
d.Invoke(new Action(() =>
{
Navigation.Navigator.SetContol(enumVal, id, vm);
}));
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
}
};
bw.RunWorkerCompleted += (o, args) =>
{
parent.IsEnabled = true;
parent.Cursor = tmpCursor;
};
bw.RunWorkerAsync(Dispatcher.CurrentDispatcher);
}
This method works on the very first call, the form is disabled, and then enabled when data is loaded. But on next calls, it doesn't work anymore, everything freezes until the operation completes. I tried setting a breakpoint, and the method is correctly hit and executed. I don't understant why it only works one time...
Have you an idea ?
Thanks in advance!
Edit: A bit of precision: this code is part of a usercontrol, which is why I call the parent using Window.GetWindow(this);
Edit2: Setting a Thread.Sleep(1000); just before invoking the dispatcher does the job. My guess is that the parent.IsEnabled instruction is not executed quickly enough... but why ?
Edit3: Having made some timings, my data retrieval is quite quick. It seems that the problem exists on the binding phase. I set the value to the bound property, and the method returns. However, the UI still frozen for a moment after that.

Resources