DEVEXPRESS WAY
dmitry.garavsky@devexpress.com
MVVM? ?
When capabilities are extended, the
codebase should become smaller.
@Thinking Out Loud
- .
.
View
View
Data binding
View Model
Model
Presentation
Logic
Business Logic
and Data
Commands
r
e
r
e
v
d
n
n
a
o
m
m
C
o
C
Relay S oVisibility
e
T
r
v
n
i
c
a
e
e
C
l
ontainer
o
o
B
t
x
e
t
n
o
C
Data
MVVM
Framework?
,
POCO
DX
(MVVM-driven
design)
MVVM WinForms. ?
Notifications
View
View
Data binding
View Model
Model
Presentation
Logic
Business Logic
and Data
Commands
Events
Methods
Code-behind
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;
}
set
{
string
userNameCore;
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
{
set
{
if(value
!=
userNameCore)
{
this.userNameCore
=
value;
PropertyChangedEventHandler
handler
=
PropertyChanged;
if(handler
!=
null)
POCO-
POCO-class
???
Full-featured
ViewModel
DevExpress.Mvvm.POCO.ViewModelSource
public
class
LoginViewModel
:
INotifyPropertyChanged
{
public
event
PropertyChangedEventHandler
PropertyChanged;
string
userNameCore;
public
string
UserName
{
set
{
if(value
!=
userNameCore)
{
this.userNameCore
=
value;
PropertyChangedEventHandler
handler
=
PropertyChanged;
if(handler
!=
null)
?
XAML:
<UserControl
x:Class="DXPOCO.Views.LoginView"
WinForms Code-behind:
public
LoginViewModel
ViewModel
{
xmlns:ViewModels="clr-namespace:DXPOCO.ViewModels"
get
{
return
mvvmContext.GetViewModel<LoginViewModel>();
}
DataContext="{dxmvvm:ViewModelSource
Type=ViewModels:LoginViewModel}"
}
xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm"
...>
</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;
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
{
if(value
!=
userNameCore)
{
get
{
return
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
{
get
{
return
userNameCore;
}
POCO-ViewModel:
set
{
if(value
!=
userNameCore)
{
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(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;
public
string
UserName
{
get
{
return
userNameCore;
}
set
{
if(value
!=
userNameCore)
{
this.userNameCore
=
value;
POCO-ViewModel:
//
Extension
method
PropertyChangedEventHandler
handler
=
PropertyChanged;
this.RaisePropertyChanged(x
=>
x.UserName);
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
{
?
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!");
.
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());
. Model.
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));
(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.
Expenses
(
DevExpress
Code
Central)
2. DevExpress.MVVM.Free
(github)