【问题标题】:MVVM ViewModel inside ViewModelViewModel 中的 MVVM ViewModel
【发布时间】: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


【解决方案1】:

你可以将ProductSerialCodes定义为ObservableCollection来绑定View。

public ObservableCollection<string> ProductSerialCodes
    { get { return _productSerialCodes; } }

另外,要更新 ObservableCollection(因为它是在后台线程而不是主线程中更新的),您应该使用 Dispatcher 对象。

Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
            {
              <<Your code>>
            }));

【讨论】:

    【解决方案2】:

    -我的方法正确吗?我乐于接受新想法。

    如果ProductList在运行时没有被修改,我认为最好声明为List,并将ProductSerialCodes设置为ObservableCollection。

    -如何将 ListBox 或其他控件绑定到另一个 ViewModel 内的 ViewModel 中的属性?

    通常,ViewModel 的父子(子作为集合)类型非常适合任何 ItemsControl 派生控件,例如 ListBox。您只需要将 ItemSource 绑定到子项并拥有项目的模板。子 ViewModel 将绑定到 Item 的视图。

    -如何将 OnPropertyChanged 编组到 UI 线程,并且无需单击窗口即可应用更新?

    我同意user1672994,Dispatcher可以做到。

    【讨论】:

    • (1) ProductList 可以在运行时修改,如果需要,可以创建一个新的 ProductViewModel 并添加到列表中。 (2) 我在将 Listbox 绑定到 Container.ProductList 集合时遇到问题,我添加了一个 xaml sn-p。 (3) 我同意他的看法。感谢您的宝贵时间。
    • 应用程序运行时没有错误。或者,它们是否被扔进了特定的窗口?我怎样才能看到这些错误?我正在使用 VS2015 社区。当我运行它时,中间窗口或输出上没有任何内容。
    • 您的代码应该可以工作。剩下的唯一事情是确保 Window 设置了 Datacontext。你要么窥探它,要么处理 DataContextChanged 来检查它。
    • datacontext是在xaml中建立的,我在xaml中添加了那个sn-p。然后,我把它移到了视图后面的代码中,什么也没有。此外,在代码隐藏中,我创建了一个全局 MainViewModel 实例,并将其设置在 Initialize() 中,并且在 ListBox(我在 xaml 中放置了一个 x:name)中,将 datacontext 设置为 Loaded( ) 代码隐藏事件,什么也没发生。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多