SlideShare a Scribd company logo
MVVM в WinForms
DEVEXPRESS WAY
dmitry.garavsky@devexpress.com
MVVM? А зачем оно надо?
”When capabilities are extended, the
codebase should become smaller”.
@Thinking Out Loud
Четкое разделение бизнес-логики и логики представления.
Отсюда все вытекающие бенефиты и профиты.
MVVM в WPF. Типичная схема...
View	
  View	
   Model	
  
Business Logic
and Data
View	
  Model	
  
Presentation
Logic
Data binding
Commands
Notifications
… и типичные проблемы:
	
   реализация уведомленийреализация командограничения механизма привязкиреализация механизма сервисов
RelayCommand
BooleanToVisibilityConverer
DataContext
ServiceContainer
Зачем нам свой MVVM
Framework?
●  Стандартные	
  конвертеры,	
  сервисы	
  и	
  
поведения	
  
●  Мощный	
  и	
  гибкий	
  механизм	
  POCO	
  
●  Полная	
  поддержка	
  со	
  стороны	
  DX	
  контролов	
  
(MVVM-­‐driven	
  design)	
  
	
  
MVVM в WinForms. Попробуем?
View	
  View	
   Model	
  
Business Logic
and Data
View	
  Model	
  
Presentation
Logic
Data binding
Commands
Notifications
Code-behind
Events
Methods
class	
  AccountCollectionViewPresenter	
  {	
  
	
  	
  	
  	
  public	
  CollectionViewPresenter(GridView	
  gridView,	
  AccountCollectionViewModel	
  viewModel)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  gridView.FocusedRowObjectChanged	
  +=	
  (s,	
  e)	
  =>	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  viewModel.SelectedEntity	
  =	
  e.Row	
  as	
  Model.Account;	
  
	
  	
  	
  	
  	
  	
  	
  	
  };	
  
	
  	
  	
  	
  	
  	
  	
  	
  ((INotifyPropertyChanged)viewModel).PropertyChanged	
  +=	
  (s,	
  e)	
  =>	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if(e.PropertyName	
  ==	
  "SelectedEntity")	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  var	
  entity	
  =	
  viewModel.SelectedEntity;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if(entity	
  !=	
  null)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  gridView.FocusedRowHandle	
  =	
  gridView.LocateByValue("Id",	
  entity.ID);	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  else	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  gridView.FocusedRowHandle	
  =	
  GridControl.InvalidRowHandle;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  };	
  
	
  	
  	
  	
  }	
  
}	
  
От MVVM к MVPVM. Presenter.
Выделяем	
  узконаправленный	
  код	
  в	
  специализированные	
  классы	
  
class	
  AccountCollectionViewPresenter	
  {	
  
	
  	
  	
  	
  public	
  CollectionViewPresenter(GridView	
  gridView,	
  AccountCollectionViewModel	
  viewModel)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  gridView.FocusedRowObjectChanged	
  +=	
  (s,	
  e)	
  =>	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  viewModel.SelectedEntity	
  =	
  e.Row	
  as	
  Model.Account;	
  
	
  	
  	
  	
  	
  	
  	
  	
  };	
  
	
  	
  	
  	
  	
  	
  	
  	
  ((INotifyPropertyChanged)viewModel).PropertyChanged	
  +=	
  (s,	
  e)	
  =>	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if(e.PropertyName	
  ==	
  "SelectedEntity")	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  var	
  entity	
  =	
  viewModel.SelectedEntity;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if(entity	
  !=	
  null)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  gridView.FocusedRowHandle	
  =	
  gridView.LocateByValue("Id",	
  entity.ID);	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  else	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  gridView.FocusedRowHandle	
  =	
  GridControl.InvalidRowHandle;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  };	
  
	
  	
  	
  	
  }	
  
}	
  
gridView.FocusedRowObjectChanged	
  +=	
  (s,	
  e)	
  =>	
  {	
  
	
  	
  	
  viewModel.SelectedEntity	
  =	
  e.Row	
  as	
  Model.Account;	
  
};
От MVVM к MVPVM. Presenter.
Выделяем	
  узконаправленный	
  код	
  в	
  специализированные	
  классы	
  
WinForms - Presenter повсюду.
●  User	
  Control	
  и	
  его	
  Code-­‐Behind	
  
●  Отдельный	
  класс	
  
●  Отдельный	
  метод	
  для	
  настройки	
  
контрола	
  
●  Специфичный	
  кусок	
  Code-­‐Behind	
  
●  Обработчик	
  события	
  
●  Привязка(Binding)	
  
•  Привязки	
  к	
  данным.	
  
•  Команды	
  и	
  привязки	
  к	
  командам.	
  
•  Поведения	
  и	
  сервисы.	
  
•  Бонус	
  –	
  удобный	
  механизм	
  реализации	
  
уведомлений,	
  зависимостей,	
  команд	
  
MVPVM без буквы “P”.
больше	
  удобства,	
  меньше	
  кода	
  
Уведомления об изменении
public	
  class	
  LoginViewModel	
  :	
  INotifyPropertyChanged	
  {	
  
	
  	
  	
  	
  public	
  event	
  PropertyChangedEventHandler	
  PropertyChanged;	
  
	
  	
  	
  	
  string	
  userNameCore;	
  
	
  	
  	
  	
  public	
  string	
  UserName	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  get	
  {	
  return	
  userNameCore;	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  set	
  {	
  
	
   	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if(userNameCore	
  ==	
  value)	
  return;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  this.userNameCore	
  =	
  value;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  PropertyChangedEventHandler	
  handler	
  =	
  PropertyChanged;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if(handler	
  !=	
  null)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  handler(this,	
  new	
  PropertyChangedEventArgs("UserName"));	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  }	
  
}	
  
дать возможность стороннему
наблюдателю узнать об
изменении значения свойства
или состояния целевого объекта
Уведомления об изменении
public	
  class	
  LoginViewModel	
  :	
  INotifyPropertyChanged	
  {	
  
	
  	
  	
  	
  public	
  event	
  PropertyChangedEventHandler	
  PropertyChanged;	
  
	
  	
  	
  	
  string	
  userNameCore;	
  
	
  	
  	
  	
  public	
  string	
  UserName	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  get	
  {	
  return	
  userNameCore;	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  set	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if(value	
  !=	
  userNameCore)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  this.userNameCore	
  =	
  value;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  PropertyChangedEventHandler	
  handler	
  =	
  PropertyChanged;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if(handler	
  !=	
  null)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  handler(this,	
  new	
  PropertyChangedEventArgs("UserName"));	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  }	
  
}	
  
public	
  class	
  LoginViewModel	
  :	
  BindableBase	
  {	
  
	
  	
  	
  	
  string	
  userNameCore;	
  
	
  	
  	
  	
  public	
  string	
  UserName	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  get	
  {	
  return	
  userNameCore;	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  set	
  {	
  SetProperty(ref	
  userNameCore,	
  "UserName");}	
  
	
  	
  	
  	
  }	
  
}	
  
Уведомления об изменении
public	
  class	
  LoginViewModel	
  :	
  INotifyPropertyChanged	
  {	
  
	
  	
  	
  	
  public	
  event	
  PropertyChangedEventHandler	
  PropertyChanged;	
  
	
  	
  	
  	
  string	
  userNameCore;	
  
	
  	
  	
  	
  public	
  string	
  UserName	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  get	
  {	
  return	
  userNameCore;	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  set	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if(value	
  !=	
  userNameCore)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  this.userNameCore	
  =	
  value;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  PropertyChangedEventHandler	
  handler	
  =	
  PropertyChanged;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if(handler	
  !=	
  null)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  handler(this,	
  new	
  PropertyChangedEventArgs("UserName"));	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  }	
  
}	
  
POCO-ViewModel:
public	
  class	
  LoginViewModel	
  {	
  
	
  	
  	
  	
  public	
  virtual	
  string	
  UserName	
  {	
  get;	
  set;	
  }	
  
}
Plain Old Clr Object
POCO-трансформация
???	
  POCO-­‐class	
  
Full-­‐featured	
  
ViewModel	
  
DevExpress.Mvvm.POCO.ViewModelSource	
  
Уведомления об изменении
public	
  class	
  LoginViewModel	
  :	
  INotifyPropertyChanged	
  {	
  
	
  	
  	
  	
  public	
  event	
  PropertyChangedEventHandler	
  PropertyChanged;	
  
	
  	
  	
  	
  string	
  userNameCore;	
  
	
  	
  	
  	
  public	
  string	
  UserName	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  get	
  {	
  return	
  userNameCore;	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  set	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if(value	
  !=	
  userNameCore)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  this.userNameCore	
  =	
  value;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  PropertyChangedEventHandler	
  handler	
  =	
  PropertyChanged;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if(handler	
  !=	
  null)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  handler(this,	
  new	
  PropertyChangedEventArgs("UserName"));	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  }	
  
}	
  
POCO-ViewModel:
public	
  class	
  LoginViewModel	
  {	
  
	
  	
  	
  	
  public	
  virtual	
  string	
  UserName	
  {	
  get;	
  set;	
  }	
  
}
Как это использовать?
XAML:
<UserControl	
  x:Class="DXPOCO.Views.LoginView"	
  
	
  	
  	
  	
  xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm"	
  
	
  	
  	
  	
  xmlns:ViewModels="clr-­‐namespace:DXPOCO.ViewModels"	
  
	
  	
  	
  	
  DataContext="{dxmvvm:ViewModelSource	
  Type=ViewModels:LoginViewModel}"	
  
	
  	
  	
  	
  ...>	
  
</UserControl>	
  
WinForms Code-behind:
public	
  LoginViewModel	
  ViewModel	
  {	
  
	
  	
  	
  	
  get	
  {	
  return	
  mvvmContext.GetViewModel<LoginViewModel>();	
  }	
  
}	
  
