c# - MVVM:绑定可变模型
问题描述
我有某种定期执行的后台任务,或者如果有人手动启动它。
现在我想要某种进度/结果视图,它显示处理的数据。该窗口应始终显示。
问题是,每次后台任务启动时都会创建一个新的数据模型实例。那么即使模型被重新实例化,如何保持模型->视图模型的绑定呢?
我创建了一些非常基本的示例作为展示:
看法:
<Window x:Class="View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:View"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="300" Background="Black">
<Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" Foreground="White" HorizontalAlignment="Center" Content="{Binding MainModelText}"/>
</Grid>
</Grid>
视图模型:
public class ViewModel : INotifyPropertyChanged
{
MainModel _MainModel;
string _MainModelText;
public string MainModelText
{
get { return this._MainModelText; }
set
{
this._MainModelText = value;
OnNotifyPropertyChanged("MainModelText");
}
}
public ViewModel(MainModel mainModel)
{
this._MainModel = mainModel;
this._MainModel.PropertyChanged += _MainModel_PropertyChanged;
}
private void _MainModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(string.Equals(e.PropertyName, "SomeText"))
{
this.MainModelText = _MainModel.SomeText + new Random().Next(1000);
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnNotifyPropertyChanged(string propName)
{
if(this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
模型:
public class MainModel : INotifyPropertyChanged
{
string _SomeText;
public string SomeText
{
get { return this._SomeText; }
set
{
this._SomeText = value;
OnNotifyPropertyChanged("SomeText");
}
}
public MainModel()
{
this.SomeText = "Its MainModel!";
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnNotifyPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
商业逻辑
public class Logic
{
MainModel _MainModel;
View.MainWindow _Window;
public Logic()
{
this._MainModel = new MainModel();
_Window = new View.MainWindow(new ViewModel(_MainModel));
}
public void Start()
{
_Window.ShowDialog();
}
public void NewAll()
{
this._MainModel = new MainModel();
//working...
this._MainModel.SomeText = "Finished";
}
}
所以显然“完成”没有显示在窗口上,因为它设置为另一个 MainModel 实例。
那么如何更新 ViewModel 中的 Modelreference 呢?什么是这样的最佳实践?
编辑:
public class Logic
{
MainModel _MainModel;
ViewModel _ViewModel;
View.MainWindow _Window;
public Logic()
{
this._MainModel = new MainModel();
this._ViewModel = new ViewModel(this._MainModel);
_Window = new View.MainWindow(this._ViewModel);
}
public void Start()
{
_Window.ShowDialog();
}
public void NewAll()
{
this._MainModel = new MainModel();
this._ViewModel.Reload(this._MainModel);
//working...
this._MainModel.SomeText = "Finished";
}
}
在虚拟机中添加:
internal void Reload(MainModel mainModel)
{
this._MainModel = mainModel;
this._MainModel.PropertyChanged -= _MainModel_PropertyChanged;
this._MainModel.PropertyChanged += _MainModel_PropertyChanged;
}
解决方案
除非您有充分的理由不通过 VM 属性提供 MainModel,否则我建议您这样做。然后您可以简单地跳过 SomeText 属性并直接绑定到 Model.SomeText,因为您的模型已经实现了 INotifyPropertyChanged。
如果你想加载一个全新的模型实例,你可以直接在 VM 属性上设置它。
如果您不想为视图提供完整的模型,那么您的方法就可以了。但我也会从模型构造函数中调用 ReloadModel,以便将代码放在一个地方。此外,您应该在将旧模型更新为新模型之后从旧模型中取消注册 PropertyChanged 事件(首先检查 _mainModel == null)。
在您的 PropertyChanged 调用中,考虑使用 nameof(SomeProperty) 而不是像现在这样的固定字符串“SomeProperty”。如果您在某个时候重命名该属性,则代码不会中断。
看看 MVVM Light,该框架将使您免于一遍又一遍地编写一些样板代码,并且它们提供的 Messenger 是从模型到 VM 发出信号的好方法。它将这个示例简化为几行。
推荐阅读
- apache-spark - 如何使用 Spark runner 运行 Cloud Dataflow 管道?
- jwt - 如何使用 OpenID Connect 进行身份验证,使用 JWT 进行其他所有操作
- java - 如何根据java中文件和子文件夹的更改查找文件夹的“最后修改时间”
- php - ajax成功响应后如何在文本框中显示列表
- activemq - ActiveMQ 的多个实例开始导致问题
- java - 四个不同的 wsdl2java 应用程序无法导入 wsdl,但 SoapUI 可以
- javascript - Jquery - 根据返回的 ajax 值从选择选项中选择突出显示
- node.js - nodejs mongoose,回调,比较旧数据并将新数据存储到变量
- meteor - 我可以在 Blaze 模板实例中使用 querySelector 方法吗
- laravel - vue 页面不渲染更改