【问题标题】:How can I make one viewmodel update a property on another viewmodel?如何让一个视图模型更新另一个视图模型上的属性?
【发布时间】:2011-07-04 22:28:33
【问题描述】:

我需要一个简单的例子来说明如何让一个视图模型更新另一个视图模型的属性。

这里'是情况。我有一个视图和视图模型负责显示专辑列表。我有另一个视图和视图模型负责添加一个新专辑(几个文本框和一个按钮),现在当新专辑被添加时,我如何告诉另一个视图中的集合新专辑已添加到其中? 我读到了可以为我做这件事的框架,但我正在努力学习,所以我不会使用框架。

【问题讨论】:

    标签: c# events mvvm delegates view


    【解决方案1】:

    只需像这样实现您的属性。

    private bool _checked;
        public bool Checked
        {
            get { return _checked; }
            set
            {
                if (value != _checked)
                {
                    _checked = value;
                    OnPropertyChanged("Checked");
                }
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        public virtual void OnPropertyChanged(string propertyName)
        {
            var handler = PropertyCHanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    

    然后将您的其他视图模型订阅到 propertyChangedEvent 并执行您需要执行的操作。 确保您的 ViewModel 实现 INotifyPropertyChanged

    【讨论】:

    • 感谢输入。我决定创建一个了解这两个视图模型的控制器。
    • @Marco 你能解释一下我如何实现这个:“将你的其他视图模型订阅到 propertyChangedEvent ”?
    • @Ehsan 您只需在模型上使用 += 语法来订阅事件处理程序。每当您的视图模型调用处理程序时,订阅者都会调用它。例如,它与您在传统 winform 中执行 OnClick 事件等相同。
    【解决方案2】:

    有几种方法:

    1) AlbumsVM 知道CreateAlbumVM(例如第一个打开第二个)。在这种情况下,您只需使用CreateAlbumVM提供的详细信息将相册添加到AlbumsVM
    2)CreateAlbumVM 知道AlbumsVM。然后它可以将相册插入AlbumsVM 本身。
    3) AlbumsVM 从某个地方以ObservableCollection 的形式接收专辑。然后CreateAlbumVM 可以将新专辑插入到原来的ObservableCollection 中,这将反映在AlbumsVM
    4) 在这些 viewModel 之间有一些提供事件AlbumWasAdded 的中介。

    【讨论】:

    • 请你举个例子
    • “CreateAlbumVM 知道 AlbumsVM”怎么说?我很难建立这种联系。我所看到的只是使用一个不容易使用蝙蝠的信使类!你能解释一下怎么做吗?谢谢
    【解决方案3】:

    这里有一些拼图,取自 Josh Smith's classic demo app,展示了如何使用事件来支持 mvvm 提供的可测试性和松散耦合

    数据

    这不是一个视图模型,但大多数有趣的应用程序都有数据并且它必须来自某个地方!它是在添加新项目时举行活动的明显且方便的候选者:

    public class CustomerRepository
    {
        ...
    
        /// <summary>Raised when a customer is placed into the repository.</summary>
        public event EventHandler<CustomerAddedEventArgs> CustomerAdded;
    
        /// <summary>
        /// Places the specified customer into the repository.
        /// If the customer is already in the repository, an
        /// exception is not thrown.
        /// </summary>
        public void AddCustomer(Customer customer)
        {
            if (customer == null) throw new ArgumentNullException("customer");
            if (_customers.Contains(customer)) return;
    
            _customers.Add(customer);
    
            if (CustomerAdded != null)
                CustomerAdded(this, new CustomerAddedEventArgs(customer));
        }
    
        ...
    }
    

    贝壳

    考虑有一个视图模型来协调给定的演示文稿,也许是通过管理工作区。有些人可能会称它为 Manager (yuk!) 或 MainViewModel。我喜欢 ShellViewModel。这个视图模型有一个创建新项目的命令:

    public class MainWindowViewModel : WorkspaceViewModel
    {
    
        readonly CustomerRepository _customerRepository;
    
        public MainWindowViewModel(...)
        {
            _customerRepository = new CustomerRepository(customerDataFile);
        }
    
        void _createNewCustomer()
        {
            var newCustomer = Customer.CreateNewCustomer();
            var workspace = new CustomerViewModel(newCustomer, _customerRepository);
            Workspaces.Add(workspace);
            _setActiveWorkspace(workspace);
        }
    
        ObservableCollection<WorkspaceViewModel> _workspaces;
    
        void _setActiveWorkspace(WorkspaceViewModel workspace)
        {
            var collectionView = CollectionViewSource.GetDefaultView(Workspaces);
            if (collectionView != null)
                collectionView.MoveCurrentTo(workspace);
        }
    
     }
    

    模型对象

    你注意到静态工厂构造方法(Customer.CreateNewCustomer)了吗?它明确了它的目的是什么,并提供了一个机会来封装创建新客户所涉及的任何复杂性。

    模型对象 ViewModel 包装器

    这通常源自使 INPC 通知易于使用的基类,因为它是数据绑定的基础。请注意,Email 属性是模型对象的 email 属性的直接传递,但 DisplayName 是纯 UI 驱动的。在添加新项目的情况下,它适当地表示...“New Cistomer”:

    public class CustomerViewModel : WorkspaceViewModel, IDataErrorInfo
    {
    
        public CustomerViewModel(Customer customer, CustomerRepository customerRepository)
        {
            if (customer == null) throw new ArgumentNullException("customer");
            if (customerRepository == null) throw new ArgumentNullException("customerRepository");
    
            _customer = customer;
            _customerRepository = customerRepository;
         }
    
        readonly Customer _customer;
    
        public string Email
        {
            get { return _customer.Email; }
            set
            {
                if (value == _customer.Email) return;
    
                _customer.Email = value;
                base.OnPropertyChanged("Email");
            }
        }
    
        public override string DisplayName
        {
            get {
                if (IsNewCustomer)
                {
                    return Strings.CustomerViewModel_DisplayName;
                }
                ...
    
                return String.Format("{0}, {1}", _customer.LastName, _customer.FirstName);
            }
        }
    
    
        #region Save Command
    
        /// <summary>
        /// Returns a command that saves the customer.
        /// </summary>
        public ICommand SaveCommand
        {
            get
            {
                return _saveCommand ??
                       (_saveCommand = new RelayCommand(param => _save(), param => _canSave));
            }
        }
        RelayCommand _saveCommand;
    
        /// <summary>
        /// Returns true if the customer is valid and can be saved.
        /// </summary>
        bool _canSave
        {
            get { return String.IsNullOrEmpty(_validateCustomerType()) && _customer.IsValid; }
        }
    
        /// <summary>
        /// Saves the customer to the repository.  This method is invoked by the SaveCommand.
        /// </summary>
        void _save()
        {
            if (!_customer.IsValid)
                throw new InvalidOperationException(Strings.CustomerViewModel_Exception_CannotSave);
    
            if (IsNewCustomer)
                _customerRepository.AddCustomer(_customer);
            base.OnPropertyChanged("DisplayName");
        }
    

    }

    ViewModel 的 ViewModel 集合

    这可能支持过滤、排序、求和。在添加新客户的情况下,请注意它正在订阅我们添加到存储库中的那个事件。另请注意,它使用 ObservableCollection,因为它内置了对数据绑定的支持。

    public class AllCustomersViewModel : WorkspaceViewModel
    {
    
        public AllCustomersViewModel(CustomerRepository customerRepository)
        {
            if (customerRepository == null) throw new ArgumentNullException("customerRepository");
    
            _customerRepository = customerRepository;
    
            // Subscribe for notifications of when a new customer is saved.
            _customerRepository.CustomerAdded += OnCustomerAddedToRepository;
    
            // Populate the AllCustomers collection with CustomerViewModels.
            _createAllCustomers();              
        }
    
        /// <summary>
        /// Returns a collection of all the CustomerViewModel objects.
        /// </summary>
        public ObservableCollection<CustomerViewModel> AllCustomers
        {
            get { return _allCustomers; }
        }
        private ObservableCollection<CustomerViewModel> _allCustomers;
    
        void _createAllCustomers()
        {
            var all = _customerRepository
                .GetCustomers()
                .Select(cust => new CustomerViewModel(cust, _customerRepository))
                .ToList();
    
            foreach (var cvm in all)
                cvm.PropertyChanged += OnCustomerViewModelPropertyChanged;
    
            _allCustomers = new ObservableCollection<CustomerViewModel>(all);
            _allCustomers.CollectionChanged += OnCollectionChanged;
        }
    
        void OnCustomerViewModelPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            const string IsSelected = "IsSelected";
    
            // Make sure that the property name we're referencing is valid.
            // This is a debugging technique, and does not execute in a Release build.
            (sender as CustomerViewModel).VerifyPropertyName(IsSelected);
    
            // When a customer is selected or unselected, we must let the
            // world know that the TotalSelectedSales property has changed,
            // so that it will be queried again for a new value.
            if (e.PropertyName == IsSelected)
                OnPropertyChanged("TotalSelectedSales");
        }
    
        readonly CustomerRepository _customerRepository;
    
        void OnCustomerAddedToRepository(object sender, CustomerAddedEventArgs e)
        {
            var viewModel = new CustomerViewModel(e.NewCustomer, _customerRepository);
            _allCustomers.Add(viewModel);
        }
    
    }
    

    查看全文并下载代码!

    HTH,
    浆果

    【讨论】:

      【解决方案4】:

      您给人的印象是,您认为每个 View 的 Viewmodel 应该是独立的类。他们不是。 Viewmodels 以 View 可以绑定到它的方式重新打包底层数据。因此,创建一个 ObservableCollection&lt;Album> 并让两个 Viewmodel 都引用它。完成!

      【讨论】:

        猜你喜欢
        • 2011-08-09
        • 1970-01-01
        • 1970-01-01
        • 2014-10-06
        • 1970-01-01
        • 1970-01-01
        • 2017-04-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多