Зависимости свойств
public	
  class	
  LoginViewModel	
  :	
  INotifyPropertyChanged	
  {	
  
	
  	
  	
  	
  public	
  event	
  PropertyChangedEventHandler	
  PropertyChanged;	
  
	
  	
  	
  	
  string	
  userNameCore;	
  
	
  	
  	
  	
  public	
  string	
  UserName	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  get	
  {	
  return	
  userNameCore;	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  set	
  {	
  
	
   	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if(userNameCore	
  ==	
  value)	
  return;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  this.userNameCore	
  =	
  value;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  PropertyChangedEventHandler	
  handler	
  =	
  PropertyChanged;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if(handler	
  !=	
  null)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  handler(this,	
  new	
  PropertyChangedEventArgs("UserName"));	
  
	
   	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  OnUserNameChanged();	
  //	
  OnUserNameChanged(oldValue)	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  }	
  
}	
  
дать возможность явно
уведомить сторонних
наблюдателей об изменении
значения свойства или
состояния целевого объекта
Зависимости свойств
public	
  class	
  LoginViewModel	
  :	
  INotifyPropertyChanged	
  {	
  
	
  	
  	
  	
  public	
  event	
  PropertyChangedEventHandler	
  PropertyChanged;	
  
	
  	
  	
  	
  string	
  userNameCore;	
  
	
  	
  	
  	
  public	
  string	
  UserName	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  get	
  {	
  return	
  userNameCore;	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  set	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if(value	
  !=	
  userNameCore)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  this.userNameCore	
  =	
  value;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  PropertyChangedEventHandler	
  handler	
  =	
  PropertyChanged;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if(handler	
  !=	
  null)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  handler(this,	
  new	
  PropertyChangedEventArgs("UserName"));	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  }	
  
}	
  
public	
  class	
  LoginViewModel	
  :	
  BindableBase	
  {	
  
	
  	
  	
  	
  string	
  userNameCore;	
  
	
  	
  	
  	
  public	
  string	
  UserName	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  get	
  {	
  return	
  userNameCore;	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  set	
  {	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  SetProperty(ref	
  userNameCore,	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "UserName",	
  OnUserNameChanged);	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  }	
  
}	
  
Зависимости свойств
public	
  class	
  LoginViewModel	
  :	
  INotifyPropertyChanged	
  {	
  
	
  	
  	
  	
  public	
  event	
  PropertyChangedEventHandler	
  PropertyChanged;	
  
	
  	
  	
  	
  string	
  userNameCore;	
  
	
  	
  	
  	
  public	
  string	
  UserName	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  get	
  {	
  return	
  userNameCore;	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  set	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if(value	
  !=	
  userNameCore)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  this.userNameCore	
  =	
  value;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  PropertyChangedEventHandler	
  handler	
  =	
  PropertyChanged;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if(handler	
  !=	
  null)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  handler(this,	
  new	
  PropertyChangedEventArgs("UserName"));	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  }	
  
}	
  
POCO-ViewModel:
public	
  class	
  LoginViewModel	
  {	
  
	
  	
  	
  	
  public	
  virtual	
  string	
  UserName	
  {	
  get;	
  set;	
  }	
  
	
  	
  	
  	
  protected	
  void	
  OnUserNameChanged()	
  {/*...*/}	
  
}
Ручное обновление зависимостей
public	
  class	
  LoginViewModel	
  :	
  INotifyPropertyChanged	
  {	
  
	
  	
  	
  	
  public	
  event	
  PropertyChangedEventHandler	
  PropertyChanged;	
  
	
  	
  	
  	
  string	
  userNameCore;	
  
	
  	
  	
  	
  public	
  string	
  UserName	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  get	
  {	
  return	
  userNameCore;	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  set	
  {	
  
	
   	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if(userNameCore	
  ==	
  value)	
  return;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  this.userNameCore	
  =	
  value;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  PropertyChangedEventHandler	
  handler	
  =	
  PropertyChanged;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if(handler	
  !=	
  null)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  handler(this,	
  new	
  PropertyChangedEventArgs("UserName"));	
  
	
   	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  OnUserNameChanged();	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  }	
  
}	
  
PropertyChangedEventHandler	
  handler	
  =	
  PropertyChanged;	
  
if(handler	
  !=	
  null)	
  
	
  	
  	
  	
  handler(this,	
  new	
  PropertyChangedEventArgs("UserName"));	
  
Ручное обновление зависимостей
public	
  class	
  LoginViewModel	
  :	
  INotifyPropertyChanged	
  {	
  
	
  	
  	
  	
  public	
  event	
  PropertyChangedEventHandler	
  PropertyChanged;	
  
	
  	
  	
  	
  string	
  userNameCore;	
  
	
  	
  	
  	
  public	
  string	
  UserName	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  get	
  {	
  return	
  userNameCore;	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  set	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if(value	
  !=	
  userNameCore)	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  this.userNameCore	
  =	
  value;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  PropertyChangedEventHandler	
  handler	
  =	
  PropertyChanged;	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if(handler	
  !=	
  null)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  handler(this,	
  new	
  PropertyChangedEventArgs("UserName"));	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  	
  	
  	
  	
  }	
  
	
  	
  	
  	
  }	
  
}	
  
POCO-ViewModel:
//	
  Extension	
  method	
  
this.RaisePropertyChanged(x	
  =>	
  x.UserName);	
  
Как это использовать?
XAML:
	
  
	
  	
  	
  	
  <dxe:TextEdit	
  Text="{Binding	
  UserName}"/>	
  
	
  
WinForms Code-behind (MVVMContext API):
	
  
mvvmContext.SetBinding(userNameTextEdit,	
  
	
  	
  	
  	
  edit	
  =>	
  edit.EditValue,	
  "UserName");	
  
//	
  ...	
  
var	
  fluentAPI	
  =	
  mvvmContext.OfType<LoginViewModel>();	
  
fluentAPI.SetBinding(lblUserName,	
  
	
  	
  	
  	
  lbl	
  =>	
  lbl.Text,	
  x	
  =>	
  x.UserName);	
  
Команды
	
   public	
  class	
  DelegateCommand<T>	
  :	
  System.Windows.Input.ICommand	
  {	
  
	
   	
  	
  	
  	
  readonly	
  Predicate<T>	
  _canExecute;	
  
	
   	
  	
  	
  	
  readonly	
  Action<T>	
  _execute;	
  
	
   	
  	
  	
  	
  public	
  DelegateCommand(Action<T>	
  execute)	
  
	
   	
  	
  	
  	
  	
  	
  	
  	
  :	
  this(execute,	
  null)	
  {	
  
	
   	
  	
  	
  	
  }	
  
	
   	
  	
  	
  	
  public	
  DelegateCommand(Action<T>	
  execute,	
  Predicate<T>	
  canExecute)	
  {	
  
	
   	
  	
  	
  	
  	
  	
  	
  	
  _execute	
  =	
  execute;	
  
	
   	
  	
  	
  	
  	
  	
  	
  	
  _canExecute	
  =	
  canExecute;	
  
	
   	
  	
  	
  	
  }	
  
	
   	
  	
  	
  	
  //...	
  
	
   }	
  
public	
  class	
  MyViewModel	
  {	
  
	
  	
  	
  	
  public	
  MyViewModel()	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  this.SayHelloCommand	
  =	
  new	
  DelegateCommand(SayHello);	
  
	
  	
  	
  	
  }	
  
	
  	
  	
  	
  public	
  System.Windows.Input.ICommand	
  SayHelloCommand	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  get;	
  
	
  	
  	
  	
  	
  	
  	
  	
  private	
  set;	
  
	
  	
  	
  	
  }	
  
	
  	
  	
  	
  public	
  void	
  SayHello()	
  {	
  /*	
  do	
  something	
  */	
  }	
  
}	
  
дать возможность
инкапсулировать действие в
отдельном объекте
Команды
	
   public	
  class	
  DelegateCommand<T>	
  :	
  System.Windows.Input.ICommand	
  {	
  
	
   	
  	
  	
  	
  readonly	
  Predicate<T>	
  _canExecute;	
  
	
   	
  	
  	
  	
  readonly	
  Action<T>	
  _execute;	
  
	
   	
  	
  	
  	
  public	
  DelegateCommand(Action<T>	
  execute)	
  
	
   	
  	
  	
  	
  	
  	
  	
  	
  :	
  this(execute,	
  null)	
  {	
  
	
   	
  	
  	
  	
  }	
  
	
   	
  	
  	
  	
  public	
  DelegateCommand(Action<T>	
  execute,	
  Predicate<T>	
  canExecute)	
  {	
  
	
   	
  	
  	
  	
  	
  	
  	
  	
  _execute	
  =	
  execute;	
  
	
   	
  	
  	
  	
  	
  	
  	
  	
  _canExecute	
  =	
  canExecute;	
  
	
   	
  	
  	
  	
  }	
  
	
   	
  	
  	
  	
  //...	
  
	
   }	
  
POCO-ViewModel:
public	
  class	
  MyViewModel	
  {	
  
	
  	
  	
  	
  public	
  void	
  SayHello()	
  {	
  /*	
  do	
  something	
  */	
  }	
  
}	
  
POCO-ViewModel:
public	
  class	
  MyViewModel	
  {	
  
	
  	
  	
  	
  public	
  void	
  SaySomething(string	
  str)	
  {	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  /*	
  ...	
  */	
  	
  
	
  	
  	
  	
  }	
  
	
  	
  	
  	
  public	
  bool	
  CanSaySomething(string	
  str)	
  {	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  /*	
  ...	
  */	
  	
  
	
  	
  	
  	
  }	
  
}	
  
POCO-ViewModel:
public	
  class	
  MyViewModel	
  {	
  
	
  	
  	
  	
  public	
  Task	
  DoSomething	
  ()	
  {	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  /*	
  asynchronous	
  !!!	
  */	
  	
  
	
  	
  	
  	
  }	
  
}	
  
Как это использовать?
XAML:
	
  
<Button	
  Command="{Binding	
  SayHello}"	
  />	
  	
  
<Button	
  Command="{Binding	
  Say}"	
  CommandParameter="Hello!"/>	
  	
  
Как это использовать?
WinForms Code-behind (MVVMContext API):
public	
  MyViewModel	
  ViewModel	
  {	
  
	
  	
  	
  	
  get	
  {	
  return	
  mvvmContext.GetViewModel<MyViewModel>();	
  }	
  
}	
  
//	
  
btnSayHello.BindCommand(	
  
	
  	
  	
  ()	
  =>	
  ViewModel.SayHello(),	
  ViewModel);	
  
btnSay.BindCommand(	
  
	
  	
  	
  ()	
  =>	
  ViewModel.Say(null),	
  ViewModel,	
  ()	
  =>	
  ViewModel.Name);	
  
WinForms Code-behind (MVVMContext Fluent API):
var	
  fluentApi	
  =	
  mvvmContext.OfType<MyViewModel>();	
  
fluentApi.BindCommand(x	
  =>	
  x.SayHello());	
  
fluentApi.BindCommand((x,s)	
  =>	
  x.Say(s),	
  x	
  =>	
  x.Name);	
  
Интерактивность. Сервисы.
ViewModel:
public	
  class	
  MyViewModel	
  {
	
  	
  	
  	
  protected	
  IMessageBoxService	
  MessageBoxService	
  {
	
  	
  	
  	
  	
  	
  	
  	
  get	
  {	
  /*	
  ...	
  */	
  }
	
  	
  	
  	
  }
	
  	
  	
  	
  public	
  void	
  SayHello()	
  {
	
  	
  	
  	
  	
  	
  	
  	
  MessageBoxService.Show("Hello!");	
  
	
  	
  	
  	
  }
}
дать возможность
взаимодействовать с
пользователем не нарушая
концепцию разделения слоев
POCO ViewModel:
protected	
  IMessageBoxService	
  MessageBoxService	
  {	
  
	
  	
  	
  	
  get	
  {	
  this.GetService<IMessageBoxService>();	
  }	
  
}	
  
	
  
protected	
  virtual	
  IMessageBoxService	
  MessageBoxService	
  {	
  
	
  	
  	
  	
  get	
  {	
  throw	
  new	
  System.NotImplementedException();	
  }	
  
}	
  
Интерактивность. Сервисы.
Как это использовать?
WinForms Code-behind (MVVMContext API):
mvvmContext.RegisterService(new	
  MessageBoxService());	
  
//...	
  
var	
  mbService	
  =	
  mvvmContext.GetService<IMessageBoxService>();	
  
