[英]Multi threading in WPF using C# (with background worker)
我編寫了代碼來保存應用程序生成的圖像。 圖像大小約為32-35 MB。 將圖像保存為BMB文件時,需要很長時間,大約3-5秒。 為此,我使用了后台工作程序但是在運行后台工作程序時,它顯示了一個錯誤,例如......“無法訪問該對象,因為它是在不同的線程上創建的”。
以下是代碼:
private void btnSaveDesign_Click(object sender, RoutedEventArgs e)
{
Microsoft.Win32.SaveFileDialog sfd = new Microsoft.Win32.SaveFileDialog();
sfd.Title = "Save design as...";
sfd.Filter = "BMP|*.bmp";
if (sfd.ShowDialog() == true)
{
ww = new winWait();
ww.Show();
System.ComponentModel.BackgroundWorker bw = new System.ComponentModel.BackgroundWorker();
bw.DoWork += new System.ComponentModel.DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
fName = sfd.FileName;
cache = new CachedBitmap((BitmapSource)imgOut.Source, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
bw.RunWorkerAsync();
}
}
void bw_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
ww.Close();
}
void bw_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(cache)); //here... it says cant access...
using (FileStream file = File.OpenWrite(fName))
{
encoder.Save(file);
}
}
我已將“緩存”聲明為全局對象。 (當我使用VB.NET在Windows Forms中編程時,類似的技巧也奏效了。)
ww
是我想在執行進程時顯示的等待窗口。
這個怎么做? 在WPF中有多種其他簡單的多線程方法嗎?
創建WPF對象時,會將它們分配給Dispatcher對象。 這不允許創建線程以外的任何線程訪問該對象。 通過調用凍結方法凍結對象可以避免這種情況。 您需要在bitmapsource對象上調用Freeze。 凍結對象后,它就變得無法使用了
您的問題是因為您正在訪問不是由后台工作線程創建的對象。 通常,如果您訪問在主線程中創建並從不同線程訪問的UI控件,則會發生這種情況。
請使用以下代碼。
Dispatcher.Invoke
(
new Action(
delegate()
{
BmpBitmapEncoder encoder = new BmpBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(cache));
using (FileStream file = File.OpenWrite(fName))
{
encoder.Save(file);
}
}
)
);
.NET框架提供了一種使用BackgroundWorker組件進行線程入門的簡單方法。 這包含了大部分的復雜性,使得生成后台線程相對安全。 此外,它允許您在后台線程和UI線程之間進行通信,而無需進行任何特殊編碼。 您可以將此組件與WinForms和WPF應用程序一起使用。 BackgroundWorker提供了一些功能,包括生成后台線程,在完成后取消后台進程的功能,以及將進度報告回UI的機會。
public BackgroudWorker()
{
InitializeComponent();
backgroundWorker = ((BackgroundWorker)this.FindResource("backgroundWorker"));
}
private int DoSlowProcess(int iterations, BackgroundWorker worker, DoWorkEventArgs e)
{
int result = 0;
for (int i = 0; i <= iterations; i++)
{
if (worker != null)
{
if (worker.CancellationPending)
{
e.Cancel = true;
return result;
}
if (worker.WorkerReportsProgress)
{
int percentComplete =
(int)((float)i / (float)iterations * 100);
worker.ReportProgress(percentComplete);
}
}
Thread.Sleep(100);
result = i;
}
return result;
}
private void startButton_Click(object sender, RoutedEventArgs e)
{
int iterations = 0;
if (int.TryParse(inputBox.Text, out iterations))
{
backgroundWorker.RunWorkerAsync(iterations);
startButton.IsEnabled = false;
cancelButton.IsEnabled = true;
outputBox.Text = "";
}
}
private void cancelButton_Click(object sender, RoutedEventArgs e)
{
// TODO: Implement Cancel process
this.backgroundWorker.CancelAsync();
}
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// e.Result = DoSlowProcess((int)e.Argument);
var bgw = sender as BackgroundWorker;
e.Result = DoSlowProcess((int)e.Argument, bgw, e);
}
private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
workerProgress.Value = e.ProgressPercentage;
}
private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show(e.Error.Message);
}
else if (e.Cancelled)
{
outputBox.Text = "Canceled";
workerProgress.Value = 0;
}
else
{
outputBox.Text = e.Result.ToString();
workerProgress.Value = 0;
}
startButton.IsEnabled = true;
cancelButton.IsEnabled = false;
}
我認為你必須將緩存作為參數傳遞給新線程:
bw.RunWorkerAsync(cache);
並從DoWork方法獲取它:
var cache=(CacheType) e.Argument;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.