所以我正在开发一个可以调整(除其他外)设备音量的应用程序。所以我开始的是一个非常简单的模型,它实现了 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);
留言
亲:
骗子:
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/