MVVM ViewModel inside ViewModel
本文关键字:ViewModel inside MVVM | 更新日期: 2023-09-27 18:18:59
我正试图将现有的Windows窗体软件转换为MVVM/WPF。我是MVVM的新手,所以我可能会犯很多错误。
在这个应用程序中,这些是我的主要对象的简化:
Product类,该类保存UPC中每个不同产品的信息。同样,可以有许多相同UPC的产品,但是每个物理项目都有一个唯一标识符,比如UPC-date-serial,所以这个唯一标识符保存在Product类中的列表中。
class Product
{
// This is the barcode of the product
public UPC
{ get; set; }
// This is a list which contains the unique ID of every product
public List<string> ProductSerialCodes
{ get; set; }
}
这样,如果我们有相同UPC的10个产品,我们就有一个Product类的实例,在productserialdes列表中有10个产品。
然后,Container类,这个类是用来标识每一个装满产品的物理盒子。一个盒子可以装不同的产品。
class Container
{
// The box Id
public string ContainerID
{ get; set; }
// The list of products in the box
public List<Product> ProductList
{ get; set; }
}
这些类有更多的代码,和自我验证的函数,所以我认为最好让他们viewmodel。
ProductViewModel:
class ProductViewModel
{
List<string> _productSerialCodes = new List<string>();
public string UPC
{ get; set; }
public List<string> ProductSerialCodes
{ get { return _productSerialCodes; } }
public int ItemsCount
{ get { return _productSerialCodes.Count; } }
public AddItem(string itemID)
{
_productSerialCodes.Add(itemID);
OnPropertyChanged("ItemsCount");
}
}
*注意:AddItem()方法是从后台线程调用的。
ContainerViewModel:
public class ContainerViewModel
{
private ObservableCollection<ProductViewModel> _productList;
public ObservableCollection<ProductViewModel> ProductList
{ get { return _productList; } }
public string ContainerID
{ get; set; }
public AddItem(string itemID)
{
// This comes from a background thread.
// I find the product related to the item,
// (If the product doesn't exist in the list I create it)
_productList.Find(product).AddItem(itemId);
}
}
所以这是我的MVVM方法,我的MainViewModel有一个ContainerViewModel,在屏幕上我想显示产品列表和数量,示例:
UPC Qty
000000000000 12
由于数据来自后台线程,它必须由INotifyPropertyChanged自动更新。
我注意到的另一件事是,我需要点击窗口中的某个地方来更新UI。在阅读了一些博客之后,我发现使用SynchronizationContext对象可以解决这个问题:
当OnPropertyChanged(propertyName)被调用时,我调用这个SynchronizationContext _syncContext;
_syncContext.Post(delegate { CommandManager.InvalidateRequerySuggested(); }, null);
,但这只适用于当我在UI线程上进行测试。在我的新应用程序中,由于数据来自后台线程,SynchronizationContext为null,因此,此解决方案不起作用。
我已经读了Josh Smith的文章和karl shifflett的Stuff。顺便说一句,这些都很棒。
那么,问题是:
-我的方法正确吗?我愿意接受新想法。
-我如何绑定一个ListBox,或其他控件在另一个ViewModel内的ViewModel属性?
-我怎么能马歇尔OnPropertyChanged到UI线程,更新应用而不需要点击窗口?
——编辑
我的主要问题是显示数据。
如果我的MainViewModel看起来像这样:
public class MainViewModel
{
private ContainerViewModel _currentContainer = new ContainerViewModel();
public ContainerViewModel CurrentContainer
{ get { return _currentContainer; } }
// more code...
}
然后在View中,为了显示ProductList,我的xaml是:
<Window.DataContext>
<ViewModels:MainViewModel />
</Window.DataContext>
<ListBox Grid.Column="0" Margin="5"
ItemsSource="{Binding CurrentContainer.ProductList}">
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel LastChildFill="True">
<TextBlock Text="{Binding UPC}" />
<TextBlock Text="{Binding ItemsCount}" />
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
- 注意:数据模板将是不同的,但是,我想要的是看到列表框中的项目。
当我在后台运行应用程序时,ViewModels正在被填充。如果我设置了一个断点,我可以看到可观察列表中的项。
但是屏幕上什么也没有,ListBox仍然是空的
感谢您的宝贵时间。
问候。
可以将ProductSerialCodes
定义为ObservableCollection来绑定View。
public ObservableCollection<string> ProductSerialCodes
{ get { return _productSerialCodes; } }
同样,要更新ObservableCollection(因为它是在后台线程而不是主线程中更新的),你应该使用Dispatcher对象。
Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
<<Your code>>
}));
-我的方法正确吗?我愿意接受新想法。
如果ProductList在运行时没有被修改,我认为如果你声明它为List并使productserialdes为ObservableCollection会更好。
-我如何绑定一个ListBox,或其他控件在另一个ViewModel内的ViewModel属性?
通常,viewmodel的父-子(子作为集合)类型对于任何ItemsControl派生控件(如ListBox)都是完美的。您只需要将ItemSource绑定到子项,并为这些项创建一个Template。子ViewModel将被绑定到Item的视图。
-我怎么能马歇尔OnPropertyChanged到UI线程,更新应用而不需要点击窗口?
我同意user1672994, Dispatcher可以做到