C# 将视图模型的 属性 传递给调度程序
C# passing property of viewmodel into dispatcher
我有一个方法可以更新 WriteableBitmap (FrameDataNew) 的内容,它是我的视图模型 (VM) 中的 属性:
public WriteableBitmap FrameDataNew
{
get { return frameDataNew; }
set
{
frameDataNew = value;
OnProptertyChanged("FrameDataNew");
}
}
private WriteableBitmap frameDataNew = null;
当我收到来自 gstreamer 的新位图时 class 我已经写过更新 FrameDataNew 以便在屏幕上显示最新的帧。 window 是一个简单的图像控件,其源绑定到 FrameDataNew。
以下代码在我的事件处理程序中可以很好地执行此操作:
/// <summary>
/// BitmapCaptured event handler for when a new video frame is received from the IP source
/// </summary>
/// <param name="sender">The originating source of the event</param>
/// <param name="NewBitmap">The new frame in Bitmap form</param>
private void PipeLine_BitmapCaptured(object sender, Bitmap NewBitmap)
{
// render to the screen
Dispatcher.Invoke(() =>
{
// check the existence of the WriteableBitmap and also the dimensions, create a new one if required
if ((VM.FrameDataNew == null) || (VM.FrameDataNew.Width != NewBitmap.Width) || (VM.FrameDataNew.Height != NewBitmap.Height))
VM.FrameDataNew = new WriteableBitmap(NewBitmap.Width, NewBitmap.Height, NewBitmap.HorizontalResolution, NewBitmap.VerticalResolution, PixelFormats.Bgr24, null);
// lock the bitmap data so we can use it
BitmapData data = NewBitmap.LockBits(new Rectangle(0, 0, NewBitmap.Width, NewBitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
// lock the backbuffer of the WritebaleBitmap so we can modify it
VM.FrameDataNew.Lock();
// Copy the bitmap's data directly to the on-screen buffers
CopyMemory(VM.FrameDataNew.BackBuffer, data.Scan0, (data.Stride * data.Height));
// Moves the back buffer to the front.
VM.FrameDataNew.AddDirtyRect(new Int32Rect(0, 0, data.Width, data.Height));
// unlock the back buffer again
VM.FrameDataNew.Unlock();
//
//NewBitmap.UnlockBits(data);
});
}
[DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory")]
public static extern void CopyMemory(IntPtr Destination, IntPtr Source, int Length);
现在我想更新我的程序以处理多个管道和 WriteableBitmap,这样我就可以显示多个视频源。我做的第一件事是创建一个静态实用程序 class,这样我就可以传入我希望更新的新位图 (NewBitmap) 和 WriteableBitmap (VM.FrameDataNew)。然后我在新帧到达时根据需要调用:
实用程序class(只是与我的问题有关的代码):
public static class Utils
{
/// <summary>
/// Inject the source Bitmap into the Destination WriteableBitmap
/// </summary>
/// <param name="Src">The source Bitmap</param>
/// <param name="Dest">The destination WriteableBitmap</param>
public static void InjectBitmap(Bitmap Src, WriteableBitmap Dest)
{
if ((Dest == null) || (Dest.Width != Src.Width) || (Dest.Height != Src.Height))
Dest = new WriteableBitmap(Src.Width, Src.Height, Src.HorizontalResolution, Src.VerticalResolution, PixelFormats.Bgr24, null);
BitmapData data = Src.LockBits(new Rectangle(0, 0, Src.Width, Src.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
Dest.Lock();
// Copy the bitmap's data directly to the on-screen buffers
CopyMemory(Dest.BackBuffer, data.Scan0, (data.Stride * data.Height));
// Moves the back buffer to the front.
Dest.AddDirtyRect(new Int32Rect(0, 0, data.Width, data.Height));
Dest.Unlock();
//
Src.UnlockBits(data);
}
[DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory")]
public static extern void CopyMemory(IntPtr Destination, IntPtr Source, int Length);
}
...以及我的称呼方式:
/// <summary>
/// BitmapCaptured event handler for when a new video frame is received from the IP source
/// </summary>
/// <param name="sender">The originating source of the event</param>
/// <param name="NewBitmap">The new frame in Bitmap form</param>
private void PipeLine_BitmapCaptured(object sender, Bitmap NewBitmap)
{
// render to the screen
Dispatcher.Invoke(() =>
{
Utils.InjectBitmap(NewBitmap, VM.FrameDataNew);
});
}
图像不再出现在屏幕上。单步执行看起来每次 InjectBitmap() 被调用时目标 WriteableBitmap 为 null 的代码?该代码在第一个 if 语句中创建了一个新的,但 VM.FrameDataNew 仍然为空?
我在这方面的经验绝对处于边缘,因此非常感谢任何帮助。
编辑:目标是拥有一个可观察的 WriteableBitmaps 集合,这样我就可以有效地处理其中的几个。
原因很简单:你没有将新创建的对象赋值给FrameDataNew
属性,所以它的值仍然是null
.
不要忘记在 C# 中,引用类型实例是通过引用传递的。
所以在您的方法中,您正在执行以下操作:
void InjectBitmap(Bitmap Src, WriteableBitmap Dest)
{
if (Dest == null)
Dest = new WriteableBitmap(/* ... */);
}
但是 Dest
只是方法内部的局部变量 - 方法的参数可以看作是方法的局部变量。
所以你将一个新创建的实例分配给一个局部变量,这个局部变量当然会在方法 returns.
时丢失
这里需要的是一个ref
参数:
void InjectBitmap(Bitmap src, ref WriteableBitmap dest)
{
if (dest == null)
dest = new WriteableBitmap(/* ... */);
}
请注意,我更改了参数名称大小写以匹配 C# 风格指南。
但是,这不适用于属性,因此如果 FrameDataNew
是 属性,InjectBitmap(NewBitmap, VM.FrameDataNew)
将无法编译。
您可以将 FrameDataNew
设为一个字段,但是拥有一个非私有的可变字段是个坏主意。
您可以使用临时局部变量,但它有点丑陋:
var copy = VM.FrameDataNew;
InjectBitmap(NewBitmap, ref copy);
if (copy != VM.FrameDataNew)
VM.FrameDataNew = copy;
我会重新考虑您的设计,这样您就不需要那么多东西了。
顺便说一句,在多线程环境中多次调用 Dispatcher.Invoke
(特别是如果调用的方法很慢,而你的方法很慢)会降低应用程序的性能并使 UI 无响应。
我有一个方法可以更新 WriteableBitmap (FrameDataNew) 的内容,它是我的视图模型 (VM) 中的 属性:
public WriteableBitmap FrameDataNew
{
get { return frameDataNew; }
set
{
frameDataNew = value;
OnProptertyChanged("FrameDataNew");
}
}
private WriteableBitmap frameDataNew = null;
当我收到来自 gstreamer 的新位图时 class 我已经写过更新 FrameDataNew 以便在屏幕上显示最新的帧。 window 是一个简单的图像控件,其源绑定到 FrameDataNew。
以下代码在我的事件处理程序中可以很好地执行此操作:
/// <summary>
/// BitmapCaptured event handler for when a new video frame is received from the IP source
/// </summary>
/// <param name="sender">The originating source of the event</param>
/// <param name="NewBitmap">The new frame in Bitmap form</param>
private void PipeLine_BitmapCaptured(object sender, Bitmap NewBitmap)
{
// render to the screen
Dispatcher.Invoke(() =>
{
// check the existence of the WriteableBitmap and also the dimensions, create a new one if required
if ((VM.FrameDataNew == null) || (VM.FrameDataNew.Width != NewBitmap.Width) || (VM.FrameDataNew.Height != NewBitmap.Height))
VM.FrameDataNew = new WriteableBitmap(NewBitmap.Width, NewBitmap.Height, NewBitmap.HorizontalResolution, NewBitmap.VerticalResolution, PixelFormats.Bgr24, null);
// lock the bitmap data so we can use it
BitmapData data = NewBitmap.LockBits(new Rectangle(0, 0, NewBitmap.Width, NewBitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
// lock the backbuffer of the WritebaleBitmap so we can modify it
VM.FrameDataNew.Lock();
// Copy the bitmap's data directly to the on-screen buffers
CopyMemory(VM.FrameDataNew.BackBuffer, data.Scan0, (data.Stride * data.Height));
// Moves the back buffer to the front.
VM.FrameDataNew.AddDirtyRect(new Int32Rect(0, 0, data.Width, data.Height));
// unlock the back buffer again
VM.FrameDataNew.Unlock();
//
//NewBitmap.UnlockBits(data);
});
}
[DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory")]
public static extern void CopyMemory(IntPtr Destination, IntPtr Source, int Length);
现在我想更新我的程序以处理多个管道和 WriteableBitmap,这样我就可以显示多个视频源。我做的第一件事是创建一个静态实用程序 class,这样我就可以传入我希望更新的新位图 (NewBitmap) 和 WriteableBitmap (VM.FrameDataNew)。然后我在新帧到达时根据需要调用:
实用程序class(只是与我的问题有关的代码):
public static class Utils
{
/// <summary>
/// Inject the source Bitmap into the Destination WriteableBitmap
/// </summary>
/// <param name="Src">The source Bitmap</param>
/// <param name="Dest">The destination WriteableBitmap</param>
public static void InjectBitmap(Bitmap Src, WriteableBitmap Dest)
{
if ((Dest == null) || (Dest.Width != Src.Width) || (Dest.Height != Src.Height))
Dest = new WriteableBitmap(Src.Width, Src.Height, Src.HorizontalResolution, Src.VerticalResolution, PixelFormats.Bgr24, null);
BitmapData data = Src.LockBits(new Rectangle(0, 0, Src.Width, Src.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
Dest.Lock();
// Copy the bitmap's data directly to the on-screen buffers
CopyMemory(Dest.BackBuffer, data.Scan0, (data.Stride * data.Height));
// Moves the back buffer to the front.
Dest.AddDirtyRect(new Int32Rect(0, 0, data.Width, data.Height));
Dest.Unlock();
//
Src.UnlockBits(data);
}
[DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory")]
public static extern void CopyMemory(IntPtr Destination, IntPtr Source, int Length);
}
...以及我的称呼方式:
/// <summary>
/// BitmapCaptured event handler for when a new video frame is received from the IP source
/// </summary>
/// <param name="sender">The originating source of the event</param>
/// <param name="NewBitmap">The new frame in Bitmap form</param>
private void PipeLine_BitmapCaptured(object sender, Bitmap NewBitmap)
{
// render to the screen
Dispatcher.Invoke(() =>
{
Utils.InjectBitmap(NewBitmap, VM.FrameDataNew);
});
}
图像不再出现在屏幕上。单步执行看起来每次 InjectBitmap() 被调用时目标 WriteableBitmap 为 null 的代码?该代码在第一个 if 语句中创建了一个新的,但 VM.FrameDataNew 仍然为空?
我在这方面的经验绝对处于边缘,因此非常感谢任何帮助。
编辑:目标是拥有一个可观察的 WriteableBitmaps 集合,这样我就可以有效地处理其中的几个。
原因很简单:你没有将新创建的对象赋值给FrameDataNew
属性,所以它的值仍然是null
.
不要忘记在 C# 中,引用类型实例是通过引用传递的。
所以在您的方法中,您正在执行以下操作:
void InjectBitmap(Bitmap Src, WriteableBitmap Dest)
{
if (Dest == null)
Dest = new WriteableBitmap(/* ... */);
}
但是 Dest
只是方法内部的局部变量 - 方法的参数可以看作是方法的局部变量。
所以你将一个新创建的实例分配给一个局部变量,这个局部变量当然会在方法 returns.
时丢失这里需要的是一个ref
参数:
void InjectBitmap(Bitmap src, ref WriteableBitmap dest)
{
if (dest == null)
dest = new WriteableBitmap(/* ... */);
}
请注意,我更改了参数名称大小写以匹配 C# 风格指南。
但是,这不适用于属性,因此如果 FrameDataNew
是 属性,InjectBitmap(NewBitmap, VM.FrameDataNew)
将无法编译。
您可以将 FrameDataNew
设为一个字段,但是拥有一个非私有的可变字段是个坏主意。
您可以使用临时局部变量,但它有点丑陋:
var copy = VM.FrameDataNew;
InjectBitmap(NewBitmap, ref copy);
if (copy != VM.FrameDataNew)
VM.FrameDataNew = copy;
我会重新考虑您的设计,这样您就不需要那么多东西了。
顺便说一句,在多线程环境中多次调用 Dispatcher.Invoke
(特别是如果调用的方法很慢,而你的方法很慢)会降低应用程序的性能并使 UI 无响应。