【问题标题】:MVVM WPF Master Detail ComboboxesMVVM WPF 主从组合框
【发布时间】:2012-02-29 10:02:54
【问题描述】:

感谢我之前在 Stack Overflow 上得到的一些建议,我在理解 MVVM 方面取得了良好进展。然而,当事情开始变得更加复杂时,我仍在苦苦挣扎。

我有下面的视图,用于输入订单。它绑定到 OrderScreenViewModel 的 DataContext。

<StackPanel>
    <ComboBox Height="25" Width="100" DisplayMemberPath="CustomerCode" SelectedItem="{Binding Path=Order.Customer}" ItemsSource="{Binding Path=Customers}"></ComboBox>
    <ComboBox Height="25" Width="100" DisplayMemberPath="ProductCode" SelectedItem="{Binding Path=CurrentLine.Product}" ItemsSource="{Binding Path=Products}"></ComboBox>
</StackPanel>

第一个组合框用于选择客户。第二个组合框用于为新的 OrderLine 选择 ProductCode。

有些项目我无法解决如何在 MVVM 中实现:
1) 选择客户后,更新产品组合框,使其项目源仅显示具有与在组合框中选择的 CustomerDto 记录相同的 CustomerId
2) 调用 Load 时,在 Customers 组合框中设置 SelectedItem,以便它显示 CustomerId 等于 OrderDto 上的 CustomerId。
3) 应用,过程与 1) 相同,以便仅显示/加载属于该客户的产品,并在产品组合框中设置 SelectedItem,使其指向带有与 OrderLineDto 中包含的 ProductId 相同

