MVVM WPF Master Detail Comboboxes - PullRequest
       1

MVVM WPF Master Detail Comboboxes

5 голосов
/ 08 февраля 2012

Благодаря некоторым советам, которые я получил ранее относительно переполнения стека, я добился хорошего прогресса в моем понимании MVVM. Однако, когда все становится сложнее, я все еще борюсь.

У меня есть вид ниже, который предназначен для ввода заказов. Он связан с DataContext из OrderScreenViewModel.

<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>

Первый комбинированный список используется для выбора Клиента. Второй комбинированный список используется для выбора ProductCode для новой OrderLine.

Есть пункты, которые я не могу понять, как достичь в MVVM:
1) Когда выбран клиент, обновите комбинированный список продуктов, чтобы в его источнике отображались только продукты с тем же идентификатором CustomerId, что и в записи CustomerDto, выбранной в комбинированном списке
2) Когда вызывается Load, установите SelectedItem в выпадающем списке Customers, чтобы он отображал Customer с CustomerId, равным единице в OrderDto.
3) Применить, тот же процесс, что и 1), чтобы показывать / загружать только Продукты, принадлежащие этому Клиенту, и устанавливать SelectedItem в выпадающем списке продуктов, чтобы он указывал на запись с тем же ProductId, который есть содержится в OrderLineDto

Я не уверен, как действовать, или даже если я правильно понял обязанности моих моделей. Может быть, это как-то связано с 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>();
        }
    }

1 Ответ

4 голосов
/ 08 февраля 2012

Вам нужно сделать так, чтобы Товары и Клиенты набирали ObservableCollection.

Когда вы изменяете эти наблюдаемые коллекции в вашей модели представления, они обновят представление, потому что OC уже реализует INotifyPropertyChanged.

Order и CurrentLine должны быть просто типом и не должны называться ViewModel.

1) Это необходимо сделать, когда метод set вызывается в SelectedItem клиента.Выделен комбинированный список.

2) Это необходимо сделать, вероятно, в ctr объекта OrderScreenViewModel, используя свою логику, чтобы определить, какого клиента также следует изменить CurrentLine.Customer.Если вы сделаете это в ctr, это установит значение до того, как произойдет привязка.

3) Опять же, до тех пор, пока вы вносите изменения в ObservableCollection, к которому привязан комбинированный список, он будет обновлять пользовательский интерфейс.Если вы вносите изменения в то, что 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>

это должно привести вас в правильное русло, все, вся логика построения клиентов и продуктов по идентификатору клиента должна происходить в ваших репозиториях.

   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);
   }
...