寻求关于如何实施“a”的建议;卷;MVVM C#Xamarin中的属性

寻求关于如何实施“a”的建议;卷;MVVM C#Xamarin中的属性,c#,mvvm,xamarin,C#,Mvvm,Xamarin,因此,我正在开发一个应用程序,可以调整(除其他外)设备的音量。所以我从一个非常简单的模型开始,它实现了INotifyPropertyChanged。据我所知,在这样一个简单的场景中不需要ViewModel。当设置了volume属性时,会调用INPC,并且模型会生成一条TCP消息,通知设备更改卷 然而,这正是它变得复杂的地方。应用程序不必更改音量,也可以直接在设备上更改音量,甚至可以通过使用应用程序的另一部手机更改音量。从设备获取这些更改的唯一方法是定期轮询它 所以我认为合理的做法是稍微改变一下结

因此,我正在开发一个应用程序,可以调整(除其他外)设备的音量。所以我从一个非常简单的模型开始,它实现了INotifyPropertyChanged。据我所知,在这样一个简单的场景中不需要ViewModel。当设置了volume属性时,会调用INPC,并且模型会生成一条TCP消息,通知设备更改卷

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

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


如果DeviceModel卷发生更改,则引用不会更改,因此在我看来,这不会触发VolumeViewModel中volume属性的setter函数。当轮询看到不同的卷时,是否让ViewModel将事件处理程序注入要调用的模型?我是否在这两个方面都使用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) { ... }
}
结论 我在模型中遗漏了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) { ... }
}
结论 我在模型中遗漏了INPC,因为这是事件,但更糟糕的是,因为您必须比较属性名称。 如果你看一下这些例子,你会发现它们只是在订阅和取消订阅的方式上有所不同。主要区别在于它们提供的灵活性。就个人而言,我会选择被动扩展;)但活动和信息也很好。因此,选择您和您的团队成员最了解的方法。你只要记住:


总是注销^^ 我假设您打算向用户显示一个显示当前卷的UI(例如滑块小部件)。因此,您真正面临的挑战是,任何操纵滑块的尝试都无法立即得到确认——设备可能需要一段时间才能响应,一旦响应,甚至可能无法接受请求(或者可能被本地操纵所覆盖)。然而,您仍然需要向移动应用程序用户显示他们的请求正在处理中,否则他们会认为它出现了故障

我也不得不在一个应用程序中解决这个问题——尽管我的例子要复杂得多。我的应用程序用于控制灌溉管理硬件的大型安装,有许多设备(固件版本不同,远程控制能力也不同)。但最终问题还是一样的。我用标准MVVM解决了这个问题

对于每个设备,创建一个viewmodel,跟踪两个不同的值:硬件的实际最后已知(报告)状态和应用程序最近可能请求的任何“待定”值。通过标准INPC绑定将可视控件绑定到“挂起”值。在这些值的设置器中,如果新值与上次已知的硬件状态不同,则会触发对设备的异步请求,以转换到所需状态。在剩下的时间里,您只需使用任何对您有意义的机制来轮询设备状态(推送通知可能更好,但在我的情况下,我使用的基础设施只能支持活动轮询)。您将使用新的硬件状态值以及挂起的值进行更新(除非其他值已经挂起)

在应用程序UI中,您可能希望显示实际的硬件状态值以及允许用户操作的“挂起”值。对于滑块,您可能希望实现反映报告的硬件值(只读)的“幻影”滑块拇指。对于交换机,您可能希望禁用它们,直到硬件报告与挂起值相同的值。任何对你的应用程序设计语言有意义的东西

这就留下了如何处理硬件不(或不能)尊重请求的情况的最后一个边缘案例。当设备只能增加到10时,用户可能会尝试将音量增加到11。或者可能有人按下设备上的物理按钮使其静音。或者可能是其他人在运行移动应用程序,并与你争夺对它的控制权。在任何情况下,都可以通过为挂起的操作建立最大等待超时来轻松解决此问题。例如,10秒后未满足的任何卷更改请求都被假定为已被抢占,UI将通过设置挂起值=上次报告的值来停止等待

安尼