Вы находитесь на странице: 1из 51

MVVM WinForms

DEVEXPRESS WAY
dmitry.garavsky@devexpress.com
MVVM? ?
When capabilities are extended, the
codebase should become smaller.
@Thinking Out Loud

- .
.
MVVM WPF. ...

Notifications

Data binding
View
View View Model Model
Commands

Presentation Business Logic


Logic and Data
:








n d n ve r e r
C o m m a C o
Relay S oVisibility
l e a e
n T
r v i c e C ontainer
B o o

C o n te xt
Data
MVVM
Framework?
,

POCO

DX
(MVVM-driven design)

MVVM WinForms. ?

Notifications

Data binding
View
View View Model Model
Commands
Events

Methods
Presentation Business Logic
Logic and Data
Code-behind
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;
}
};
}
}
MVVM MVPVM. Presenter.

class AccountCollectionViewPresenter {
public CollectionViewPresenter(GridView gridView, AccountCollectionViewModel viewModel) {
gridView.FocusedRowObjectChanged += (s, e) => {
viewModel.SelectedEntity = e.Row as Model.Account;
};
gridView.FocusedRowObjectChanged += (s, e) => {
((INotifyPropertyChanged)viewModel).PropertyChanged += (s, e) => {
if(e.PropertyName == "SelectedEntity") {
viewModel.SelectedEntity = e.Row as Model.Account;
var entity = viewModel.SelectedEntity;
if(entity != null)
}; gridView.FocusedRowHandle = gridView.LocateByValue("Id", entity.ID);
else
gridView.FocusedRowHandle = GridControl.InvalidRowHandle;
}
};
}
}
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 {
public class LoginViewModel : BindableBase {
get { return userNameCore; }
string userNameCore;
set {
if(value != userNameCore) {
public string UserName {
this.userNameCore = value;
get { return userNameCore; }
PropertyChangedEventHandler handler = PropertyChanged;
set { SetProperty(ref userNameCore, "UserName");}
if(handler != null)
}
handler(this, new PropertyChangedEventArgs("UserName"));
}
}
}
}
}

public class LoginViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
string userNameCore;
public string UserName {
POCO-ViewModel:
get { return userNameCore; }
set {
if(value != userNameCore) {

Plain Old Clr Object


this.userNameCore = value;
public class LoginViewModel {
PropertyChangedEventHandler handler = PropertyChanged;
public virtual string UserName { get; set; }
if(handler != null)

} handler(this, new PropertyChangedEventArgs("UserName"));


}
}
}
}
POCO-

Full-featured
POCO-class
??? ViewModel

DevExpress.Mvvm.POCO.ViewModelSource

public class LoginViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
string userNameCore;
public string UserName {
POCO-ViewModel:
get { return userNameCore; }
set {
if(value != userNameCore) {
this.userNameCore = value;
public class LoginViewModel {
PropertyChangedEventHandler handler = PropertyChanged;
public virtual string UserName { get; set; }
if(handler != null)
handler(this, new PropertyChangedEventArgs("UserName"));
} }
}
}
}
?
XAML:
<UserControl x:Class="DXPOCO.Views.LoginView"
WinForms Code-behind:
xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm"
public LoginViewModel ViewModel {
xmlns:ViewModels="clr-namespace:DXPOCO.ViewModels"
get { return mvvmContext.GetViewModel<LoginViewModel>(); }
}
DataContext="{dxmvvm:ViewModelSource Type=ViewModels:LoginViewModel}"
...>
</UserControl>

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 class LoginViewModel : BindableBase {
public string UserName {
string userNameCore;
get { return userNameCore; }
public string UserName {
set {
get { return userNameCore; }
if(value != userNameCore) {
this.userNameCore = value;
set {
PropertyChangedEventHandler handler = PropertyChanged;
SetProperty(ref userNameCore,
if(handler != null)
"UserName", OnUserNameChanged);
handler(this, new PropertyChangedEventArgs("UserName"));
}
}
}
}
}
}
}

public class LoginViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
string userNameCore;
public string UserName {
POCO-ViewModel:
get { return userNameCore; }
set {
if(value != userNameCore) {
this.userNameCore = value;
public class LoginViewModel {
PropertyChangedEventHandler handler = PropertyChanged;
public virtual string UserName { get; set; }
if(handler != null)
handler(this, new PropertyChangedEventArgs("UserName"));
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;
PropertyChangedEventHandler handler = PropertyChanged;
if(handler != null)
if(handler != null)
handler(this, new PropertyChangedEventArgs("UserName"));
OnUserNameChanged();
handler(this, new PropertyChangedEventArgs("UserName"));
}
}
}

public class LoginViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
string userNameCore;
POCO-ViewModel:
public string UserName {
get { return userNameCore; }
set {
if(value != userNameCore) {
// Extension method
this.userNameCore = value;
this.RaisePropertyChanged(x => x.UserName);
PropertyChangedEventHandler handler = PropertyChanged;
if(handler != null)
handler(this, new PropertyChangedEventArgs("UserName"));
}
}
}
}
?

