C # передача свойства viewmodel в диспетчер - PullRequest
0 голосов
/ 24 мая 2018

У меня есть метод, который обновляет содержимое WriteableBitmap (FrameDataNew), которое является свойством в моей viewmodel (VM):

    public WriteableBitmap FrameDataNew
    {
        get { return frameDataNew; }
        set
        {
            frameDataNew = value;
            OnProptertyChanged("FrameDataNew");
        }
    }
    private WriteableBitmap frameDataNew = null; 

Когда я получаю новое растровое изображение из класса gstreamer, который я написалЯ обновляю FrameDataNew, чтобы показать последний кадр на экране.окно представляет собой простой элемент управления Image, источник которого привязан к 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);

Теперь я хочу обновить свою программудля обработки нескольких конвейеров и WriteableBitmaps, чтобы я мог показать несколько видео каналов.Первым делом я создал класс статических утилит, чтобы я мог передать новое растровое изображение (NewBitmap) и WriteableBitmap (VM.FrameDataNew), которые я хочу обновить.Затем я вызывал его по мере необходимости, когда приходит новый кадр:

Класс утилит (только код, связанный с моим вопросом):

    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, чтобы я мог обрабатывать несколькоэто эффективно.

1 Ответ

0 голосов
/ 24 мая 2018

Причина проста: вы не назначаете вновь созданный объект свойству FrameDataNew, поэтому его значение остается null.

Не забывайте, что в C # экземпляры ссылочного типапередается по ссылке.

Итак, в вашем методе вы делаете следующее:

void InjectBitmap(Bitmap Src, WriteableBitmap Dest)
{
    if (Dest == null)
        Dest = new WriteableBitmap(/* ... */);
}

Но Dest это просто локальная переменная внутри вашего метода - аргументы метода можно увидетькак локальные переменные метода.

Таким образом, вы назначаете только что созданный экземпляр локальной переменной, которая, конечно, будет потеряна при возврате метода.

Здесь вам нужен параметр ref:

void InjectBitmap(Bitmap src, ref WriteableBitmap dest)
{
    if (dest == null)
        dest = new WriteableBitmap(/* ... */);
}

Обратите внимание, что я изменил регистр имен параметров в соответствии с руководством по стилю C #.

Однако это не будет работать для свойств, поэтому InjectBitmap(NewBitmap, VM.FrameDataNew) не будет компилироваться, если FrameDataNew - это свойство.

Вы можете сделать FrameDataNew для поля, но иметь непубличное изменяемое поле - плохая идея.

Вы можете использовать временную локальную переменную,но это как-то некрасиво:

var copy = VM.FrameDataNew;
InjectBitmap(NewBitmap, ref copy);
if (copy != VM.FrameDataNew)
    VM.FrameDataNew = copy;

Я бычернила свой дизайн, так что вам не нужны все эти вещи.

Кстати, несколько вызовов к Dispatcher.Invoke в многопоточной среде (особенно, если вызываемые методы медленны, а ваши есть) будут отброшеныпроизводительность вашего приложения и пользовательский интерфейс не отвечает.

...