I have a UserControl with a big table that is displaying values using a lot of converters. I am trying to display a ProgressBar in a new Window with Indeterminate State that is closing automatically when the UserControl Loaded event is fired.
This is the Thread creation in the backcode of my 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(); };
This code is "working", it is opening progressBarWindow but when I shutdown the thread using InvokeShutdown (the ugliest way to do it, i agree). The problem is that the DoWork from my backgroundWorker.
Here is the DoWork function :
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);
}
}
I would like to call my function contained in ProgressBarWindow to stop the DoWork from runnning and close the ProgressBarWindow normaly using :
progressBar.StopProgressBarInBackground();
This method is calling backgroundWorker.CancelAsync();
This will result in backgroundWorker terminating and progressBarWindow closing automatically.
But I don't have access to progressBar that is inside the progressBarThread. I tried to pass my UserControl using :
progressBarThread.Start(this);
this
being the main window.
When trying to pass a variable from the main thread, this error is thrown :
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.
Does someone have a nice and correct way to do it without using myThread.InvokeShutdown() ?
Edit 1 :
I found a solution to my problem using a volatile variable :
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
};
}
Now the question is do you have a better solution ?
Edit 2 :
Here is my final solution thanks to @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();
};
}
I think that you can use BackgroundWorker.RunWorkerCompleted in ProgressBarWindow - it will be invoked when you cancel a backgroundWorker.
private void backgroundWorker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
//close the window
}
}
You might try this approach, to cache ProgressBar
instance and then use it from another thread. Dispatcher.Invoke
eradicates need for CheckAccess
I've mentioned in comments.
void CreateAndStopProgressBarWhenIsLoaded()
{
var pb = new Controls.ProgressBar.ProgressBar[1];
Thread progressBarThread= new Thread(new ThreadStart(
{
Controls.ProgressBar.ProgressBar progressBar = new Controls.ProgressBar.ProgressBar();
pb[0] = progressBar;
progressBar.IsIndeterminate = true;
progressBar.LaunchProgressBarInBackground();
progressBar.ShowDialog();
}));
progressBarThread.SetApartmentState(ApartmentState.STA);
progressBarThread.Start();
this.Loaded += (sender, e) => {
pb[0].Dispatcher.Invoke(()=>pb[0].Close());
};
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.