mbService.Show("Something	
  happens!");	
  
public	
  class	
  ConfirmationBehavior	
  	
  
	
  	
  	
  :	
  ConfirmationBehavior<FormClosingEventArgs>	
  {	
  
	
  	
  	
  public	
  ConfirmationBehavior()	
  :	
  base("FormClosing")	
  {	
  }	
  
	
  	
  	
  protected	
  override	
  string	
  GetConfirmationCaption()	
  {	
  	
  
	
  	
  	
  	
  	
  	
  	
  return	
  "Oops!";	
  	
  
	
  	
  	
  }	
  
	
  	
  	
  protected	
  override	
  string	
  GetConfirmationText()	
  {	
  
	
  	
  	
  	
  	
  	
  	
  return	
  "Form	
  will	
  be	
  closed.	
  Are	
  you	
  sure?";	
  
	
  	
  	
  }	
  
}	
  
Поведения.
WinForms Code-behind (MVVMContext API):	
  
//...	
  
mvvmContext.AttachBehavior<ConfirmationBehavior>(this);	
  
	
  
дать возможность наделить
объект дополнительным
функционалом
действуя снаружи объекта
Поведения.
WinForms Code-behind (MVVMContext Fluent API):	
  
//...	
  
mvvmContext.WithEvent<CancelEventArgs>(this,	
  "Closing")	
  
	
  	
  	
  	
  .Confirmation(	
  
	
  	
  	
  	
  	
  	
  settings	
  =>	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  settings.Caption	
  =	
  "Closing	
  Confirmation";	
  
	
  	
  	
  	
  	
  	
  	
  	
  settings.Text	
  =	
  "Form	
  will	
  be	
  closed.	
  Press	
  OK	
  to	
  confirm.";	
  
	
  	
  	
  	
  	
  	
  	
  	
  settings.Buttons	
  =	
  ConfirmationButtons.OKCancel;	
  
	
  	
  	
  	
  	
  	
  	
  	
  settings.ShowQuestionIcon	
  =	
  false;	
  
	
  	
  	
  	
  	
  	
  });	
  
Поведения. EventToCommand.
public	
  class	
  ClickToSayHello	
  :	
  
	
  	
  	
  EventToCommandBehavior<MyViewModel,	
  EventArgs>	
  {	
  
	
  	
  	
  public	
  ClickToSayHello()	
  
	
  	
  	
   	
  :	
  base("Click",	
  x	
  =>	
  x.SayHello())	
  {	
  
	
  	
  	
  }	
  
}	
  
WinForms Code-behind (MVVMContext API):	
  
//...	
  
mvvmContext.AttachBehavior<ClickToSayHello>(thirdPartyButton);	
  
Поведения. EventToCommand.
WinForms Code-behind (MVVMContext Fluent API):	
  
	
  
mvvmContext.WithEvent<ViewModel,	
  EventArgs>	
  
	
  	
  	
  	
  (thirdPartyButton,	
  "Click")	
  
	
  	
  	
  	
  .EventToCommand(x	
  =>	
  x.SayHello());	
  
	
  
Простое CRUD-приложение Expenses.
•  Entity Framework 6.x (Nuget)
•  DevExpress MVVM Framework
•  DevExpress Scaffolding Wizard
•  DevExpress WinForms Controls
Структура проекта. Model.
public	
  class	
  Account	
  {
	
  	
  	
  	
  public	
  string	
  Name	
  {	
  get;	
  set;	
  }
	
  	
  	
  	
  public	
  decimal	
  Amount	
  {	
  get;	
  set;	
  }
}
public	
  class	
  ExpensesDbContext	
  :	
  DbContext	
  {	
  
	
  	
  	
  	
  static	
  ExpensesDbContext()	
  {	
  
	
  	
  	
  	
   	
  Database.SetInitializer<ExpensesDbContext>(	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  new	
  ExpensesDbContextInitializer());	
  
	
  	
  	
  	
  }	
  
	
  	
  	
  	
  public	
  DbSet<Account>	
  Accounts	
  {	
  get;	
  set;	
  }	
  
	
  	
  	
  	
  public	
  DbSet<Category>	
  Categories	
  {	
  get;	
  set;	
  }	
  
	
  	
  	
  	
  public	
  DbSet<Transaction>	
  Transactions	
  {	
  get;	
  set;	
  }	
  
}
public	
  class	
  Account	
  {	
  
	
  	
  	
  	
  [Key,	
  Display(AutoGenerateField	
  =	
  false)]	
  
	
  	
  	
  	
  public	
  int	
  ID	
  {	
  get;	
  set;	
  }	
  
	
  	
  	
  	
  [Required,	
  StringLength(30,	
  MinimumLength	
  =	
  4)]	
  
	
  	
  	
  	
  [Display(Name	
  =	
  "ACCOUNT")]	
  
	
  	
  	
  	
  public	
  string	
  Name	
  {	
  get;	
  set;	
  }	
  
	
  	
  	
  	
  [DataType(DataType.Currency)]	
  
	
  	
  	
  	
  [Display(Name	
  =	
  "AMOUNT")]	
  
	
  	
  	
  	
  public	
  decimal	
  Amount	
  {	
  get;	
  set;	
  }	
  
}	
  
Добавим Entity Framework
Используем Scaffolding
Создаем представления.
Структура	
   Типы	
  View	
  
●  Контейнер	
  навигации	
  
(DocumentManager+Ribbon)	
  
●  Контейнер	
  коллекции	
  
(XtraGrid)	
  
●  Контейнер	
  деталей	
  
(DataLayoutControl)	
  
	
  
	
  
Контейнер навигации
Привязки и навигация
mvvmContext.RegisterService(DocumentManagerService.Create(tabbedView));	
  
	
  
var	
  fluent	
  =	
  mvvmContext.OfType<ExpensesDbContextViewModel>();	
  
fluent.BindCommand(biAccounts,	
  (x,	
  m)	
  =>	
  x.Show(m),	
  x	
  =>	
  x.Modules[0]);	
  
fluent.BindCommand(biCategories,	
  (x,	
  m)	
  =>	
  x.Show(m),	
  x	
  =>	
  x.Modules[1]);	
  
fluent.BindCommand(biTransactions,	
  (x,	
  m)	
  =>	
  x.Show(m),	
  x	
  =>	
  x.Modules[2]);	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  
fluent.WithEvent<FormClosingEventArgs>(this,	
  "FormClosing")	
  
	
  	
  	
  	
  .EventToCommand(x	
  =>	
  x.OnClosing(null));	
  
	
  
Контейнер коллекции (XtraGrid)
Привязки и поведение
var	
  fluent	
  =	
  mvvmContext.OfType<AccountCollectionViewModel>();	
  
fluent.SetBinding(gridView,	
  v	
  =>	
  v.LoadingPanelVisible,	
  x	
  =>	
  x.IsLoading);	
  
fluent.SetBinding(gridControl,	
  g	
  =>	
  g.DataSource,	
  x	
  =>	
  x.Entities);	
  
	
  	
  
fluent.WithEvent<ColumnView,	
  FocusedRowObjectChangedEventArgs>(	
  
	
  	
  	
  	
  gridView,	
  "FocusedRowObjectChanged")	
  
	
  	
  	
  	
  .SetBinding(x	
  =>	
  x.SelectedEntity,	
  
	
  	
  	
  	
  	
  	
  	
  	
  args	
  =>	
  args.Row	
  as	
  Model.Account,	
  
	
  	
  	
  	
  	
  	
  	
  	
  (gView,	
  entity)	
  =>	
  gView.FocusedRowHandle	
  =	
  gView.FindRow(entity));	
  
fluent.WithEvent<RowCellClickEventArgs>(gridView,	
  "RowCellClick")	
  
	
  	
  	
  	
  .EventToCommand(	
  
	
  	
  	
  	
  	
  	
  	
  	
  x	
  =>	
  x.Edit(null),	
  x	
  =>	
  x.SelectedEntity,	
  
	
  	
  	
  	
  	
  	
  	
  	
  args	
  =>	
  (args.Clicks	
  ==	
  2)	
  &&	
  (args.Button	
  ==	
  MouseButtons.Left));	
  
Контейнер деталей (DataLayout)
Привязки и поведение
var	
  fluent	
  =	
  mvvmContext.OfType<AccountViewModel>();	
  
fluent.SetObjectDataSourceBinding(	
  
	
  	
  	
  	
  bindingSource,	
  x	
  =>	
  x.Entity,	
  x	
  =>	
  x.Update());	
  
	
  	
  
fluent.BindCommand(btnSave,	
  x	
  =>	
  x.Save());	
  
fluent.BindCommand(btnReset,	
  x	
  =>	
  x.Reset());	
  
	
  
[DataType(DataType.Date)]	
  
[Display(Name	
  =	
  "DATE")]	
  
public	
  DateTime	
  Date	
  {	
  get;	
  set;	
  }	
  
[DataType(DataType.Currency)]	
  
[Display(Name	
  =	
  "AMOUNT")]	
  
public	
  decimal	
  Amount	
  {	
  get;	
  set;	
  }	
  
[DataType(DataType.MultilineText)]	
  
[Display(Name	
  =	
  "COMMENT")]	
  
public	
  string	
  Comment	
  {	
  get;	
  set;	
  }	
  
[Required,	
  StringLength(30,	
  MinimumLength	
  =	
  4)]
[Display(Name	
  =	
  "ACCOUNT")]
public	
  string	
  Name	
  {	
  get;	
  set;	
  }
Полезные плюшки «из коробки»
Поддержка	
  аннотационных	
  аттрибутов	
  
Полезные плюшки «из коробки»
Автоматическая	
  привязка	
  команд	
  в	
  дизайнере	
  
Полезные плюшки «из коробки»
Регистрация	
  сервисов	
  в	
  дизайнере	
  
MessageBoxService - выбор типа
(стандартный, скинированный, Flyout):
DevExpress.Utils.MVVM.MVVMContext.RegisterFlyoutMessageBoxService();	
  
DocumentManagerService - управление навигацией
(обработка события QueryControl):
tabbedView.QueryControl	
  +=	
  (s,	
  e)	
  =>	
  {	
  
	
  	
  	
  	
  if(e.Document.ControlName	
  ==	
  "AccountCollectionView")	
  
	
  	
  	
  	
  	
  	
  	
  	
  e.Control	
  =	
  new	
  Views.Accounts();	
  
};	
  
	
  
Полезные плюшки «из коробки»
Настройка	
  сервисов	
  на	
  уровне	
  контролов	
  
Подведем итоги
•  Бесплатное	
  и	
  кроссплатформенное	
  ядро	
  
(WPF/Silverlight/WinForms/WinRT/UAP)	
  
•  Вся	
  мощь	
  системы	
  привязок	
  
•  Полная	
  поддержка	
  процесса	
  разработки	
  
•  Уверенность	
  в	
  положительном	
  
результате	
  на	
  любой	
  платформе	
  
MVVM	
  подход	
  +	
  DevExpress	
  
Спасибо за
внимание!
WWW.DEVEXPRESS.COM
support@devexpress.com
dmitry.garavsky@devexpress.com
Гаравский Дмитрий
Материалы
Статьи	
  и	
  руководства:	
  