我不确定如何继续,或者即使我的视图模型的职责是否正确。也许它与 NotifyPropertyChanged 有关?任何关于我如何实现上述目标的指示都将不胜感激。我相信如果我做对了,它将对我的应用程序有很大帮助。非常感谢亚历克斯。

 public class OrderScreenViewModel
    {
        public WMSOrderViewModel Order { get; private set; }
        public WMSOrderLineViewModel CurrentLine { get; private set; }

        public OrderScreenViewModel()
        {
            Order = new WMSOrderViewModel();
            CurrentLine = new WMSOrderLineViewModel(new OrderLineDto());
        }

        public void Load(int orderId)
        {
            var orderDto = new OrderDto { CustomerId = 1, Lines = new List<OrderLineDto> { new OrderLineDto{ProductId = 1 }} };
            Order = new WMSOrderViewModel(orderDto);
        }

        public List<CustomerDto> Customers
        {
            get{
                return new List<CustomerDto> { 
                        new CustomerDto{CustomerId=1,CustomerCode="Apple"},
                        new CustomerDto{CustomerId=1,CustomerCode="Microsoft"},
                };
            }
        }

        public List<ProductDto> Products
        {
            get
            {
                return new List<ProductDto> { 
                    new ProductDto{CustomerId=1,ProductId=1,ProductCode="P100",Description="Pepsi"},
                    new ProductDto{CustomerId=1,ProductId=2,ProductCode="P110",Description="Coke"},
                    new ProductDto{CustomerId=2,ProductId=3,ProductCode="P120",Description="Fanta"},
                    new ProductDto{CustomerId=2,ProductId=4,ProductCode="P130",Description="Sprite"}
                };
            }
        }
    public class WMSOrderLineViewModel
    {
        private ProductDto _product;
        private OrderLineDto _orderLineDto;

        public WMSOrderLineViewModel(OrderLineDto orderLineDto)
        {
            _orderLineDto = orderLineDto;
        }

        public ProductDto Product { get { return _product; } 
            set{_product = value; RaisePropertyChanged("Product"); }
    }

    public class WMSOrderViewModel
    {
        private ObservableCollection<WMSOrderLineViewModel> _lines;
        private OrderDto _orderDto;
        public ObservableCollection<WMSOrderLineViewModel> Lines { get { return _lines; } }
        private CustomerDto _customer;

        public CustomerDto Customer { get{return _customer;} set{_customer =value; RaisePropertyChanged("Customer") }

        public WMSOrderViewModel(OrderDto orderDto)
        {
            _orderDto = orderDto;
            _lines = new ObservableCollection<WMSOrderLineViewModel>();
            foreach(var lineDto in orderDto.Lines)
            {
                _lines.Add(new WMSOrderLineViewModel(lineDto));
            }
        }

        public WMSOrderViewModel()
        {
            _lines = new ObservableCollection<WMSOrderLineViewModel>();
        }
    }

【问题讨论】:

    标签: c# wpf mvvm combobox prism


    【解决方案1】:

    您需要让 Products 和 Customers 键入 ObservableCollection。

    当您在视图模型中更改这些可观察集合时,它们将更新视图,因为 OC 已经实现了 INotifyPropertyChanged。

    Order 和 CurrentLine 应该只是一个类型,而不是真正称为 ViewModel。

    1) 当在客户组合框的 SelectedItem 上调用 setter 时,您将不得不这样做。

    2) 您可能需要在 OrderScreenViewModel 的 ctr 中执行此操作,方法是使用您的逻辑来确定要更改 CurrentLine.Customer 的客户。如果您在 ctr 中执行此操作,这将在绑定发生之前设置值。

    3) 同样,只要您更改组合框绑定到的 ObservableCollection,它就会更新 UI。如果您更改 SelectedItem 绑定的内容,请确保调用 RaisedPropertyChanged 事件。

    ETA:将 xaml 更改为此,绑定到 SelectedProduct 和 SelectedCustomer 的 SelectedItem 属性

    <StackPanel>
        <ComboBox Height="25" Width="100" DisplayMemberPath="CustomerCode" SelectedItem="{Binding Path=SelectedCustomer}" ItemsSource="{Binding Path=Customers}"></ComboBox>
        <ComboBox Height="25" Width="100" DisplayMemberPath="ProductCode" SelectedItem="{Binding Path=SelectedProduct}" ItemsSource="{Binding Path=Products}"></ComboBox>
    </StackPanel>
    

    这应该可以让您朝着正确的方向开始,所有用于通过客户 ID 构建客户和产品的逻辑都需要在您的存储库中进行。

       public class OrderScreenViewModel : INotifyPropertyChanged
       {
          private readonly IProductRepository _productRepository;
          private readonly ICustomerRepository _customerRepository;
    
          public OrderScreenViewModel(IProductRepository productRepository,
             ICustomerRepository customerRepository)
          {
             _productRepository = productRepository;
             _customerRepository = customerRepository;
    
             BuildCustomersCollection();
          }
    
          private void BuildCustomersCollection()
          {
             var customers = _customerRepository.GetAll();
             foreach (var customer in customers)
                _customers.Add(customer);
          }
    
          private ObservableCollection<Customer> _customers = new ObservableCollection<Customer>();
          public ObservableCollection<Customer> Customers
          {
             get { return _customers; }
             private set { _customers = value; }
          }
    
          private ObservableCollection<Product> _products = new ObservableCollection<Product>();
          public ObservableCollection<Product> Products
          {
             get { return _products; }
             private set { _products = value; }
          }
    
          private Customer _selectedCustomer;
          public Customer SelectedCustomer
          {
             get { return _selectedCustomer; }
             set
             {
                _selectedCustomer = value;
                PropertyChanged(this, new PropertyChangedEventArgs("SelectedCustomer"));
                BuildProductsCollectionByCustomer();
             }
          }
    
          private Product _selectedProduct;
          public Product SelectedProduct
          {
             get { return _selectedProduct; }
             set
             {
                _selectedProduct = value;
                PropertyChanged(this, new PropertyChangedEventArgs("SelectedProduct"));
                DoSomethingWhenSelectedPropertyIsSet();
             }
          }
    
          private void DoSomethingWhenSelectedPropertyIsSet()
          {
             // elided
          }
    
          private void BuildProductsCollectionByCustomer()
          {
             var productsForCustomer = _productRepository.GetById(_selectedCustomer.Id);
             foreach (var product in Products)
             {
                _products.Add(product);
             }
          }
    
          public event PropertyChangedEventHandler PropertyChanged = delegate { };
       }
    
       public interface ICustomerRepository : IRepository<Customer>
       {
       }
    
       public class Customer
       {
          public int Id { get; set; }
       }
    
       public interface IProductRepository : IRepository<Product>
       {
       }
    
       public class Product
       {
       }
    

    这是标准 IRepository 的样子,这被称为存储库模式:

       public interface IRepository<T>
       {
          IEnumerable<T> GetAll();
          T GetById(int id);
          void Save(T saveThis);
          void Delete(T deleteThis);
       }
    

    【讨论】:

    • 当你说 Order 和 CurrentLine 应该只是一个类型时,你的意思是像 OrderDto 一样吗?参考 1) 这就是我认为我需要做的。我有点卡住了,因为 Customer 的设置器在 WMSOrderViewModel 上,我没有从这里参考 OrderScreenViewModel 上的产品 - 我如何将这两者链接在一起?
    • 感谢您的代码。我已经在使用 NHibernate 支持的存储库接口来获取我的数据——我不想在这个例子中包含它的代码。在使用您的代码作为灵感之后,我的视图模型可以按照我的意愿工作。我现在唯一的问题是它非常大。我想我需要将 OrderScreenViewModel 拆分为组件视图模型并在它们之间进行通信。
    猜你喜欢
    • 1970-01-01
    • 2014-03-30
    • 1970-01-01
    • 2011-01-30
    • 2018-05-03
    • 2017-02-02
    • 2015-09-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多