如何在MVVM C# WPF中实现多线程?[关闭]

pdkcd3nj  于 2023-01-18  发布在  C#
关注(0)|答案(2)|浏览(222)

2天前关闭。
Improve this question
我正在尝试用WPF C#和MVVM设计模式设计一个应用程序。这个应用程序应该从相机提供的dll和cs文件中获取相机源。我应该如何在MVVM模式中实现这个呢?
SDK随摄像头提供,他们在摄像头中使用Dispatcher. invoke()获取摄像头源和其他摄像头功能,但SDK是单窗口STA应用程序。我尝试使用与MVVM相同的技术,但无法从视图模型更新视图中的摄像头源。我尝试将图像画笔的imageSource与视图模型中的位图属性绑定。
xaml代码:

<Grid>

    <Border CornerRadius="10" Margin="10,10,10,10">
        <Border.Background>
            <ImageBrush ImageSource="{Binding ImageSource}"/>
        </Border.Background>
    </Border>

</Grid>

后面的xaml代码:

public HomePage()
    {
        InitializeComponent();

        this.DataContext = new HomePageViewModel();
    }

主视图型号:

public class HomePageViewModel : INotifyPropertyChanged
{

    camera? craam_ = null;
    WriteableBitmap? bmp_ = null;

    WriteableBitmap? imageSource;

    public WriteableBitmap ImageSource
    {
        get { return imageSource; }
        set
        {
            imageSource = value;
            OnPropertyChanged(nameof(ImageSource));

        }
    }


    public HomePageViewModel()
    {
       
        cam_ = camera.Open(camera.EnumV2()[0].id);
        CameraInit();
    }

    void CameraInit()
    {

        int width = 0, height = 0;

        if (cam_ != null)
        {
            if (cam_.get_Size(out width, out height))
            {

                bmp_ = new WriteableBitmap(width, height, 0, 0, PixelFormats.Bgr32, null);
                ImageSource = new WriteableBitmap(width, height, 0, 0, PixelFormats.Bgr32, null);
                cam_.StartPullModeWithCallback(new camera.DelegateEventCallback(DelegateEventCallback));
            }


        }

    }

    private void DelegateEventCallback(camera.eEVENT nEvent)
    {
        Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => { OnEventImage(); }));
    }

    private void OnEventImage()
    {
        if (bmp_ != null)
        {
            camera.FrameInfoV3 info = new camera.FrameInfoV3();
            bool bOK = false;
            try
            {
                bmp_.Lock();
                try
                {
                    bOK = cam_.PullImageV3(bmp_.BackBuffer, 0, 32, bmp_.BackBufferStride, out info); // check the return value
                    bmp_.AddDirtyRect(new Int32Rect(0, 0, bmp_.PixelWidth, bmp_.PixelHeight));
                }
                finally
                {
                    bmp_.Unlock();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }

            if (bOK)
                ImageSource = bmp_;

        }
    }

    public event PropertyChangedEventHandler? PropertyChanged;
    public void OnPropertyChanged(string PropertyName ) {

        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
    
    }
}
toe95027

toe950271#

DelegateEventCallback可以在任何线程上调用,因此此时Dispatcher.CurrentDispatcher不是与UI线程关联的调度程序。
因此,最好在构造函数中初始化一个字段(在本例中,在UI线程上调用):

private Dispatcher _dispatcher;

public HomePageViewModel()
{
    _dispatcher = Dispatcher.CurrentDispatcher;
    cam_ = camera.Open(camera.EnumV2()[0].id);
    CameraInit();
}

然后使用这个:

private void DelegateEventCallback(camera.eEVENT nEvent)
{
    _dispatcher.BeginInvoke(new Action(() => { OnEventImage(); }));
}
  • 无关:最好从OnEventImage代码中删除MessageBox。如果出现问题,将为每一帧调用MessageBox;可能看起来很滑稽。*
7d7tgy0s

7d7tgy0s2#

我没有这样的摄像头,也不知道这些变量是什么,但是我知道可写位图和线程。
编辑:
看起来你眼前的问题的一个因素可能是由调度器队列的工作方式引起的。用户界面所做的一切都是由这个队列处理的,它不是一个FIFO队列,它是一个优先级队列。
当您通知属性已更改时,从视图模型到视图所需的数据传输由添加到调度程序队列的项驱动。
一旦数据传输,它将使一些UI无效,并且UI将需要重新呈现。它将一个项目添加到调度程序队列。
该渲染项目的优先级低于数据传输。
如果你在一个紧密的循环中触发属性改变,那么UI线程将处理一个,返回到队列并发现另一个数据传输位于队列顶部。它将处理该数据传输。依此类推。重新呈现的项目永远不会到达队列顶部,直到你暂停所有内容。
另一个因素可能是在UI线程上运行一些代码。
这可能也是在消耗周期。在UI线程上进行昂贵的处理在这里是不好的。
如果你真的只是想显示一个视频,我怀疑MVVM是增加任何东西,但开销在这里。
也许这将是简单得多,只是去与工作代码,这是纯粹的用户界面为基础的和工程。
如果你想要mvvm不管什么原因。
我认为您可能必须确保尽可能多的处理确实发生在后台线程上。
可写位图是一个可冻结的位图,一旦你构建了你的图片,你应该在它上面调用. Freeze()。这使得内存使用更有效,并允许你把它从后台线程传递到UI线程。
我自己没有尝试过,但是如果你尝试重新创建每一帧,bitmapsource.create可能比可写位图更值得探索。
然后你可以运行你的代码,在一个循环或事件处理程序的后台线程中抓取那个帧。也就是说,假设不管相机是什么东西,都没有线程关联。
具体如何做到这一点取决于相机软件,当然我们不知道它是什么。
调度程序本身就是一个队列,但如果性能较差,则可以考虑使用阻塞队列。运行多个图像处理线程,这些线程会冻结,然后将其输出位图源添加到阻塞队列。在UI线程上订阅该队列并设置属性。
除非你要创建额外的sta线程(通常是个坏主意),否则我认为application. current. dispatcher是你唯一的调度程序。一旦你的Wpf应用程序启动,你可以从任何地方引用它,而不需要引用UI。

相关问题