1.  DevExpress	
  MVVM	
  Framework	
  and	
  MVVM(MVPVM)	
  Architectural	
  Pa{ern	
  in	
  
WinForms	
  
2.  MVVM	
  In	
  Ac~on:	
  Expenses	
  App	
  -­‐	
  Displaying	
  and	
  naviga~ng	
  between	
  DB	
  
Collec~ons(Tutorial	
  01)	
  
3.  MVVM	
  In	
  Ac~on:	
  Expenses	
  App	
  –	
  Displaying	
  DB	
  En~ty	
  details.	
  DB	
  En~ty	
  
proper~es	
  edi~ng.(Tutorial	
  02)	
  
	
  
Примеры	
  и	
  ссылки:	
  
1.  Приложение	
  Expenses	
  (ссылка	
  на	
  DevExpress	
  Code	
  Central)	
  
2.  DevExpress.MVVM.Free	
  (github)	
  

More Related Content

What's hot

Zagor Ludens Kolor 006 - Duhovi Diging Bila
Zagor Ludens Kolor 006 - Duhovi Diging BilaZagor Ludens Kolor 006 - Duhovi Diging Bila
Zagor Ludens Kolor 006 - Duhovi Diging Bila
Stripovizijacom
 
302 krater strave
302  krater strave302  krater strave
302 krater strave
Milenko Gavric
 
Vajat erp 294 srebrni kanjon
Vajat erp 294   srebrni kanjonVajat erp 294   srebrni kanjon
Vajat erp 294 srebrni kanjon
zoran radovic
 
Zagor vec 62 zaustavite dzelata
Zagor vec 62 zaustavite dzelataZagor vec 62 zaustavite dzelata
Zagor vec 62 zaustavite dzelataDino dino
 
97865113 carmen-de-posadas-kiwi (1)
97865113 carmen-de-posadas-kiwi (1)97865113 carmen-de-posadas-kiwi (1)
97865113 carmen-de-posadas-kiwi (1)
catherin valenzuela valderrama
 
Tafseer e fasl ul khitab - jild - 03, by Ayatullah al Uzma Syed Ali Naqi Naq...
Tafseer e fasl ul khitab - jild - 03,  by Ayatullah al Uzma Syed Ali Naqi Naq...Tafseer e fasl ul khitab - jild - 03,  by Ayatullah al Uzma Syed Ali Naqi Naq...
Tafseer e fasl ul khitab - jild - 03, by Ayatullah al Uzma Syed Ali Naqi Naq...
Jamal Mirza
 
0772. ŽRTVA KI-NOAH
0772. ŽRTVA KI-NOAH0772. ŽRTVA KI-NOAH
0772. ŽRTVA KI-NOAHTompa *
 
12.01 Keep Calm and Carry On
12.01 Keep Calm and Carry On 12.01 Keep Calm and Carry On
12.01 Keep Calm and Carry On
JulesWilkinson
 
0088. Blago Mrtvog Gusara
0088. Blago Mrtvog Gusara0088. Blago Mrtvog Gusara
0088. Blago Mrtvog GusaraTompa *
 
VSAT( Very Small Aperture Terminal) :- In Detail
VSAT( Very Small Aperture Terminal) :- In DetailVSAT( Very Small Aperture Terminal) :- In Detail
VSAT( Very Small Aperture Terminal) :- In Detail
Nitish Kumar
 
Educação Cristã - Ouvindo a Voz de Deus, Estudo Bíblico, Igreja Adventista
Educação Cristã - Ouvindo a Voz de Deus, Estudo Bíblico, Igreja AdventistaEducação Cristã - Ouvindo a Voz de Deus, Estudo Bíblico, Igreja Adventista
Educação Cristã - Ouvindo a Voz de Deus, Estudo Bíblico, Igreja Adventista
Igreja Adventista do Sétimo Dia
 
Jean Chevalier et Alain Gheerbrant - Dictionnaire des symboles
Jean Chevalier et Alain Gheerbrant - Dictionnaire des symbolesJean Chevalier et Alain Gheerbrant - Dictionnaire des symboles
Jean Chevalier et Alain Gheerbrant - Dictionnaire des symboles
Animus Prime
 
Zagor - Akt nasilja
Zagor - Akt nasiljaZagor - Akt nasilja
Zagor - Akt nasilja
Stripovizijacom
 
Dok holidej 137 adam bredi - gonic krda
Dok holidej 137   adam bredi - gonic krdaDok holidej 137   adam bredi - gonic krda
Dok holidej 137 adam bredi - gonic krda
zoran radovic
 
Monthly Khazina-e-Ruhaniyaat December’2021 (Vol.12, Issue 8)
Monthly Khazina-e-Ruhaniyaat December’2021 (Vol.12, Issue 8)Monthly Khazina-e-Ruhaniyaat December’2021 (Vol.12, Issue 8)
Monthly Khazina-e-Ruhaniyaat December’2021 (Vol.12, Issue 8)
Darul Amal Chishtia
 
Zagor LUDSP 036-Zenit 666
Zagor LUDSP 036-Zenit 666Zagor LUDSP 036-Zenit 666
Zagor LUDSP 036-Zenit 666
Stripovizijacom
 
Zagor VC-086- Bog praha
Zagor VC-086- Bog prahaZagor VC-086- Bog praha
Zagor VC-086- Bog praha
Stripovizijacom
 
Відповіді ДПА 2021, 4 клас, Українська мова Літературне читання Шевченко, Ранок
Відповіді ДПА 2021, 4 клас, Українська мова Літературне читання Шевченко, РанокВідповіді ДПА 2021, 4 клас, Українська мова Літературне читання Шевченко, Ранок
Відповіді ДПА 2021, 4 клас, Українська мова Літературне читання Шевченко, Ранок
ErudytNet
 
καστράτο η πιο σκύλα γάτα
καστράτο   η πιο σκύλα γάτακαστράτο   η πιο σκύλα γάτα
καστράτο η πιο σκύλα γάταpinnokio.gr
 
ισοβίτης γιατρέ έχω ένα βάρος
ισοβίτης   γιατρέ έχω ένα βάροςισοβίτης   γιατρέ έχω ένα βάρος
ισοβίτης γιατρέ έχω ένα βάροςpinnokio.gr
 

What's hot (20)

Zagor Ludens Kolor 006 - Duhovi Diging Bila
Zagor Ludens Kolor 006 - Duhovi Diging BilaZagor Ludens Kolor 006 - Duhovi Diging Bila
Zagor Ludens Kolor 006 - Duhovi Diging Bila
 
302 krater strave
302  krater strave302  krater strave
302 krater strave
 
Vajat erp 294 srebrni kanjon
Vajat erp 294   srebrni kanjonVajat erp 294   srebrni kanjon
Vajat erp 294 srebrni kanjon
 
Zagor vec 62 zaustavite dzelata
Zagor vec 62 zaustavite dzelataZagor vec 62 zaustavite dzelata
Zagor vec 62 zaustavite dzelata
 
97865113 carmen-de-posadas-kiwi (1)
97865113 carmen-de-posadas-kiwi (1)97865113 carmen-de-posadas-kiwi (1)
97865113 carmen-de-posadas-kiwi (1)
 
Tafseer e fasl ul khitab - jild - 03, by Ayatullah al Uzma Syed Ali Naqi Naq...
Tafseer e fasl ul khitab - jild - 03,  by Ayatullah al Uzma Syed Ali Naqi Naq...Tafseer e fasl ul khitab - jild - 03,  by Ayatullah al Uzma Syed Ali Naqi Naq...
Tafseer e fasl ul khitab - jild - 03, by Ayatullah al Uzma Syed Ali Naqi Naq...
 
0772. ŽRTVA KI-NOAH
0772. ŽRTVA KI-NOAH0772. ŽRTVA KI-NOAH
0772. ŽRTVA KI-NOAH
 
12.01 Keep Calm and Carry On
12.01 Keep Calm and Carry On 12.01 Keep Calm and Carry On
12.01 Keep Calm and Carry On
 
0088. Blago Mrtvog Gusara
0088. Blago Mrtvog Gusara0088. Blago Mrtvog Gusara
0088. Blago Mrtvog Gusara
 
VSAT( Very Small Aperture Terminal) :- In Detail
VSAT( Very Small Aperture Terminal) :- In DetailVSAT( Very Small Aperture Terminal) :- In Detail
VSAT( Very Small Aperture Terminal) :- In Detail
 
Educação Cristã - Ouvindo a Voz de Deus, Estudo Bíblico, Igreja Adventista
Educação Cristã - Ouvindo a Voz de Deus, Estudo Bíblico, Igreja AdventistaEducação Cristã - Ouvindo a Voz de Deus, Estudo Bíblico, Igreja Adventista
Educação Cristã - Ouvindo a Voz de Deus, Estudo Bíblico, Igreja Adventista
 
Jean Chevalier et Alain Gheerbrant - Dictionnaire des symboles
Jean Chevalier et Alain Gheerbrant - Dictionnaire des symbolesJean Chevalier et Alain Gheerbrant - Dictionnaire des symboles
Jean Chevalier et Alain Gheerbrant - Dictionnaire des symboles
 
Zagor - Akt nasilja
Zagor - Akt nasiljaZagor - Akt nasilja
Zagor - Akt nasilja
 
Dok holidej 137 adam bredi - gonic krda
Dok holidej 137   adam bredi - gonic krdaDok holidej 137   adam bredi - gonic krda
Dok holidej 137 adam bredi - gonic krda
 
Monthly Khazina-e-Ruhaniyaat December’2021 (Vol.12, Issue 8)
Monthly Khazina-e-Ruhaniyaat December’2021 (Vol.12, Issue 8)Monthly Khazina-e-Ruhaniyaat December’2021 (Vol.12, Issue 8)
Monthly Khazina-e-Ruhaniyaat December’2021 (Vol.12, Issue 8)
 
Zagor LUDSP 036-Zenit 666
Zagor LUDSP 036-Zenit 666Zagor LUDSP 036-Zenit 666
Zagor LUDSP 036-Zenit 666
 
Zagor VC-086- Bog praha
Zagor VC-086- Bog prahaZagor VC-086- Bog praha
Zagor VC-086- Bog praha
 
Відповіді ДПА 2021, 4 клас, Українська мова Літературне читання Шевченко, Ранок
Відповіді ДПА 2021, 4 клас, Українська мова Літературне читання Шевченко, РанокВідповіді ДПА 2021, 4 клас, Українська мова Літературне читання Шевченко, Ранок
Відповіді ДПА 2021, 4 клас, Українська мова Літературне читання Шевченко, Ранок
 
καστράτο η πιο σκύλα γάτα
καστράτο   η πιο σκύλα γάτακαστράτο   η πιο σκύλα γάτα
καστράτο η πιο σκύλα γάτα
 
ισοβίτης γιατρέ έχω ένα βάρος
ισοβίτης   γιατρέ έχω ένα βάροςισοβίτης   γιατρέ έχω ένα βάρος
ισοβίτης γιατρέ έχω ένα βάρος
 

Viewers also liked

XAF and DevExtreme frameworks by DevExpress
XAF and DevExtreme frameworks by DevExpressXAF and DevExtreme frameworks by DevExpress
XAF and DevExtreme frameworks by DevExpressDmitri Artamonov
 
Wzorce Repository, Unity of Work, Devexpress MVC w architekturze Asp.net MVC
Wzorce Repository, Unity of Work, Devexpress MVC  w architekturze Asp.net MVCWzorce Repository, Unity of Work, Devexpress MVC  w architekturze Asp.net MVC
Wzorce Repository, Unity of Work, Devexpress MVC w architekturze Asp.net MVC
Quick-Solution
 
Навыки .NET-разработчика глазами зарубежных и российских работодателей
Навыки .NET-разработчика глазами зарубежных и российских работодателейНавыки .NET-разработчика глазами зарубежных и российских работодателей
Навыки .NET-разработчика глазами зарубежных и российских работодателей
SkillsWiki
 
Maryland's Community Development Budget
Maryland's Community Development BudgetMaryland's Community Development Budget
Maryland's Community Development Budget
Kali Schumitz
 
Save Repository From Save
Save Repository From SaveSave Repository From Save
Save Repository From Save
Norbert Orzechowicz
 
Model View ViewModel
Model View ViewModelModel View ViewModel
Model View ViewModelDoncho Minkov
 
05 standard cost &amp; flexible budget
05 standard cost &amp; flexible budget05 standard cost &amp; flexible budget
05 standard cost &amp; flexible budget
pop Jaturong
 
1° Sessione Oracle CRUI: Analytics Data Lab, the power of Big Data Investiga...
1° Sessione Oracle CRUI: Analytics Data Lab,  the power of Big Data Investiga...1° Sessione Oracle CRUI: Analytics Data Lab,  the power of Big Data Investiga...
1° Sessione Oracle CRUI: Analytics Data Lab, the power of Big Data Investiga...
Jürgen Ambrosi
 
Building a 2012 Marketing Budget
Building a 2012 Marketing BudgetBuilding a 2012 Marketing Budget
Building a 2012 Marketing Budget
Jamie Steven
 
Big Data: Introducing BigInsights, IBM's Hadoop- and Spark-based analytical p...
Big Data: Introducing BigInsights, IBM's Hadoop- and Spark-based analytical p...Big Data: Introducing BigInsights, IBM's Hadoop- and Spark-based analytical p...
Big Data: Introducing BigInsights, IBM's Hadoop- and Spark-based analytical p...
Cynthia Saracco
 
Effective Dashboard Design: Why Your Baby is Ugly
Effective Dashboard Design: Why Your Baby is UglyEffective Dashboard Design: Why Your Baby is Ugly
Effective Dashboard Design: Why Your Baby is Ugly
Aaron Hursman
 
[Webinar] 7 Reasons to Change Your Budgeting & Forecasting Process
[Webinar] 7 Reasons to Change Your Budgeting & Forecasting Process[Webinar] 7 Reasons to Change Your Budgeting & Forecasting Process
[Webinar] 7 Reasons to Change Your Budgeting & Forecasting Process
Jedox
 
E book Microsoft Dynamics CRM 2013 Personal Dashboard for End Users
E book Microsoft Dynamics CRM 2013 Personal Dashboard for End UsersE book Microsoft Dynamics CRM 2013 Personal Dashboard for End Users
E book Microsoft Dynamics CRM 2013 Personal Dashboard for End Users
Aileen Gusni
 
Touchpoint Dashboard Journey Mapping Guide 2014
Touchpoint Dashboard Journey Mapping Guide 2014Touchpoint Dashboard Journey Mapping Guide 2014
Touchpoint Dashboard Journey Mapping Guide 2014
Touchpoint Dashboard
 
Participatory Budgeting Overview
Participatory Budgeting OverviewParticipatory Budgeting Overview
Participatory Budgeting Overview
Ryan Smith
 

Viewers also liked (15)

XAF and DevExtreme frameworks by DevExpress
XAF and DevExtreme frameworks by DevExpressXAF and DevExtreme frameworks by DevExpress
XAF and DevExtreme frameworks by DevExpress
 
Wzorce Repository, Unity of Work, Devexpress MVC w architekturze Asp.net MVC
Wzorce Repository, Unity of Work, Devexpress MVC  w architekturze Asp.net MVCWzorce Repository, Unity of Work, Devexpress MVC  w architekturze Asp.net MVC
Wzorce Repository, Unity of Work, Devexpress MVC w architekturze Asp.net MVC
 
Навыки .NET-разработчика глазами зарубежных и российских работодателей
Навыки .NET-разработчика глазами зарубежных и российских работодателейНавыки .NET-разработчика глазами зарубежных и российских работодателей
Навыки .NET-разработчика глазами зарубежных и российских работодателей
 
Maryland's Community Development Budget
Maryland's Community Development BudgetMaryland's Community Development Budget
Maryland's Community Development Budget
 
Save Repository From Save
Save Repository From SaveSave Repository From Save
Save Repository From Save
 
Model View ViewModel
Model View ViewModelModel View ViewModel
Model View ViewModel
 
05 standard cost &amp; flexible budget
05 standard cost &amp; flexible budget05 standard cost &amp; flexible budget
05 standard cost &amp; flexible budget
 
1° Sessione Oracle CRUI: Analytics Data Lab, the power of Big Data Investiga...
1° Sessione Oracle CRUI: Analytics Data Lab,  the power of Big Data Investiga...1° Sessione Oracle CRUI: Analytics Data Lab,  the power of Big Data Investiga...
1° Sessione Oracle CRUI: Analytics Data Lab, the power of Big Data Investiga...
 
Building a 2012 Marketing Budget
Building a 2012 Marketing BudgetBuilding a 2012 Marketing Budget
Building a 2012 Marketing Budget
 
Big Data: Introducing BigInsights, IBM's Hadoop- and Spark-based analytical p...
Big Data: Introducing BigInsights, IBM's Hadoop- and Spark-based analytical p...Big Data: Introducing BigInsights, IBM's Hadoop- and Spark-based analytical p...
Big Data: Introducing BigInsights, IBM's Hadoop- and Spark-based analytical p...
 
Effective Dashboard Design: Why Your Baby is Ugly
Effective Dashboard Design: Why Your Baby is UglyEffective Dashboard Design: Why Your Baby is Ugly
Effective Dashboard Design: Why Your Baby is Ugly
 
[Webinar] 7 Reasons to Change Your Budgeting & Forecasting Process
[Webinar] 7 Reasons to Change Your Budgeting & Forecasting Process[Webinar] 7 Reasons to Change Your Budgeting & Forecasting Process
[Webinar] 7 Reasons to Change Your Budgeting & Forecasting Process
 
E book Microsoft Dynamics CRM 2013 Personal Dashboard for End Users
E book Microsoft Dynamics CRM 2013 Personal Dashboard for End UsersE book Microsoft Dynamics CRM 2013 Personal Dashboard for End Users
E book Microsoft Dynamics CRM 2013 Personal Dashboard for End Users
 
Touchpoint Dashboard Journey Mapping Guide 2014
Touchpoint Dashboard Journey Mapping Guide 2014Touchpoint Dashboard Journey Mapping Guide 2014
Touchpoint Dashboard Journey Mapping Guide 2014
 
Participatory Budgeting Overview
Participatory Budgeting OverviewParticipatory Budgeting Overview
Participatory Budgeting Overview
 

Similar to MVVM в WinForms – DevExpress Way (теория и практика)

Moxy. Как правильно пользоваться? / Юрий Шмаков (Arello Mobile)
Moxy. Как правильно пользоваться? / Юрий Шмаков (Arello Mobile)Moxy. Как правильно пользоваться? / Юрий Шмаков (Arello Mobile)
Moxy. Как правильно пользоваться? / Юрий Шмаков (Arello Mobile)
Ontico
 
Yii2
Yii2Yii2
Yii2
Noveo
 
Разработка WPF приложений в стиле ViewModel First
Разработка WPF приложений в стиле ViewModel FirstРазработка WPF приложений в стиле ViewModel First
Разработка WPF приложений в стиле ViewModel First
Denis Tsvettsih
 
Viper - чистая архитектура iOS-приложения (И. Чирков)
Viper - чистая архитектура iOS-приложения (И. Чирков)Viper - чистая архитектура iOS-приложения (И. Чирков)
Viper - чистая архитектура iOS-приложения (И. Чирков)
65apps
 
Паттерны быстрой разработки WPF MVVM бизнес-приложений
Паттерны быстрой разработки WPF MVVM бизнес-приложенийПаттерны быстрой разработки WPF MVVM бизнес-приложений
Паттерны быстрой разработки WPF MVVM бизнес-приложенийGoSharp
 
Школа-студия разработки приложений для iOS. 2 лекция. MVC, View, Controllers
Школа-студия разработки приложений для iOS. 2 лекция. MVC, View, ControllersШкола-студия разработки приложений для iOS. 2 лекция. MVC, View, Controllers
Школа-студия разработки приложений для iOS. 2 лекция. MVC, View, ControllersГлеб Тарасов
 
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 1)
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 1)ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 1)
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 1)ZFConf Conference
 
