如何使用CancellationTokenSource关闭另一个线程上的对话框?

时间:2016-12-07 05:58:42

标签: c# wpf multithreading cancellationtokensource

这与我的其他问题How to cancel background printing有关。

我试图更好地理解CancellationTokenSource模型以及如何跨线程边界使用它。

我有一个主窗口(在UI线程上)后面的代码所在的位置:

 public MainWindow()
        {
            InitializeComponent();

            Loaded += (s, e) => {
                DataContext = new MainWindowViewModel();
                Closing += ((MainWindowViewModel)DataContext).MainWindow_Closing;

            };
        }

在关闭时正确调用CloseWindow代码:

 private void CloseWindow(IClosable window)
        {
            if (window != null)
            {
                windowClosingCTS.Cancel();
                window.Close();
            }
        }

通过选择菜单项,在后台线程上创建第二个窗口:

    // Print Preview
    public 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();

        }
    }

在MainWindowViewModel(在UI线程上),我把:

public CancellationTokenSource windowClosingCTS { get; set; }

使用其构造函数:

    // Constructor
    public MainMenu()
    {
        readers = new List<Reader>();
        CloseWindowCommand = new RelayCommand<IClosable>(this.CloseWindow);
        windowClosingCTS = new CancellationTokenSource();
    }

现在我的问题。当关闭UI线程上的MainWindow时,windowClosingCTS.Cancel()会立即调用使用ct注册的委托,即 previewWindow.Close()被调用。 现在立即返回&#34;如果(Windows!= null):

  

&#34;调用线程因为不同而无法访问此对象   线程拥有它。&#34;

那么我做错了什么?

2 个答案:

答案 0 :(得分:4)

您的问题是您的预览窗口在另一个线程上运行。当您触发取消时,您在该线程上执行取消令牌的注册操作,而不是在运行预览的线程上执行。

在这些情况下,黄金标准是不使用两个UI线程。这通常会带来麻烦,而处理它们所需的工作通常是不值得的。

如果您想继续使用您的解决方案,或者想要从后台线程触发取消,您必须将您的关闭操作封送到您的窗口打开的线程中:

var initDataTable = function (selector) {
    $.when(App.availableViewsXhr).done(function () {
        $.ajax({
            url: "api/v1.0/views/" + App.activeView + "/metadata",
            type: "GET"
        })
                .done(function (data) {
                    if (App.dataTable) {
                        App.dataTable.destroy();
                        $(selector).html('');
                    }
                    appendHeaderAndFooter(selector, data.columns);
                    var dom = "<'row'<'col-sm-7'AEOBF><'col-sm-5'f>>";
                    dom += "<'row'<'#notice.col-sm-12'>>";
                    dom += "<'row'<'col-sm-12'tr>>";
                    dom += "<'row' <'col-sm-12'lip>>";
                    App.dataTable = $(selector).DataTable({
                        "lengthMenu": [[25, 50, 100], ['25', '50', '100']],
                        "dom": dom,
                        "buttons": [
                            {
                                className: 'btn-sm',
                                extend: 'colvis',
                                columns: getHideableColumnNumbers(data.columns)
                            }
                        ],
                        "stateSave": true,
                        "colReorder": {
                            "realtime": false,
                            "fixedColumnsLeft": data.config.colReorder.fixedColumnsLeft
                        },
                        "order": [[getFirstOrderableColumnNumber(data.columns), "desc"]],
                        "columns": getColumns(data.columns, data.config),
                        "serverSide": true,
                        "ajax": {
                            "url": 'api/v1.0/views/' + App.activeView,
                            "type": "POST",
                            "complete": function () {
                                App.dataTable.columns.adjust();
                            }
                        },
                        "scrollX": true,
                        stateSaveCallback: function (settings, data) {
                            console.log('save');
                        },
                        stateLoadCallback: function (settings) {
                            console.log('load');
                        }
                    });
                });
    });
};

答案 1 :(得分:3)

您的代码存在问题

  

通过选择菜单项,在a上创建第二个窗口   后台主题​​:

// Print Preview
public 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();

    }
}

我认为是

Task.Run(() => PrintPreview(foo, cancel));

正确的解决方案是在一个线程上完成所有事情。

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;
}

然后致电

 var shouldPrint = await PrintPreview(foo, cancel);
 if (shouldPrint)
     await PrintAsync(foo);