c# - 如何让一个 View 模型更新另一个 View 模型的属性?

标签 c# events mvvm delegates view

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

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

最佳答案

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

数据

这不是资源的 View 模型,但大多数有趣的应用程序都有数据,而且它必须来自某个地方!当添加新项目时,它是举办事件的明显且方便的候选者:

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

    ...
}

外壳

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

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 通知易于使用的基类,因为它是数据绑定(bind)的基础。请注意,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,因为它内置了对数据绑定(bind)的支持。

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,
贝瑞尔

关于c# - 如何让一个 View 模型更新另一个 View 模型的属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5128256/

相关文章:

objective-c - 全局热键碳事件的 cocoa 替代品

c# - 防止事件处理程序中 InvokeRequired 的必要性

c# - "Ternary"不同方法签名的运算符

c# - 无效的ID应该抛出什么异常?

c# - OData 是否允许用户使用 IQueryable 查询 SQL EF 数据库?

Silverlight ListBox - 静态加绑定(bind)项

php - 在加载页面之前执行代码的最佳方式是什么

javascript - 访问事件监听器中的对象

c# - 带有 MVVM 模式的 WPF 中的组合框列绑定(bind) (Galasoft)

c# - 绑定(bind)复选框状态不会触发 OnPropertyChanged