c# - 寻找有关如何在 MVVM C#​​ Xamarin 中实现 "volume"属性的建议

标签 c# mvvm xamarin

所以我正在开发一个可以调整(除其他外)设备音量的应用程序。所以我开始的是一个非常简单的模型,它实现了 INotifyPropertyChanged。据我所知,在如此简单的场景中不需要 ViewModel。设置音量属性时调用 INPC,Model 生成 TCP 消息告诉设备改变音量。

然而,这就是它变得复杂的地方。音量不必由应用程序更改,也可以直接在设备上更改,甚至可以通过另一部装有该应用程序的手机进行更改。从设备获取这些更改的唯一方法是定期轮询它。

所以我认为合理的是稍微改变结构。所以现在我有一个代表实际设备的 DeviceModel。我添加了一个 VolumeViewModel。 DeviceModel 类现在处理生成 TCP 消息。它还定期轮询设备。但是,假设 DeviceModel 发现音量发生了变化。这应该如何传播回 VolumeViewModel 以便所有更改都是从 UI 和实际设备双向进行的?如果我将 INPC 放在 DeviceModel 中,我的 VolumeViewModel 似乎就变得多余了。也许对于这个简单的人为示例很好,但可以说该设备比仅 1 卷更复杂。我在想 VM 可以包含对模型的引用,而卷属性可能只是对 DeviceModel 中的卷的引用,但它仍然不能真正解决我的问题。

如果 DeviceModel 卷发生更改,则引用不会更改,因此在我看来,这不会触发 VolumeViewModel 中卷属性的 setter 函数。当轮询看到不同的卷时,我是否让 ViewModel 将事件处理程序注入(inject)到要调用的模型中?我是否在两者中都使用 INPC(以这种方式实现它会是什么样子?)

最佳答案

设定方向明确。你想明确地得到它。所以我们需要类似的东西

class MyDeviceService : IDeviceService
{
    public async Task SetVolumeAsync(int volume) { }
    public async Task<int> GetVolumeAsync() { }
}

// ViewModel
class DeviceViewModel : INotifyPropertyChanged
{
    public int Volume { get{ ... } set { ... } }
    public DeviceViewModel(IDeviceService service) { ... }
}

对于更新,您有不同的选择:

回电

亲:
  • 易于实现

  • 骗子:
  • 只有一个订阅者
  • 看起来像是一个糟糕的事件实现(在我们的场景中)

  • class MyDeviceService
    {
        public Action<int> VolumeChangedCallback { get; set; }
        public async Task SetVolumeAsync(int volume) { }
        public async Task<int> GetVolumeAsync() { }
    
        // producer
        VolumeChangedCallback(newVolume);
    }
    
    // consumer
    myDeviceService.VolumeChangedCallback = v => Volume = v;
    
    // deregistration
    myDeviceService.VolumeChangedCallback = null;
    

    事件

    亲:
  • 语言功能(内置)
  • 多个订阅者

  • 骗子:
  • ???

  • class MyDeviceService
    {
        public event EventHandler<VolumeChangedEventArgs> VolumeChanged;
        public async Task SetVolumeAsync(int volume) { }
        public async Task<int> GetVolumeAsync() { }
    
        // producer
        VolumeChanged(new VolumeChangedEventArgs(newVolume));
    }
    
    // consumer
    MessagingCenter.Subscribe<MyDeviceService, int>(this, 
         MyDeviceService.VolumeMessageKey, newVolume => Volume = newVolume);
    
    // needs deregistration
    MessagingCenter.Unsubscribe<MyDeviceService, int>(this, 
        MyDeviceService.VolumeMessageKey, newVolume => Volume = newVolume);
    

    留言

    亲:
  • 轻松订阅/取消订阅
  • 多个订阅者
  • 多个发件人
  • 接收方不需要知道发送方

  • 骗子:
  • 需要外部库(但包含在 Xamarin.Forms、MvvMCross、其他 MvvM 框架中)

  • class MyDeviceService
    {
        public static string VolumeMessageKey = "Volume";
        public async Task SetVolumeAsync(int volume) { }
        public async Task<int> GetVolumeAsync() { }
    
        // producer
        MessagingCenter.Send<MyDeviceService, int>(this, 
            VolumeMessageKey, newVolume);
    }
    
    // consumer
    MessagingCenter.Subscribe<MyDeviceService, int>(this, 
         MyDeviceService.VolumeMessageKey, newVolume => Volume = newVolume);
    
    // needs deregistration
    MessagingCenter.Unsubscribe<MyDeviceService, int>(this, 
        MyDeviceService.VolumeMessageKey, newVolume => Volume = newVolume);
    

    可观察

    使用 Reactive extensions总是很好,如果你有事件流。

    亲:
  • 轻松订阅/取消订阅
  • 多个订阅者
  • 可过滤,如 IEnumerable (例如 Where(volume => volume > 10) )

  • 骗子:
  • 仅用于一种情况的外部库
  • 全新方法带来的高学习努力

  • class MyDeviceService
    {
        IObservable<int> VolumeUpdates { get; }
        public async Task SetVolumeAsync(int volume) { }
        public async Task<int> GetVolumeAsync() { }
    }
    
    // consumer
    _volumeSubscription = myDeviceService.VolumeUpdates
                              .Subscribe(newVolume => Volume = newVolume);
    
    // deregistration
    // - implicitly, if object gets thrown away (but not deterministic because of GC)
    // - explicitly:
    _volumeSubscription.Dispose();
    

    结论

    我在模型中省略了 INPC,因为那是事件,但更糟糕的是,因为您必须比较属性名称。
    如果您查看这些示例,您会发现,它们只是订阅和取消订阅的方式不同。主要区别在于它们提供的灵 active 。就个人而言,我会选择响应式扩展;)但事件和消息传递也很好。因此,请选择您和您的团队成员最了解的方法。你只需要记住:

    总是注销! ^^

    关于c# - 寻找有关如何在 MVVM C#​​ Xamarin 中实现 "volume"属性的建议,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36584825/

    相关文章:

    ios - 发出本地警报通知

    c# - 如何保存并在以后恢复线程执行状态?

    c# - C# 中的 ">>"运算符有什么作用?

    c# - 如何对绑定(bind)到 ListBoxControl 的 ObservableCollection<T> 进行排序?

    c# - 如何在配置中指定颜色

    c# - Datagrid列可以在不同的行中包含不同类型的控件吗

    xamarin - 如何将 View 模型属性绑定(bind)到 XAML 控件

    c# - 数据网格未正确更新,滚动时更新

    Xamarin Android Profiler 无法启动 — "Deploy your app again with a newer version of Xamarin Studio"

    c# - 无法从内容页面执行 PopAsync、PopModalAsync 和 PopToRootAsync