C#WPF从另一个线程

时间:2018-01-22 17:21:25

标签: c# wpf multithreading progress-bar backgroundworker

我有一个UserControl,其中包含一个使用大量转换器显示值的大表。我试图在具有Indeterminate状态的新窗口中显示ProgressBar,该窗口在触发UserControl Loaded事件时自动关闭。

这是我的UserControl的反码中的线程创建:

Thread progressBarThread = new Thread(new ThreadStart(delegate { 
    ProgressBarWindow progressBarWindow = new ProgressBarWindow();
    progressBarWindow.IsIndeterminate = true;
    progressBarWindow.LaunchProgressBarInBackground();
    progressBarWindow.ShowDialog();
}));

progressBarThread.SetApartmentState(ApartmentState.STA);
progressBarThread.Start();

this.Loaded += (sender, e) => { Dispatcher.FromThread(progressBarThread).InvokeShutdown(); };

这段代码正在“工作”,它正在打开progressBarWindow但是当我使用InvokeShutdown关闭线程时(最丑陋的方式来做,我同意)。问题是来自我的backgroundWorker的DoWork。

这是DoWork功能:

private void BackgroundWorker_WaitUntilShouldStop(object sender, DoWorkEventArgs e)
{
    // Do not access the form's BackgroundWorker reference directly.
    // Instead, use the reference provided by the sender parameter.
    BackgroundWorker bw = sender as BackgroundWorker;

    // Start the time-consuming operation.
    while (!bw.CancellationPending)
    {
        Thread.Sleep(500);
    }
}

我想调用ProgressBarWindow中包含的函数来阻止DoWork运行并使用以下方法正常关闭ProgressBarWindow:

progressBar.StopProgressBarInBackground();

此方法正在调用backgroundWorker.CancelAsync();

这将导致backgroundWorker终止并且progressBarWindow自动关闭。

但我无法访问progressBarThread中的progressBar。我尝试使用以下方式传递UserControl:

progressBarThread.Start(this);

this是主窗口。

尝试从主线程传递变量时,抛出此错误:

An exception of type 'System.InvalidOperationException' occurred in WindowsBase.dll but was not handled in user code  Additional information: The calling thread cannot access this object because a different thread owns it.

有人在没有使用myThread.InvokeShutdown()的情况下有一个很好的正确方法吗?

修改1:

我使用volatile变量找到了解决问题的方法:

volatile bool _isLoaded;
void CreateAndStopProgressBarWhenIsLoaded()
{
    Thread progressBarThread= new Thread(new ThreadStart(    
    {
        Controls.ProgressBar.ProgressBar progressBar = new Controls.ProgressBar.ProgressBar();
        progressBar.IsIndeterminate = true;
        progressBar.LaunchProgressBarInBackground();

        DispatcherTimer dispatcherTimer = new DispatcherTimer();
        dispatcherTimer = new DispatcherTimer();
        dispatcherTimer.Tick += (sender, e) => {
            if (_isLoaded)
                progressBar.StopProgressBarInBackground();
        };
        // Try to stop `progressBar` every 500 ms
        dispatcherTimer.Interval = TimeSpan.FromMilliseconds(500);
        dispatcherTimer.Start();

        progressBar.ShowDialog();

        // Will only be reached once progressBar.ShowDialog(); returns
        dispatcherTimer.Stop();
    }));
    progressBarThread.SetApartmentState(ApartmentState.STA);
    progressBarThread.Start();

    this.Loaded += (sender, e) => {
        _isLoaded = this.IsLoaded;
        progressBarThread.Join(); // Wait for progressBarThread to end
    };
}

现在问题是你有更好的解决方案吗?

编辑2:

感谢@AlexSeleznyov:

,这是我的最终解决方案
    void CreateAndStopProgressBarWhenIsLoaded()
    {
        Controls.ProgressBar.ProgressBar pb = null;
        ManualResetEvent manualResetEvent = new ManualResetEvent(false);

        Thread progressBarThread = new Thread(new ThreadStart(delegate
        {
            Controls.ProgressBar.ProgressBar progressBar = new Controls.ProgressBar.ProgressBar();
            pb = progressBar;
            manualResetEvent.Set();
            progressBar.IsIndeterminate = true;
            progressBar.LaunchProgressBarInBackground();

            progressBar.ShowDialog();
        }));
        progressBarThread.SetApartmentState(ApartmentState.STA);
        progressBarThread.Start();

        this.Loaded += (sender, e) => {
            pb.Dispatcher.Invoke(delegate {
                manualResetEvent.WaitOne();
                pb.StopProgressBarInBackground();
            });
            progressBarThread.Join();
        };
    }

2 个答案:

答案 0 :(得分:0)

我认为你可以在ProgressBarWindow中使用BackgroundWorker.RunWorkerCompleted - 当你取消backgroundWorker时会调用它。

private void backgroundWorker_RunWorkerCompleted(object sender, 
                                                 RunWorkerCompletedEventArgs e)
{
    if (e.Cancelled)
    {
        //close the window
    }
}

答案 1 :(得分:0)

您可以尝试这种方法,缓存@JoinTable实例,然后从另一个线程使用它。 ProgressBar根除了Dispatcher.Invoke我在评论中提到的需求。

CheckAccess

}