WinForms Code-behind (MVVMContext API):


XAML:
mvvmContext.SetBinding(userNameTextEdit,

<dxe:TextEdit Text="{Binding UserName}"/>
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;

public class MyViewModel {
readonly Action<T> _execute;


public MyViewModel() {
public DelegateCommand(Action<T> execute)
this.SayHelloCommand = new DelegateCommand(SayHello);
: this(execute, null) {

}

}

public DelegateCommand(Action<T> execute, Predicate<T> canExecute) {
public System.Windows.Input.ICommand SayHelloCommand {

_execute = execute;
get;
_canExecute = canExecute;

private set;
}

}
//...
public void SayHello() { /* do something */ }
}
}

POCO-ViewModel:
public class DelegateCommand<T> : System.Windows.Input.ICommand {
readonly Predicate<T> _canExecute;
public class MyViewModel {
POCO-ViewModel:
readonly Action<T> _execute;
public void SaySomething(string str) {
public DelegateCommand(Action<T> execute)
public class MyViewModel {
: this(execute, null) {
/* ... */

}
public Task DoSomething () {
public void SayHello() { /* do something */ }
public DelegateCommand(Action<T> execute, Predicate<T> canExecute) {
}

} _execute = execute;
/* asynchronous !!! */
public bool CanSaySomething(string str) {
_canExecute = canExecute;
}
}
/* ... */
} //...
}
}

}
?

XAML:

<Button Command="{Binding SayHello}" />
<Button Command="{Binding Say}" CommandParameter="Hello!"/>
?
WinForms Code-behind (MVVMContext API):
public MyViewModel ViewModel {
get { return mvvmContext.GetViewModel<MyViewModel>(); }
WinForms Code-behind (MVVMContext Fluent API):
}
var fluentApi = mvvmContext.OfType<MyViewModel>();
//
fluentApi.BindCommand(x => x.SayHello());
btnSayHello.BindCommand(
fluentApi.BindCommand((x,s) => x.Say(s), x => x.Name);
() => ViewModel.SayHello(), ViewModel);
btnSay.BindCommand(
() => ViewModel.Say(null), ViewModel, () => ViewModel.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> {

WinForms Code-behind (MVVMContext API):
public ConfirmationBehavior() : base("FormClosing") { }
//...
protected override string GetConfirmationCaption() {

mvvmContext.AttachBehavior<ConfirmationBehavior>(this);
return "Oops!";
}

protected override string GetConfirmationText() {
return "Form will be closed. Are you sure?";
}
}


.
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());


Entity Framework 6.x (Nuget)
DevExpress MVVM Framework
CRUD- Expenses.
DevExpress Scaffolding Wizard
DevExpress WinForms Controls
. Model.

public class Account {


public string Name { get; set; }
public decimal Amount { get; set; }
}
Entity Framework
public class Account {
public class ExpensesDbContext : DbContext {
[Key, Display(AutoGenerateField = false)]
static ExpensesDbContext() {
public int ID { get; set; }
Database.SetInitializer<ExpensesDbContext>(
[Required, StringLength(30, MinimumLength = 4)]
new ExpensesDbContextInitializer());
[Display(Name = "ACCOUNT")]
}
public string Name { get; set; }
public DbSet<Account> Accounts { get; set; }
[DataType(DataType.Currency)]
public DbSet<Category> Categories { get; set; }
[Display(Name = "AMOUNT")]
public DbSet<Transaction> Transactions { get; set; }
public decimal Amount { get; set; }
}
}
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; }
[Required, StringLength(30, MinimumLength = 4)]
[DataType(DataType.Currency)]
[Display(Name = "ACCOUNT")]
[Display(Name = "AMOUNT")]
public string Name { get; set; }
public decimal Amount { get; set; }
[DataType(DataType.MultilineText)]
[Display(Name = "COMMENT")]
public string Comment { get; set; }





DocumentManagerService -
( QueryControl):
MessageBoxService -
(, , Flyout):
tabbedView.QueryControl += (s, e) => {
if(e.Document.ControlName == "AccountCollectionView")
DevExpress.Utils.MVVM.MVVMContext.RegisterFlyoutMessageBoxService();
e.Control = new Views.Accounts();
};



MVVM + DevExpress

(WPF/Silverlight/WinForms/WinRT/UAP)





!

dmitry.garavsky@devexpress.com

WWW.DEVEXPRESS.COM
support@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)

Вам также может понравиться