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