Референсная архитектура приложения на ASP.NET MVC
Референсная архитектура приложения на ASP.NET MVCРеференсная архитектура приложения на ASP.NET MVC
Референсная архитектура приложения на ASP.NET MVCAndrew Mayorov
 
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...
Denis Tsvettsih
 
"Погружение в Robolectric" Дмитрий Костырев (Avito)
"Погружение в Robolectric"  Дмитрий Костырев (Avito)"Погружение в Robolectric"  Дмитрий Костырев (Avito)
"Погружение в Robolectric" Дмитрий Костырев (Avito)
AvitoTech
 
Курсы по мобильной разработке. 2 лекция. Построение интерфейсов в iOS
Курсы по мобильной разработке. 2 лекция. Построение интерфейсов в iOSКурсы по мобильной разработке. 2 лекция. Построение интерфейсов в iOS
Курсы по мобильной разработке. 2 лекция. Построение интерфейсов в iOSГлеб Тарасов
 
ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET
ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NETASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET
ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET
Dev2Dev
 
AndroidMVPHelper
AndroidMVPHelperAndroidMVPHelper
AndroidMVPHelper
DataArt
 
Android service
Android serviceAndroid service
Android service
Kirill Rozov
 
Антон Валюх - Использование паттерна Mvvm в android
Антон Валюх - Использование паттерна Mvvm в androidАнтон Валюх - Использование паттерна Mvvm в android
Антон Валюх - Использование паттерна Mvvm в android
DataArt
 
C# Web. Занятие 11.
C# Web. Занятие 11.C# Web. Занятие 11.
C# Web. Занятие 11.
Igor Shkulipa
 
Веселая ферма. Соседи.
Веселая ферма. Соседи.Веселая ферма. Соседи.
Веселая ферма. Соседи.
Doomer Samoiloff
 
My batis
My batisMy batis
MVP, Moxy. Как правильно пользоваться
MVP, Moxy. Как правильно пользоватьсяMVP, Moxy. Как правильно пользоваться
MVP, Moxy. Как правильно пользоваться
Yuri Shmakov
 
Как навести порядок в коде вашего web-приложения, Андрей Чебукин
Как навести порядок в коде вашего web-приложения, Андрей Чебукин Как навести порядок в коде вашего web-приложения, Андрей Чебукин
Как навести порядок в коде вашего web-приложения, Андрей Чебукин
Sigma Software
 

Similar to MVVM в WinForms – DevExpress Way (теория и практика) (20)

Moxy. Как правильно пользоваться? / Юрий Шмаков (Arello Mobile)
Moxy. Как правильно пользоваться? / Юрий Шмаков (Arello Mobile)Moxy. Как правильно пользоваться? / Юрий Шмаков (Arello Mobile)
Moxy. Как правильно пользоваться? / Юрий Шмаков (Arello Mobile)
 
Yii2
Yii2Yii2
Yii2
 
Разработка WPF приложений в стиле ViewModel First
Разработка WPF приложений в стиле ViewModel FirstРазработка WPF приложений в стиле ViewModel First
Разработка WPF приложений в стиле ViewModel First
 
Viper - чистая архитектура iOS-приложения (И. Чирков)
Viper - чистая архитектура iOS-приложения (И. Чирков)Viper - чистая архитектура iOS-приложения (И. Чирков)
Viper - чистая архитектура iOS-приложения (И. Чирков)
 
