我对后台工作程序有疑问,并且在5秒操作正在进行时在WPF应用程序中显示UI。我有一个必须连接到相机的对象,这个连接需要一段时间(几秒钟)。同时,当这个对象试图连接时,我想显示一个带旋转圆圈的单独窗口(WaitWindow),表明应用程序仍在运行,但用户必须等待几秒钟才能完成连接。到现在为止还挺好。
问题在于我有一个计时器,它每秒从摄像机获取当前图像作为位图,并在画布上的Image控件中显示该位图(_image)。因此,当必须呈现此位图时,应用程序会因此异常而崩溃:
“Windows在mywpfapp.exe中触发了断点 这可能是由于堆的损坏,这表示mywpfapp.exe或它已加载的任何DLL中的错误。 这也可能是由于用户在mywpfapp.exe具有焦点时按下F12。 输出窗口可能包含更多诊断信息。“
我认为这是因为线程不同 - backgroundWorkers连接到相机的线程 - 想要显示位图的UI线程
它抛出异常的第一行代码是“source.Save(ms,System.Drawing.Imaging.ImageFormat.Bmp);”,即使源代码似乎有效。
以下是代码:
public partial class MainWindow
{
private WaitWindow _waitWindow;
private ConnectionObject _myConnObject;
private Timer _myTimer;
private bool _isConnected;
public MainWindow()
{
_myTimer = new Timer();
_myTimer.Tick += new EventHandler(TimerEventProcessor);
}
private void btnConnect_Click(object sender, RoutedEventArgs e)
{
_isConnected = false;
BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += (sender1, e1) =>
{
// this takes at least 5 seconds
_isConnected = _myConnObject.Connect();
};
backgroundWorker.RunWorkerCompleted += (sender1, e1) =>
{
ConnectEnded();
};
backgroundWorker.RunWorkerAsync();
// show wait window
_waitWindow = new WaitWindow();
_waitWindow.Owner = this;
_waitWindow.ShowDialog();
}
private void ConnectEnded()
{
if (_waitWindow != null)
{
_waitWindow.Close();
_waitWindow = null;
}
if (_isConnected)
{
lblStatus.Content = "connected";
}
else
{
lblStatus.Content = "not connected";
}
}
private BitmapSource TimerEventProcessor(Object myObject, EventArgs myEventArgs)
{
if (_isConnected)
{
// the bitmap is
Bitmap source = _myConnObject.GetBitmapFromDevice();
if (source == null)
{
_image.Source = null;
}
else
{
MemoryStream ms = new MemoryStream();
// here it throws an exception
source.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
ms.Position = 0;
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = ms;
bi.EndInit();
_image.Source = bi;
}
}
}
}
您对此代码有什么问题有什么想法吗?或者有更好的方法吗?
非常感谢!
答案 0 :(得分:1)
这可能是由于具有线程亲和性,以及您的Timer(当前)在ThreadPool线程上运行,而不是在UI线程上运行。
您可能需要考虑切换为使用DispatcherTimer。这将导致计时器的事件在UI线程而不是ThreadPool线程上触发,这将允许您将所有图像处理保留在主线程上。