My image processing is all in C++. Was using MFC, now exploring to upgrade to WPF. WPF side will be mainly user interface, I'm passing image pointers from unmanaged to managed for display. I found that if I'm use thread to loop the backend vector it will not display anything, but if I change it to button trigger one by one it will be functional. With the same functions, I have no idea what's going on.
WPF own my CppCli object, which it can directly call any function in c++ / cli. What could be wrong here.
wpf cs code:
private CppCli cli;
public MainWindow() {
cli = new CppCli();
cli.ImageUpdated += Cli_ImageUpdated; // delegate and event to call method Cli_ImageUpdated
InitializeComponent();
}
private void Cli_ImageUpdated(object sender, ImageUpdateEventArgs e)
{
Dispatcher.Invoke(() =>
{
MyImageControl.Source = e.img;
});
}
private void LiveToggle_Click(object sender, RoutedEventArgs e) {
cli.LiveToggle();
}
Cpp cli code:
public ref class ImageUpdateEventArgs : EventArgs {
public: BitmapSource^ img;
ImageUpdateEventArgs(BitmapSource^ bitmap) {
img = bitmap;
}
};
void CppCli::LiveToggle() {
running = !running;
if (running) {
liveThread = gcnew Thread(gcnew ThreadStart(this, &MathFunctions::UpdateDisplay));
liveThread->IsBackground = true;
liveThread->Start();
}
//UpdateDisplay() // This works if replace the whole thing above.
}
void CppCli::UpdateDisplay() {
while (running) { // Remove this while loop and it works.
int stride, rows, cols, bufferSize;
stride = cvObj->images[imageIndex].step[0];
rows = cvObj->images[imageIndex].rows;
cols = cvObj->images[imageIndex].cols;
bufferSize = cvObj->images[imageIndex].total() * cvObj->images[imageIndex].elemSize();
IntPtr ptr(cvObj->images[imageIndex].ptr());
ImageUpdated(this, gcnew ImageUpdateEventArgs(BitmapSource::Create(cols, rows, 96, 96, PixelFormats::Bgra32, nullptr, ptr, bufferSize, stride)));
Thread::Sleep(100);
imageIndex++;
if (imageIndex >= cvObj->images.size())
{
imageIndex = 0;
}
}
}
Since the BitmapSource is created in a thread other than the UI thread, it must be frozen before being assigned to the Source property of the Image element in the UI thread:
private void Cli_ImageUpdated(object sender, ImageUpdateEventArgs e)
{
e.img.Freeze(); // call here or in the ImageUpdateEventArgs constructor
Dispatcher.Invoke(() => MyImageControl.Source = e.img);
}
For details, please see the Remarks section in Freezable.Freeze and Freezable.IsFrozen.