Паттерны быстрой разработки WPF MVVM бизнес-приложений
Паттерны быстрой разработки WPF MVVM бизнес-приложенийПаттерны быстрой разработки WPF MVVM бизнес-приложений
Паттерны быстрой разработки WPF MVVM бизнес-приложений
 
Школа-студия разработки приложений для iOS. 2 лекция. MVC, View, Controllers
Школа-студия разработки приложений для iOS. 2 лекция. MVC, View, ControllersШкола-студия разработки приложений для iOS. 2 лекция. MVC, View, Controllers
Школа-студия разработки приложений для iOS. 2 лекция. MVC, View, Controllers
 
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 1)
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 1)ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 1)
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 1)
 
Референсная архитектура приложения на ASP.NET MVC
Референсная архитектура приложения на ASP.NET MVCРеференсная архитектура приложения на ASP.NET MVC
Референсная архитектура приложения на ASP.NET MVC
 
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...
Объять необъятное, или как использовать несколько MVVM фреймворков в одном XA...
 
"Погружение в Robolectric" Дмитрий Костырев (Avito)
"Погружение в Robolectric"  Дмитрий Костырев (Avito)"Погружение в Robolectric"  Дмитрий Костырев (Avito)
"Погружение в Robolectric" Дмитрий Костырев (Avito)
 
Курсы по мобильной разработке. 2 лекция. Построение интерфейсов в iOS
Курсы по мобильной разработке. 2 лекция. Построение интерфейсов в iOSКурсы по мобильной разработке. 2 лекция. Построение интерфейсов в iOS
Курсы по мобильной разработке. 2 лекция. Построение интерфейсов в iOS
 
ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET
ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NETASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET
ASP.NET MVC за пределами Hello World. Дятлов Александр D2D Just.NET
 
AndroidMVPHelper
AndroidMVPHelperAndroidMVPHelper
AndroidMVPHelper
 
Android service
Android serviceAndroid service
Android service
 
Антон Валюх - Использование паттерна Mvvm в android
Антон Валюх - Использование паттерна Mvvm в androidАнтон Валюх - Использование паттерна Mvvm в android
Антон Валюх - Использование паттерна Mvvm в android
 
C# Web. Занятие 11.
C# Web. Занятие 11.C# Web. Занятие 11.
C# Web. Занятие 11.
 
Веселая ферма. Соседи.
Веселая ферма. Соседи.Веселая ферма. Соседи.
Веселая ферма. Соседи.
 
My batis
My batisMy batis
My batis
 
MVP, Moxy. Как правильно пользоваться
MVP, Moxy. Как правильно пользоватьсяMVP, Moxy. Как правильно пользоваться
MVP, Moxy. Как правильно пользоваться
 
Как навести порядок в коде вашего web-приложения, Андрей Чебукин
Как навести порядок в коде вашего web-приложения, Андрей Чебукин Как навести порядок в коде вашего web-приложения, Андрей Чебукин
Как навести порядок в коде вашего web-приложения, Андрей Чебукин
 

More from GoSharp

TPL Dataflow – зачем и для кого?
TPL Dataflow – зачем и для кого?TPL Dataflow – зачем и для кого?
TPL Dataflow – зачем и для кого?
GoSharp
 
Живые приложения с Rx
Живые приложения с RxЖивые приложения с Rx
Живые приложения с Rx
GoSharp
 
Anemic Domain Model - антипаттерн или SOLID?
Anemic Domain Model - антипаттерн или SOLID?Anemic Domain Model - антипаттерн или SOLID?
Anemic Domain Model - антипаттерн или SOLID?
GoSharp
 
Эволюция пользовательского интерфейса бизнес-приложений: от DOSa через окна в...
Эволюция пользовательского интерфейса бизнес-приложений: от DOSa через окна в...Эволюция пользовательского интерфейса бизнес-приложений: от DOSa через окна в...
Эволюция пользовательского интерфейса бизнес-приложений: от DOSa через окна в...
GoSharp
 
UniversalApp "убийца" WPF или же это WPF+ ?
UniversalApp "убийца" WPF или же это WPF+ ?UniversalApp "убийца" WPF или же это WPF+ ?
UniversalApp "убийца" WPF или же это WPF+ ?
GoSharp
 
UI тестирование WPF приложений в Дойче Банке
UI тестирование WPF приложений в Дойче БанкеUI тестирование WPF приложений в Дойче Банке
UI тестирование WPF приложений в Дойче Банке
GoSharp
 
Практика применения Enterprise Architect и T4-шаблонов для разработки системы...
Практика применения Enterprise Architect и T4-шаблонов для разработки системы...Практика применения Enterprise Architect и T4-шаблонов для разработки системы...
Практика применения Enterprise Architect и T4-шаблонов для разработки системы...
GoSharp
 
За что не любить EF и чем его заменить
За что не любить EF и чем его заменитьЗа что не любить EF и чем его заменить
За что не любить EF и чем его заменить
GoSharp
 
Gosharp Intro
Gosharp IntroGosharp Intro
Gosharp Intro
GoSharp
 
Проектирование сетевой инфраструктуры под SOA проекты ASP.NET
Проектирование сетевой инфраструктуры под SOA проекты ASP.NETПроектирование сетевой инфраструктуры под SOA проекты ASP.NET
Проектирование сетевой инфраструктуры под SOA проекты ASP.NET
GoSharp
 
Мониторинг приложений ASP.NET на основе сервиса Application Insights
Мониторинг приложений ASP.NET на основе сервиса Application InsightsМониторинг приложений ASP.NET на основе сервиса Application Insights
Мониторинг приложений ASP.NET на основе сервиса Application Insights
GoSharp
 
Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NET
Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NETОпыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NET
Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NET
GoSharp
 
ASP.NET Internals
ASP.NET InternalsASP.NET Internals
ASP.NET Internals
GoSharp
 
Кросплатформенная разработка на ASP.NET vNext
Кросплатформенная разработка на ASP.NET vNextКросплатформенная разработка на ASP.NET vNext
Кросплатформенная разработка на ASP.NET vNext
GoSharp
 
Внедрение зависимостей в ASP.NET MVС и ASP.NET vNext
Внедрение зависимостей в ASP.NET MVС и ASP.NET vNextВнедрение зависимостей в ASP.NET MVС и ASP.NET vNext
Внедрение зависимостей в ASP.NET MVС и ASP.NET vNext
GoSharp
 
Будущее ASP.NET
Будущее ASP.NETБудущее ASP.NET
Будущее ASP.NET
GoSharp
 
Коучинг команд разработки и коучинговые инструменты в работе тимлида
Коучинг команд разработки и коучинговые инструменты в работе тимлидаКоучинг команд разработки и коучинговые инструменты в работе тимлида
Коучинг команд разработки и коучинговые инструменты в работе тимлида
GoSharp
 
Взаимное влияние Source Code Management и других средств организации разработки
Взаимное влияние Source Code Management и других средств организации разработкиВзаимное влияние Source Code Management и других средств организации разработки
Взаимное влияние Source Code Management и других средств организации разработки
GoSharp
 
DevOPS инструменты для .NET проектов
DevOPS инструменты для .NET проектовDevOPS инструменты для .NET проектов
DevOPS инструменты для .NET проектов
GoSharp
 
Доски проектов и продуктов на TFS: Agile-визуализация на уровне компании
Доски проектов и продуктов на TFS: Agile-визуализация на уровне компанииДоски проектов и продуктов на TFS: Agile-визуализация на уровне компании
Доски проектов и продуктов на TFS: Agile-визуализация на уровне компании
GoSharp
 

More from GoSharp (20)

TPL Dataflow – зачем и для кого?
TPL Dataflow – зачем и для кого?TPL Dataflow – зачем и для кого?
TPL Dataflow – зачем и для кого?
 
Живые приложения с Rx
Живые приложения с RxЖивые приложения с Rx
Живые приложения с Rx
 
Anemic Domain Model - антипаттерн или SOLID?
Anemic Domain Model - антипаттерн или SOLID?Anemic Domain Model - антипаттерн или SOLID?
Anemic Domain Model - антипаттерн или SOLID?
 
Эволюция пользовательского интерфейса бизнес-приложений: от DOSa через окна в...
Эволюция пользовательского интерфейса бизнес-приложений: от DOSa через окна в...Эволюция пользовательского интерфейса бизнес-приложений: от DOSa через окна в...
Эволюция пользовательского интерфейса бизнес-приложений: от DOSa через окна в...
 
UniversalApp "убийца" WPF или же это WPF+ ?
UniversalApp "убийца" WPF или же это WPF+ ?UniversalApp "убийца" WPF или же это WPF+ ?
UniversalApp "убийца" WPF или же это WPF+ ?
 
UI тестирование WPF приложений в Дойче Банке
UI тестирование WPF приложений в Дойче БанкеUI тестирование WPF приложений в Дойче Банке
UI тестирование WPF приложений в Дойче Банке
 
Практика применения Enterprise Architect и T4-шаблонов для разработки системы...
Практика применения Enterprise Architect и T4-шаблонов для разработки системы...Практика применения Enterprise Architect и T4-шаблонов для разработки системы...
Практика применения Enterprise Architect и T4-шаблонов для разработки системы...
 
За что не любить EF и чем его заменить
За что не любить EF и чем его заменитьЗа что не любить EF и чем его заменить
За что не любить EF и чем его заменить
 
Gosharp Intro
Gosharp IntroGosharp Intro
Gosharp Intro
 
Проектирование сетевой инфраструктуры под SOA проекты ASP.NET
Проектирование сетевой инфраструктуры под SOA проекты ASP.NETПроектирование сетевой инфраструктуры под SOA проекты ASP.NET
Проектирование сетевой инфраструктуры под SOA проекты ASP.NET
 
Мониторинг приложений ASP.NET на основе сервиса Application Insights
Мониторинг приложений ASP.NET на основе сервиса Application InsightsМониторинг приложений ASP.NET на основе сервиса Application Insights
Мониторинг приложений ASP.NET на основе сервиса Application Insights
 
Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NET
Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NETОпыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NET
Опыт разработки сложных клиент-серверных приложений на TypeScript и ASP.NET
 
ASP.NET Internals
ASP.NET InternalsASP.NET Internals
ASP.NET Internals
 
Кросплатформенная разработка на ASP.NET vNext
Кросплатформенная разработка на ASP.NET vNextКросплатформенная разработка на ASP.NET vNext
Кросплатформенная разработка на ASP.NET vNext
 
Внедрение зависимостей в ASP.NET MVС и ASP.NET vNext
Внедрение зависимостей в ASP.NET MVС и ASP.NET vNextВнедрение зависимостей в ASP.NET MVС и ASP.NET vNext
Внедрение зависимостей в ASP.NET MVС и ASP.NET vNext
 
Будущее ASP.NET
Будущее ASP.NETБудущее ASP.NET
Будущее ASP.NET
 
Коучинг команд разработки и коучинговые инструменты в работе тимлида
Коучинг команд разработки и коучинговые инструменты в работе тимлидаКоучинг команд разработки и коучинговые инструменты в работе тимлида
Коучинг команд разработки и коучинговые инструменты в работе тимлида
 
Взаимное влияние Source Code Management и других средств организации разработки
Взаимное влияние Source Code Management и других средств организации разработкиВзаимное влияние Source Code Management и других средств организации разработки
Взаимное влияние Source Code Management и других средств организации разработки
 
