我编写了代码来保存应用程序生成的图像。图像大小约为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窗体中编程时,类似的技巧也有效。)
ww
是我想在执行过程时显示的等待窗口。
怎么做?在WPF中还有其他简单的多线程方法吗?
答案 0 :(得分:4)
创建WPF对象时,会将它们分配给Dispatcher对象。这不允许创建线程以外的任何线程访问该对象。通过调用冻结方法冻结对象可以避免这种情况。您需要在bitmapsource对象上调用Freeze。冻结对象后,它就变得不可编辑了
答案 1 :(得分:4)
您的问题是因为您正在访问不是由后台工作线程创建的对象。通常,如果您访问在主线程中创建并从不同线程访问的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);
}
}
)
);
答案 2 :(得分:0)
我认为您必须将缓存作为参数传递给新线程:
bw.RunWorkerAsync(cache);
从DoWork方法中获取它:
var cache=(CacheType) e.Argument;
答案 3 :(得分:0)
.NET框架提供了一种简单的线程入门方法 BackgroundWorker组件。这包含了很多复杂性 使得后台线程产生相对安全。另外,它 允许您在后台线程和UI之间进行通信 线程没有做任何特殊编码。您可以使用此组件 使用WinForms和WPF应用程序。 BackgroundWorker提供 几个功能,包括产生一个后台线程, 能够在完成后取消后台进程,以及 有机会将进度报告回您的用户界面。
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;
}