【发布时间】:2015-10-24 07:13:12
【问题描述】:
我正在尝试将现有的 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; }
}
这样,如果我们有 10 个相同 UPC 的产品,我们就有一个 Product 类实例,其中 ProductSerialCodes 列表中有 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 为空,因此,此解决方案不起作用。
我已经阅读了 Josh Smith 的 article 和 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>
- 注意:数据模板会有所不同,但是,我想要的是查看列表框中的项目。
当我在后台运行应用程序时,正在填充 ViewModel。如果我设置断点,我可以看到可观察列表中的项目。
但屏幕上什么都没有,ListBox 仍然是空的
感谢您的宝贵时间。
问候。
【问题讨论】:
-
我写了一篇关于 wpf 中 mvvm 最佳实践的文章,也许你也很感兴趣:blog.rsuter.com/…
标签: c# wpf multithreading mvvm data-binding