DevOPS инструменты для .NET проектов
DevOPS инструменты для .NET проектовDevOPS инструменты для .NET проектов
DevOPS инструменты для .NET проектов
 
Доски проектов и продуктов на TFS: Agile-визуализация на уровне компании
Доски проектов и продуктов на TFS: Agile-визуализация на уровне компанииДоски проектов и продуктов на TFS: Agile-визуализация на уровне компании
Доски проектов и продуктов на TFS: Agile-визуализация на уровне компании
 

MVVM в WinForms – DevExpress Way (теория и практика)

  • 1. MVVM в WinForms DEVEXPRESS WAY dmitry.garavsky@devexpress.com
  • 2. MVVM? А зачем оно надо? ”When capabilities are extended, the codebase should become smaller”. @Thinking Out Loud Четкое разделение бизнес-логики и логики представления. Отсюда все вытекающие бенефиты и профиты.
  • 3. MVVM в WPF. Типичная схема... View  View   Model   Business Logic and Data View  Model   Presentation Logic Data binding Commands Notifications
  • 4. … и типичные проблемы:   реализация уведомленийреализация командограничения механизма привязкиреализация механизма сервисов RelayCommand BooleanToVisibilityConverer DataContext ServiceContainer
  • 5. Зачем нам свой MVVM Framework? ●  Стандартные  конвертеры,  сервисы  и   поведения   ●  Мощный  и  гибкий  механизм  POCO   ●  Полная  поддержка  со  стороны  DX  контролов   (MVVM-­‐driven  design)    
  • 6. MVVM в WinForms. Попробуем? View  View   Model   Business Logic and Data View  Model   Presentation Logic Data binding Commands Notifications Code-behind Events Methods
  • 7. class  AccountCollectionViewPresenter  {          public  CollectionViewPresenter(GridView  gridView,  AccountCollectionViewModel  viewModel)  {                  gridView.FocusedRowObjectChanged  +=  (s,  e)  =>  {                          viewModel.SelectedEntity  =  e.Row  as  Model.Account;                  };                  ((INotifyPropertyChanged)viewModel).PropertyChanged  +=  (s,  e)  =>  {                          if(e.PropertyName  ==  "SelectedEntity")  {                                  var  entity  =  viewModel.SelectedEntity;                                  if(entity  !=  null)                                          gridView.FocusedRowHandle  =  gridView.LocateByValue("Id",  entity.ID);                                  else                                          gridView.FocusedRowHandle  =  GridControl.InvalidRowHandle;                          }                  };          }   }   От MVVM к MVPVM. Presenter. Выделяем  узконаправленный  код  в  специализированные  классы  
  • 8. class  AccountCollectionViewPresenter  {          public  CollectionViewPresenter(GridView  gridView,  AccountCollectionViewModel  viewModel)  {                  gridView.FocusedRowObjectChanged  +=  (s,  e)  =>  {                          viewModel.SelectedEntity  =  e.Row  as  Model.Account;                  };                  ((INotifyPropertyChanged)viewModel).PropertyChanged  +=  (s,  e)  =>  {                          if(e.PropertyName  ==  "SelectedEntity")  {                                  var  entity  =  viewModel.SelectedEntity;                                  if(entity  !=  null)                                          gridView.FocusedRowHandle  =  gridView.LocateByValue("Id",  entity.ID);                                  else                                          gridView.FocusedRowHandle  =  GridControl.InvalidRowHandle;                          }                  };          }   }   gridView.FocusedRowObjectChanged  +=  (s,  e)  =>  {        viewModel.SelectedEntity  =  e.Row  as  Model.Account;   }; От MVVM к MVPVM. Presenter. Выделяем  узконаправленный  код  в  специализированные  классы  
  • 9. WinForms - Presenter повсюду. ●  User  Control  и  его  Code-­‐Behind   ●  Отдельный  класс   ●  Отдельный  метод  для  настройки   контрола   ●  Специфичный  кусок  Code-­‐Behind   ●  Обработчик  события   ●  Привязка(Binding)  
  • 10. •  Привязки  к  данным.   •  Команды  и  привязки  к  командам.   •  Поведения  и  сервисы.   •  Бонус  –  удобный  механизм  реализации   уведомлений,  зависимостей,  команд   MVPVM без буквы “P”. больше  удобства,  меньше  кода  
  • 11. Уведомления об изменении public  class  LoginViewModel  :  INotifyPropertyChanged  {          public  event  PropertyChangedEventHandler  PropertyChanged;          string  userNameCore;          public  string  UserName  {                  get  {  return  userNameCore;  }                  set  {                             if(userNameCore  ==  value)  return;                          this.userNameCore  =  value;                          PropertyChangedEventHandler  handler  =  PropertyChanged;                          if(handler  !=  null)                                  handler(this,  new  PropertyChangedEventArgs("UserName"));                                          }          }   }   дать возможность стороннему наблюдателю узнать об изменении значения свойства или состояния целевого объекта
  • 12. Уведомления об изменении public  class  LoginViewModel  :  INotifyPropertyChanged  {          public  event  PropertyChangedEventHandler  PropertyChanged;          string  userNameCore;          public  string  UserName  {                  get  {  return  userNameCore;  }                  set  {                          if(value  !=  userNameCore)  {                                  this.userNameCore  =  value;                                  PropertyChangedEventHandler  handler  =  PropertyChanged;                                  if(handler  !=  null)                                          handler(this,  new  PropertyChangedEventArgs("UserName"));                          }                  }          }   }   public  class  LoginViewModel  :  BindableBase  {          string  userNameCore;          public  string  UserName  {                  get  {  return  userNameCore;  }                  set  {  SetProperty(ref  userNameCore,  "UserName");}          }   }  
  • 13. Уведомления об изменении public  class  LoginViewModel  :  INotifyPropertyChanged  {          public  event  PropertyChangedEventHandler  PropertyChanged;          string  userNameCore;          public  string  UserName  {                  get  {  return  userNameCore;  }                  set  {                          if(value  !=  userNameCore)  {                                  this.userNameCore  =  value;                                  PropertyChangedEventHandler  handler  =  PropertyChanged;                                  if(handler  !=  null)                                          handler(this,  new  PropertyChangedEventArgs("UserName"));                          }                  }          }   }   POCO-ViewModel: public  class  LoginViewModel  {          public  virtual  string  UserName  {  get;  set;  }   } Plain Old Clr Object
  • 14. POCO-трансформация ???  POCO-­‐class   Full-­‐featured   ViewModel   DevExpress.Mvvm.POCO.ViewModelSource  
  • 15. Уведомления об изменении public  class  LoginViewModel  :  INotifyPropertyChanged  {          public  event  PropertyChangedEventHandler  PropertyChanged;          string  userNameCore;          public  string  UserName  {                  get  {  return  userNameCore;  }                  set  {                          if(value  !=  userNameCore)  {                                  this.userNameCore  =  value;                                  PropertyChangedEventHandler  handler  =  PropertyChanged;                                  if(handler  !=  null)                                          handler(this,  new  PropertyChangedEventArgs("UserName"));                          }                  }          }   }   POCO-ViewModel: public  class  LoginViewModel  {          public  virtual  string  UserName  {  get;  set;  }   }
  • 16. Как это использовать? XAML: <UserControl  x:Class="DXPOCO.Views.LoginView"          xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm"          xmlns:ViewModels="clr-­‐namespace:DXPOCO.ViewModels"          DataContext="{dxmvvm:ViewModelSource  Type=ViewModels:LoginViewModel}"          ...>   </UserControl>   WinForms Code-behind: public  LoginViewModel  ViewModel  {          get  {  return  mvvmContext.GetViewModel<LoginViewModel>();  }   }  
  • 17. Зависимости свойств public  class  LoginViewModel  :  INotifyPropertyChanged  {          public  event  PropertyChangedEventHandler  PropertyChanged;          string  userNameCore;          public  string  UserName  {                  get  {  return  userNameCore;  }                  set  {                             if(userNameCore  ==  value)  return;                          this.userNameCore  =  value;                          PropertyChangedEventHandler  handler  =  PropertyChanged;                          if(handler  !=  null)                                  handler(this,  new  PropertyChangedEventArgs("UserName"));                             OnUserNameChanged();  //  OnUserNameChanged(oldValue)                  }          }   }   дать возможность явно уведомить сторонних наблюдателей об изменении значения свойства или состояния целевого объекта
  • 18. Зависимости свойств public  class  LoginViewModel  :  INotifyPropertyChanged  {          public  event  PropertyChangedEventHandler  PropertyChanged;          string  userNameCore;          public  string  UserName  {                  get  {  return  userNameCore;  }                  set  {                          if(value  !=  userNameCore)  {                                  this.userNameCore  =  value;                                  PropertyChangedEventHandler  handler  =  PropertyChanged;                                  if(handler  !=  null)                                          handler(this,  new  PropertyChangedEventArgs("UserName"));                          }                  }          }   }   public  class  LoginViewModel  :  BindableBase  {          string  userNameCore;          public  string  UserName  {                  get  {  return  userNameCore;  }                  set  {                            SetProperty(ref  userNameCore,                                  "UserName",  OnUserNameChanged);                  }          }   }  
  • 19. Зависимости свойств public  class  LoginViewModel  :  INotifyPropertyChanged  {          public  event  PropertyChangedEventHandler  PropertyChanged;          string  userNameCore;          public  string  UserName  {                  get  {  return  userNameCore;  }                  set  {                          if(value  !=  userNameCore)  {                                  this.userNameCore  =  value;                                  PropertyChangedEventHandler  handler  =  PropertyChanged;                                  if(handler  !=  null)                                          handler(this,  new  PropertyChangedEventArgs("UserName"));                          }                  }          }   }   POCO-ViewModel: public  class  LoginViewModel  {          public  virtual  string  UserName  {  get;  set;  }          protected  void  OnUserNameChanged()  {/*...*/}   }
  • 20. Ручное обновление зависимостей public  class  LoginViewModel  :  INotifyPropertyChanged  {          public  event  PropertyChangedEventHandler  PropertyChanged;          string  userNameCore;          public  string  UserName  {                  get  {  return  userNameCore;  }                  set  {                             if(userNameCore  ==  value)  return;                          this.userNameCore  =  value;                          PropertyChangedEventHandler  handler  =  PropertyChanged;                          if(handler  !=  null)                                  handler(this,  new  PropertyChangedEventArgs("UserName"));                             OnUserNameChanged();                  }          }   }   PropertyChangedEventHandler  handler  =  PropertyChanged;   if(handler  !=  null)          handler(this,  new  PropertyChangedEventArgs("UserName"));  
  • 21. Ручное обновление зависимостей public  class  LoginViewModel  :  INotifyPropertyChanged  {          public  event  PropertyChangedEventHandler  PropertyChanged;          string  userNameCore;          public  string  UserName  {                  get  {  return  userNameCore;  }                  set  {                          if(value  !=  userNameCore)  {                                  this.userNameCore  =  value;                                  PropertyChangedEventHandler  handler  =  PropertyChanged;                                  if(handler  !=  null)                                          handler(this,  new  PropertyChangedEventArgs("UserName"));                          }                  }          }   }   POCO-ViewModel: //  Extension  method   this.RaisePropertyChanged(x  =>  x.UserName);  
  • 22. Как это использовать? XAML:          <dxe:TextEdit  Text="{Binding  UserName}"/>     WinForms Code-behind (MVVMContext API):   mvvmContext.SetBinding(userNameTextEdit,          edit  =>  edit.EditValue,  "UserName");   //  ...   var  fluentAPI  =  mvvmContext.OfType<LoginViewModel>();   fluentAPI.SetBinding(lblUserName,          lbl  =>  lbl.Text,  x  =>  x.UserName);  
  • 23. Команды   public  class  DelegateCommand<T>  :  System.Windows.Input.ICommand  {             readonly  Predicate<T>  _canExecute;             readonly  Action<T>  _execute;             public  DelegateCommand(Action<T>  execute)                     :  this(execute,  null)  {             }             public  DelegateCommand(Action<T>  execute,  Predicate<T>  canExecute)  {                     _execute  =  execute;                     _canExecute  =  canExecute;             }             //...     }   public  class  MyViewModel  {          public  MyViewModel()  {                  this.SayHelloCommand  =  new  DelegateCommand(SayHello);          }          public  System.Windows.Input.ICommand  SayHelloCommand  {                  get;                  private  set;          }          public  void  SayHello()  {  /*  do  something  */  }   }   дать возможность инкапсулировать действие в отдельном объекте
  • 24. Команды   public  class  DelegateCommand<T>  :  System.Windows.Input.ICommand  {             readonly  Predicate<T>  _canExecute;             readonly  Action<T>  _execute;             public  DelegateCommand(Action<T>  execute)                     :  this(execute,  null)  {             }             public  DelegateCommand(Action<T>  execute,  Predicate<T>  canExecute)  {                     _execute  =  execute;                     _canExecute  =  canExecute;             }             //...     }   POCO-ViewModel: public  class  MyViewModel  {          public  void  SayHello()  {  /*  do  something  */  }   }   POCO-ViewModel: public  class  MyViewModel  {          public  void  SaySomething(string  str)  {                    /*  ...  */            }          public  bool  CanSaySomething(string  str)  {                    /*  ...  */            }   }   POCO-ViewModel: public  class  MyViewModel  {          public  Task  DoSomething  ()  {                    /*  asynchronous  !!!  */            }   }  
  • 25. Как это использовать? XAML:   <Button  Command="{Binding  SayHello}"  />     <Button  Command="{Binding  Say}"  CommandParameter="Hello!"/>    
  • 26. Как это использовать? WinForms Code-behind (MVVMContext API): public  MyViewModel  ViewModel  {          get  {  return  mvvmContext.GetViewModel<MyViewModel>();  }   }   //   btnSayHello.BindCommand(        ()  =>  ViewModel.SayHello(),  ViewModel);   btnSay.BindCommand(        ()  =>  ViewModel.Say(null),  ViewModel,  ()  =>  ViewModel.Name);   WinForms Code-behind (MVVMContext Fluent API): var  fluentApi  =  mvvmContext.OfType<MyViewModel>();   fluentApi.BindCommand(x  =>  x.SayHello());   fluentApi.BindCommand((x,s)  =>  x.Say(s),  x  =>  x.Name);  
  • 27. Интерактивность. Сервисы. ViewModel: public  class  MyViewModel  {        protected  IMessageBoxService  MessageBoxService  {                get  {  /*  ...  */  }        }        public  void  SayHello()  {                MessageBoxService.Show("Hello!");          } } дать возможность взаимодействовать с пользователем не нарушая концепцию разделения слоев
  • 28. POCO ViewModel: protected  IMessageBoxService  MessageBoxService  {          get  {  this.GetService<IMessageBoxService>();  }   }     protected  virtual  IMessageBoxService  MessageBoxService  {          get  {  throw  new  System.NotImplementedException();  }   }   Интерактивность. Сервисы.
  • 29. Как это использовать? WinForms Code-behind (MVVMContext API): mvvmContext.RegisterService(new  MessageBoxService());   //...   var  mbService  =  mvvmContext.GetService<IMessageBoxService>();   mbService.Show("Something  happens!");  
  • 30. public  class  ConfirmationBehavior          :  ConfirmationBehavior<FormClosingEventArgs>  {        public  ConfirmationBehavior()  :  base("FormClosing")  {  }        protected  override  string  GetConfirmationCaption()  {                  return  "Oops!";          }        protected  override  string  GetConfirmationText()  {                return  "Form  will  be  closed.  Are  you  sure?";        }   }   Поведения. WinForms Code-behind (MVVMContext API):   //...   mvvmContext.AttachBehavior<ConfirmationBehavior>(this);     дать возможность наделить объект дополнительным функционалом действуя снаружи объекта
  • 31. Поведения. WinForms Code-behind (MVVMContext Fluent API):   //...   mvvmContext.WithEvent<CancelEventArgs>(this,  "Closing")          .Confirmation(              settings  =>  {                  settings.Caption  =  "Closing  Confirmation";                  settings.Text  =  "Form  will  be  closed.  Press  OK  to  confirm.";                  settings.Buttons  =  ConfirmationButtons.OKCancel;                  settings.ShowQuestionIcon  =  false;              });  
  • 32. Поведения. EventToCommand. public  class  ClickToSayHello  :        EventToCommandBehavior<MyViewModel,  EventArgs>  {        public  ClickToSayHello()          :  base("Click",  x  =>  x.SayHello())  {        }   }   WinForms Code-behind (MVVMContext API):   //...   mvvmContext.AttachBehavior<ClickToSayHello>(thirdPartyButton);  
  • 33. Поведения. EventToCommand. WinForms Code-behind (MVVMContext Fluent API):     mvvmContext.WithEvent<ViewModel,  EventArgs>          (thirdPartyButton,  "Click")          .EventToCommand(x  =>  x.SayHello());    
  • 34. Простое CRUD-приложение Expenses. •  Entity Framework 6.x (Nuget) •  DevExpress MVVM Framework •  DevExpress Scaffolding Wizard •  DevExpress WinForms Controls
  • 35. Структура проекта. Model. public  class  Account  {        public  string  Name  {  get;  set;  }        public  decimal  Amount  {  get;  set;  } }
  • 36. public  class  ExpensesDbContext  :  DbContext  {          static  ExpensesDbContext()  {            Database.SetInitializer<ExpensesDbContext>(                          new  ExpensesDbContextInitializer());          }          public  DbSet<Account>  Accounts  {  get;  set;  }          public  DbSet<Category>  Categories  {  get;  set;  }          public  DbSet<Transaction>  Transactions  {  get;  set;  }   } public  class  Account  {          [Key,  Display(AutoGenerateField  =  false)]          public  int  ID  {  get;  set;  }          [Required,  StringLength(30,  MinimumLength  =  4)]          [Display(Name  =  "ACCOUNT")]          public  string  Name  {  get;  set;  }          [DataType(DataType.Currency)]          [Display(Name  =  "AMOUNT")]          public  decimal  Amount  {  get;  set;  }   }   Добавим Entity Framework
  • 38. Создаем представления. Структура   Типы  View   ●  Контейнер  навигации   (DocumentManager+Ribbon)   ●  Контейнер  коллекции   (XtraGrid)   ●  Контейнер  деталей   (DataLayoutControl)      
  • 40. Привязки и навигация mvvmContext.RegisterService(DocumentManagerService.Create(tabbedView));     var  fluent  =  mvvmContext.OfType<ExpensesDbContextViewModel>();   fluent.BindCommand(biAccounts,  (x,  m)  =>  x.Show(m),  x  =>  x.Modules[0]);   fluent.BindCommand(biCategories,  (x,  m)  =>  x.Show(m),  x  =>  x.Modules[1]);   fluent.BindCommand(biTransactions,  (x,  m)  =>  x.Show(m),  x  =>  x.Modules[2]);                     fluent.WithEvent<FormClosingEventArgs>(this,  "FormClosing")          .EventToCommand(x  =>  x.OnClosing(null));    
  • 42. Привязки и поведение var  fluent  =  mvvmContext.OfType<AccountCollectionViewModel>();   fluent.SetBinding(gridView,  v  =>  v.LoadingPanelVisible,  x  =>  x.IsLoading);   fluent.SetBinding(gridControl,  g  =>  g.DataSource,  x  =>  x.Entities);       fluent.WithEvent<ColumnView,  FocusedRowObjectChangedEventArgs>(          gridView,  "FocusedRowObjectChanged")          .SetBinding(x  =>  x.SelectedEntity,                  args  =>  args.Row  as  Model.Account,                  (gView,  entity)  =>  gView.FocusedRowHandle  =  gView.FindRow(entity));   fluent.WithEvent<RowCellClickEventArgs>(gridView,  "RowCellClick")          .EventToCommand(                  x  =>  x.Edit(null),  x  =>  x.SelectedEntity,                  args  =>  (args.Clicks  ==  2)  &&  (args.Button  ==  MouseButtons.Left));  
  • 44. Привязки и поведение var  fluent  =  mvvmContext.OfType<AccountViewModel>();   fluent.SetObjectDataSourceBinding(          bindingSource,  x  =>  x.Entity,  x  =>  x.Update());       fluent.BindCommand(btnSave,  x  =>  x.Save());   fluent.BindCommand(btnReset,  x  =>  x.Reset());    
  • 45. [DataType(DataType.Date)]   [Display(Name  =  "DATE")]   public  DateTime  Date  {  get;  set;  }   [DataType(DataType.Currency)]   [Display(Name  =  "AMOUNT")]   public  decimal  Amount  {  get;  set;  }   [DataType(DataType.MultilineText)]   [Display(Name  =  "COMMENT")]   public  string  Comment  {  get;  set;  }   [Required,  StringLength(30,  MinimumLength  =  4)] [Display(Name  =  "ACCOUNT")] public  string  Name  {  get;  set;  } Полезные плюшки «из коробки» Поддержка  аннотационных  аттрибутов  
  • 46. Полезные плюшки «из коробки» Автоматическая  привязка  команд  в  дизайнере  
  • 47. Полезные плюшки «из коробки» Регистрация  сервисов  в  дизайнере  
  • 48. MessageBoxService - выбор типа (стандартный, скинированный, Flyout): DevExpress.Utils.MVVM.MVVMContext.RegisterFlyoutMessageBoxService();   DocumentManagerService - управление навигацией (обработка события QueryControl): tabbedView.QueryControl  +=  (s,  e)  =>  {          if(e.Document.ControlName  ==  "AccountCollectionView")                  e.Control  =  new  Views.Accounts();   };     Полезные плюшки «из коробки» Настройка  сервисов  на  уровне  контролов  
  • 49. Подведем итоги •  Бесплатное  и  кроссплатформенное  ядро   (WPF/Silverlight/WinForms/WinRT/UAP)   •  Вся  мощь  системы  привязок   •  Полная  поддержка  процесса  разработки   •  Уверенность  в  положительном   результате  на  любой  платформе   MVVM  подход  +  DevExpress  
  • 51. Материалы Статьи  и  руководства:   1.  DevExpress  MVVM  Framework  and  MVVM(MVPVM)  Architectural  Pa{ern  in   WinForms   2.  MVVM  In  Ac~on:  Expenses  App  -­‐  Displaying  and  naviga~ng  between  DB   Collec~ons(Tutorial  01)   3.  MVVM  In  Ac~on:  Expenses  App  –  Displaying  DB  En~ty  details.  DB  En~ty   proper~es  edi~ng.(Tutorial  02)     Примеры  и  ссылки:   1.  Приложение  Expenses  (ссылка  на  DevExpress  Code  Central)   2.  DevExpress.MVVM.Free  (github)