Как я могу заставить одну модель представления обновить свойство другой модели представления? - PullRequest
3 голосов
/ 26 февраля 2011

Мне нужен простой пример того, как заставить одну модель представления обновить свойство другой модели представления.

Здесь 'ситуация такова.Я получил представление и ответную модель, отображающую список альбомов.У меня есть другой вид и модель представления, отвечающая за добавление новых альбомов (пара текстовых полей и кнопка), теперь, когда добавляется новый альбом, как мне сообщить Коллекции в другом представлении, что новый альбом был добавлен в нее?Я читал о фреймворках, которые могут сделать это для меня, но я пытаюсь учиться, поэтому я не буду использовать фреймворк для существа.

Ответы [ 4 ]

3 голосов
/ 26 февраля 2011

Вот некоторые фрагменты головоломки, взятые из Классическое демонстрационное приложение Джоша Смита , показывающее, как можно использовать события для поддержки тестируемости и слабой пары, предоставляемой 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));
    }

    ...
}

Раковина

Рассмотрите возможность использования модели представления, которая координирует данную презентацию, возможно, путем управления рабочими пространствами. Некоторые люди могут назвать это Менеджером (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 - это прямой переход к свойству электронной почты объекта модели, однако DisplayNAme управляется исключительно пользовательским интерфейсом. В случае добавления нового элемента, он соответствующим образом говорит ... "Новый 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 из ViewModels

Это может поддерживать фильтрацию, сортировку, суммирование. В случае добавления нового клиента, обратите внимание, что он подписывается на то событие, которое мы добавили в репозиторий. Также обратите внимание на то, что он использует 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);
    }

}

Ознакомьтесь с полной статьей и загрузите код!

НТН,
Berryl

1 голос
/ 26 февраля 2011

Есть несколько способов:

1) AlbumsVM знает о CreateAlbumVM (например, первый открывает второй). В этом случае вы можете просто добавить альбом в AlbumsVM, используя данные, предоставленные CreateAlbumVM
2) CreateAlbumVM знает о AlbumsVM. Затем он может вставлять альбомы в AlbumsVM сам.
3) AlbumsVM получает альбомы как ObservableCollection откуда-то. Затем CreateAlbumVM может вставить новый альбом в оригинальный ObservableCollection, который будет отражен в AlbumsVM
4) Между этими viewModels существует некоторый посредник, который обеспечивает событие AlbumWasAdded.

0 голосов
/ 27 февраля 2011

У вас создается впечатление, что вы думаете, что Viewmodels для каждого View должны быть изолированными классами.Они не.Модели представления переупаковывают базовые данные таким образом, чтобы представление могло с ними связываться.Итак, сделайте ObservableCollection <Album> и сделайте так, чтобы обе Viewmodels ссылались на него.Готово! * * 1002

0 голосов
/ 26 февраля 2011

Просто реализуйте свои свойства следующим образом.

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 и сделайте все, что вам нужно с ней сделать.Убедитесь, что ваши ViewModels реализуют INotifyPropertyChanged

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...