How to use CancellationTokenSource to close a dialog on another thread?
Your problem is that your preview window runs on another thread. When you trigger cancellation, you execute the registered action of the cancellation token on that thread, not on the thread your preview is running on.
The gold standard in these cases is to not use two UI threads. This will usually cause trouble and the work you need to handle them is usually not worth it.
If you want to stay with your solution or if you want to trigger cancellation from a background thread, you have to marshal your close operation to the thread your window is opened in:
Action closeAction = () => previewWindow.Close();previewWindow.Dispatcher.Invoke(closeAction);
The problem with your code is
With the selection of a menu item, a second window is created on a background thread:
// Print Previewpublic static void PrintPreview(FixedDocument fixeddocument, CancellationToken ct){ // Was cancellation already requested? if (ct.IsCancellationRequested) ct.ThrowIfCancellationRequested(); ............................... // Use my custom document viewer (the print button is removed). var previewWindow = new PrintPreview(fixedDocumentSequence); //Register the cancellation procedure with the cancellation token ct.Register(() => previewWindow.Close() ); previewWindow.ShowDialog(); }}
And what I presume to be
Task.Run(() => PrintPreview(foo, cancel));
The correct solution is to do everything on a single thread.
public static Task<bool> PrintPreview(FixedDocument fixeddocument, CancellationToken ct){ var tcs = new TaskCompletionSource<bool>(); // Was cancellation already requested? if (ct.IsCancellationRequested) tcs.SetResult(false); else { // Use my custom document viewer (the print button is removed). var previewWindow = new PrintPreview(fixedDocumentSequence); //Register the cancellation procedure with the cancellation token ct.Register(() => previewWindow.Close()); previewWindow.Closed += (o, e) => { var result = previewWindow.DialogResult; if (result.HasValue) tcs.SetResult(result.Value); else tcs.SetResult(false); } previewWindow.Show(); } return tcs.Task;}
Then call
var shouldPrint = await PrintPreview(foo, cancel); if (shouldPrint) await PrintAsync(foo);