使用 MVVM,如何在低级服务和视图模型之间建立通信线路?

Posted

技术标签:

【中文标题】使用 MVVM,如何在低级服务和视图模型之间建立通信线路?【英文标题】:Using MVVM, how can I establish a line of communication between a low level service and view model? 【发布时间】:2018-07-17 06:01:40 【问题描述】:

我正在使用 Prism 和 Unity 实现具有蓝牙连接的媒体播放器应用程序。

我正在使用的应用程序流程如下:

    用户在远程设备(手机/平板电脑)上发出命令 桌面应用程序通过蓝牙服务接收Play 命令 更高级别的服务处理元数据并告诉VideoPlaybackViewModel 开始播放

到目前为止我所拥有的

蓝牙服务还没有实现,因为我想先完成其他元素。当谈到 是时候这样做了,我会按照这个例子(https://github.com/saramgsilva/BluetoothSampleUsing32feet.Net)。

按照这个问题(MVVM pattern violation: MediaElement.Play()), 我已经实现了VideoPlaybackViewVideoPlaybackViewModel

VideoPlaybackView:

    <UserControl x:Class="Views.VideoPlaybackView"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:ia="http://schemas.microsoft.com/expression/2010/interactivity"
                 xmlns:prism="http://prismlibrary.com/"
                 prism:ViewModelLocator.AutoWireViewModel="True"
                 x:Name="MediaService">
        <ia:Interaction.Triggers>
            <ia:EventTrigger EventName="Loaded">
                <ia:InvokeCommandAction Command="Binding LoadedCommand" CommandParameter="Binding ElementName=MediaService" />
            </ia:EventTrigger>
        </ia:Interaction.Triggers>
        <Grid>
            <MediaElement 
                x:Name="VideoPlayer"
                Source="Binding VideoSource" />
        </Grid>
    </UserControl>

VideoPlaybackViewModel:

    public class VideoPlaybackViewModel : BindableBase 
        private Uri _videoSource;

        public IMediaService MediaService  get; private set; 

        public Uri VideoSource 
            get => _videoSource;
            set => SetProperty(ref _videoSource, value);
        

        private DelegateCommand<IMediaService> _loadedCommand;

        public DelegateCommand<IMediaService> LoadedCommand 
            get 
                if (_loadedCommand == null) 
                    _loadedCommand =
                        new DelegateCommand<IMediaService>((mediaService) =>  MediaService = mediaService; );
                
                return _loadedCommand;
            
        
    

这些在加载VideoPlaybackModule 时被初始化:

    public class VideoPlaybackModule : IModule 
        private IUnityContainer _container;
        private IRegionManager _regionManager;

        public VideoPlaybackModule(IUnityContainer container, IRegionManager regionManager) 
            _container = container;
            _regionManager = regionManager;
        

        public void Initialize() 
            _regionManager.RegisterViewWithRegion("MainRegion", typeof(VideoPlaybackView));
        
    

我使用模块是因为我想学习它们。

目标

我想要的是有一个可以从蓝牙服务接收事件的控制器, 解析元数据,更新MediaElement.Source,并以某种方式向VideoPlayerViewModel 发送命令 实际播放视频。

尝试

我已经看到了实现控制器的想法,但我不确定我应该如何初始化它。我上来 有以下问题: - 如何连接控制器以响应来自蓝牙服务的蓝牙命令? - 控制器是否应该保留对VideoPlaybackViewModel 的引用以执行命令?

我认为这里也可以应用一项服务。例如,如果我创建了一个VideoPlaybackService,该服务将如何使用?类似于控制器的想法,它需要在向VideoPlayerViewModel发送命令之前处理元数据的处理。

如何使用 Prism 和 Unity 来实现这种模式?

【问题讨论】:

【参考方案1】:

有很多方法可以做到这一点,但似乎 Mediator Pattern 可能是您正在寻找的机器人。这将有助于减少您担心的耦合

Mediator pattern

使用中介者模式,对象之间的通信是 封装在中介对象中。对象不再通信 直接相互交流,而是通过 调解人。这减少了通信对象之间的依赖关系, 从而减少耦合。

Coupling

在软件工程中,耦合是相互依赖的程度 软件模块之间;衡量两个人的联系程度 例程或模块是;之间关系的强度 模块。

在类似 MVVM Light 中,您可以使用 MVVM Light Messenger。

它有点像Pub/Sub 事件,当您注册/订阅消息时,您的解耦类可以发布/发送 说的消息。

不过,既然您提到您使用的是Prism,您可以使用EventAggregator。这又是一种 Pub/Sub 安排。

从您的服务发送事件的示例

this.eventAggregator  
    .GetEvent<PubSubEvent<BlueToothData>>()  
    .Publish(new BlueToothData  SomeData = someData );

ViewModel 中的接收和事件示例

var subscriptionToken = this.eventAggregator                             
                            .GetEvent<PubSubEvent<BlueToothData>>()                          
                            .Subscribe((details) =>  
                                          
                                       // what ever you want to do here;        
                                   );  

注意:我不使用 Prism,但是 EventAggregator 有大量可用资源

其他资源

Prism Event Aggregator in WPF With MVVM

Using the Event Aggregator Pattern to Communicate

【讨论】:

感谢您的回答。考虑到我的问题,我认为这是最好的方法。我想知道随着应用程序复杂性的增加,这是否仍然可以使用。我会把这个想法留到以后。

以上是关于使用 MVVM,如何在低级服务和视图模型之间建立通信线路?的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 和 MVVM - 模型和视图模型之间的通信

在 SwiftUI MVVM 中的子视图和父视图之间共享值

如何在以下 mvvm 架构中使用 @Binding Wrapper?

多个视图模型之间状态共享的具体示例(WPF MVVM)

mvc mvp mvvm的区别

如何在MVVM架构中使用RxSwift发送参数来查看模型?