Я бы никоим образом не связывал или не связывал ВМ с событиями элементов управления в представлении. Вместо этого имейте отдельное событие, которое вызывается представлением в ответ на нажатие кнопки.
[заявление об отказе: этот код сделан прямо из моей головы, а не копируется и вставляется из VS - рассматривайте это как пример !!]
Итак, в псевдокоде вид будет выглядеть так:
private void MyView_Loaded(...)
{
MyButton.Click += new EventHandler(MyButton_Click);
}
private void MyButton_Click(...)
{
//Raise my event:
OnUserPressedGo();
}
private void OnUserPressedGo()
{
if (UserPressedTheGoButton != null)
this.UserPressedTheGoButton(this, EventArgs.Empty);
}
public EventHandler UserPressedTheGoButton;
и виртуальная машина будет иметь такую строку:
MyView.UserPressedTheGoButton += new EventHandler(myHandler);
это может показаться немного скучным, почему бы не сделать это более прямо? Основная причина этого заключается в том, что вы не хотите слишком тесно (если вообще) привязывать свою ВМ к содержимому представления, в противном случае изменение представления становится затруднительным. Наличие такого события, не связанного с пользовательским интерфейсом, как это, означает, что кнопка может меняться в любое время, не затрагивая ВМ - вы можете изменить ее с кнопки на гиперссылку, или тот дизайнер kool kat, которого вы нанимаете, может изменить его на нечто совершенно странное и прикольное, это не не имеет значения.
Теперь поговорим о событии SelectedItemChanged списка. Скорее всего, вы хотите перехватить событие для этого, чтобы вы могли изменить данные, связанные с другим элементом управления в представлении. Если это правильное предположение, тогда продолжайте читать - если я ошибаюсь, прекратите чтение и повторно используйте пример сверху:
Скорее всего, вы сможете избежать необходимости в обработчике для этого события. Если вы привяжете SelectedItem списка к свойству в ВМ:
<ListBox ItemSource={Binding SomeList} SelectedItem={Binding MyListSelectedItem} />
и затем в свойстве MyListSelectedItem виртуальной машины:
public object MyListSelectedItem
{
get { return _myListSelectedItem; }
set
{
bool changed = _myListSelectedItem != value;
if (changed)
{
_myListSelectedItem = value;
OnPropertyChanged("MyListSelectedItem");
}
}
}
private void OnPropertyChanged(string propertyName)
{
if (this.NotifyPropertyChanged != null)
this.NotifyPropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
Чтобы получить это NotifyPropertyChanged
событие, просто внедрите интерфейс INotifyPropertyChanged
на своей виртуальной машине (что вы уже должны были сделать). Это основные вещи в пути ... затем вы обрабатываете обработчик событий NotifyPropertyChanged на самой виртуальной машине:
private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "MyListSelectedItem":
//at this point i know the value of MyListSelectedItem has changed, so
//i can now retrieve its value and use it to modify a secondary
//piece of data:
MySecondaryList = AllAvailableItemsForSecondaryList.Select(p => p.Id == MyListSelectedItem.Id);
break;
}
}
Все, что вам сейчас нужно, это чтобы MySecondaryList также уведомил об изменении его значения:
public List<someObject> MySecondaryList
{
get { return _mySecondaryList; }
set
{
bool changed = .......;
if (changed)
{
... etc ...
OnNotifyPropertyChanged("MySecondaryList");
}
}
}
и все, что с ним связано, будет автоматически обновлено. Еще раз может показаться, что это долгий путь, но это означает, что вы избежали использования каких-либо обработчиков для событий пользовательского интерфейса из View, вы сохранили абстракцию между View и ViewModel.
Надеюсь, это имело для вас смысл. С моим кодом я пытаюсь, чтобы ViewModel знал абсолютно нулевой вид, а View только знал минимум о ViewModel (View получает ViewModel как интерфейс, поэтому он может знать только то, что определил интерфейс).