命令行
在標準的WinForms應用程序中,操作通常在事件處理程序中執行。例如,要在用戶單擊按鈕時刷新數據,需要處理ButtonClick事件并檢索數據源記錄。
這種標準技術不適合分離層的MVVM概念,從數據源中提取數據的代碼應該屬于ViewModel層,而不是View層。在MVVM中,這些任務是通過封裝動作的命令ViewModel對象來完成的,將UI元素綁定到此對象來實現所需的層分離:視圖代碼現在只有綁定代碼,而所有業務邏輯都保留在ViewModel中,并且可以安全地更改。
DevExpress MVVM框架將所有public void方法視為可綁定的命令。下面的代碼說明了如何聲明使用Service顯示消息框的命令,您可以通過以下鏈接在DevExpress demo Center中查看完整的演示。
C#:
//POCO ViewModel
public class ViewModelWithSimpleCommand {
//command
public void DoSomething() {
var msgBoxService = this.GetService<IMessageBoxService>();
msgBoxService.ShowMessage("Hello!");
}
}
VB.NET:
'POCO ViewModel
Public Class ViewModelWithSimpleCommand
'command
Public Sub DoSomething()
Dim msgBoxService = Me.GetService(Of IMessageBoxService)()
msgBoxService.ShowMessage("Hello!")
End Sub
End Class
注意:名稱以“Command”結尾的方法將引發異常——重命名此類方法或使用Command屬性修飾它們。
要將按鈕鏈接到此命令,請使用BindCommand或WithCommand方法。
C#:
//View code mvvmContext.ViewModelType = typeof(ViewModelWithSimpleCommand); var fluent = mvvmContext.OfType<ViewModelWithSimpleCommand>(); fluent.BindCommand(commandButton, x => x.DoSomething); \\or fluent.WithCommand(x => x.DoSomething) .Bind(commandButton1);
VB.NET:
'View code mvvmContext.ViewModelType = GetType(ViewModelWithSimpleCommand) Dim fluent = mvvmContext.OfType(Of ViewModelWithSimpleCommand)() fluent.BindCommand(commandButton, Sub(x) x.DoSomething) 'or fluent.WithCommand(Sub(x) x.DoSomething) .Bind(commandButton1)
WithCommand方法允許您同時綁定多個按鈕。
運行演示:綁定到多個UI元素。
C#:
//View var fluent = mvvmContext.OfType<ViewModelWithSimpleCommand>(); fluent.WithCommand(x => x.DoSomething) .Bind(commandButton1) .Bind(commandButton2);
VB.NET:
'View Dim fluent = mvvmContext.OfType(Of ViewModelWithSimpleCommand)() fluent.WithCommand(Sub(x) x.DoSomething) .Bind(commandButton1) .Bind(commandButton2)
可執行條件
要指定判斷命令是否應該運行的條件,請聲明一個Boolean method,該方法的名稱以“Can”開頭,后面跟著相關的命令名稱,這些方法被稱為CanExecute conditions。
C#:
//ViewModel
public class ViewModelWithConditionalCommand {
//Command
public void DoSomething() {
var msgBoxService = this.GetService<IMessageBoxService>();
msgBoxService.ShowMessage("Hello!");
}
//CanExecute condition
public bool CanDoSomething() {
return (2 + 2) == 4;
}
}
VB.NET:
'ViewModel
Public Class ViewModelWithConditionalCommand
'Command
Public Sub DoSomething()
Dim msgBoxService = Me.GetService(Of IMessageBoxService)()
msgBoxService.ShowMessage("Hello!")
End Sub
'CanExecute condition
Public Function CanDoSomething() As Boolean
Return (2 + 2) = 4
End Function
End Class
您也可以忽略CanExecute名稱要求,并使用Command屬性手動分配命令條件。
C#:
[Command(CanExecuteMethodName = "DoSomethingCriteria")]
public void DoSomething(int p) {
//command
}
VB.NET:
<Command(CanExecuteMethodName := "DoSomethingCriteria")> Public Sub DoSomething(ByVal p As Integer) 'command End Sub
如果CanExecute條件返回false,框架將改變鏈接到該命令的UI元素的狀態(禁用、取消選中或隱藏該元素)。上面的代碼示例來自以下演示:運行此演示并更改條件,使其始終返回false,“執行命令”按鈕被禁用,因為它的相關命令不能再運行。
C#:
//ViewModel
public bool CanDoSomething() {
//always "false"
return (2 + 2) == 5;
}
VB.NET:
'ViewModel Public Function CanDoSomething() As Boolean 'always "False" Return (2 + 2) = 5 End Function
當發生以下情況時,框架會檢查CanExecute條件:
- UI命令綁定初始化。
- 調用RaiseCanExecuteChanged方法。在下面的示例中,每次SelectedEntity屬性更改時,都會重新檢查CanDoSomething條件的返回值。
C#:
//Bindable Property
public virtual MyEntity SelectedEntity{ get; set; }
//OnChanged callback for the bindable property
protected void OnSelectedEntityChanged(){
this.RaiseCanExecuteChanged(x=>x.DoSomething());
}
//Command
public void DoSomething() {
//. . .
}
//CanExecute condition
public bool CanDoSomething() {
//. . .
}
VB.NET:
'Bindable Property Public Overridable Property SelectedEntity() As MyEntity 'OnChanged callback for the bindable property Protected Sub OnSelectedEntityChanged() Me.RaiseCanExecuteChanged(Function(x) x.DoSomething()) End Sub 'Command Public Sub DoSomething() '. . . End Sub 'CanExecute condition Public Function CanDoSomething() As Boolean '. . . End Function
命令與參數
DevExpress MVVM框架接受public void方法作為參數化命令,您可以使用這個參數在View和ViewModel之間傳遞數據。
運行demo:Parameterized命令。
C#:
//ViewModel
public class ViewModelWithParametrizedCommand {
public void DoSomething(object p) {
var msgBoxService = this.GetService<IMessageBoxService>();
msgBoxService.ShowMessage(string.Format("The parameter is {0}.", p));
}
}
//View
mvvmContext.ViewModelType = typeof(ViewModelWithParametrizedCommand);
var fluent = mvvmContext.OfType<ViewModelWithParametrizedCommand>();
object parameter = 5;
fluent.BindCommand(commandButton, x => x.DoSomething, x => parameter);
VB.NET:
'ViewModel
Public Class ViewModelWithParametrizedCommand
Public Sub DoSomething(ByVal p As Object)
Dim msgBoxService = Me.GetService(Of IMessageBoxService)()
msgBoxService.ShowMessage(String.Format("The parameter is {0}.", p))
End Sub
End Class
'View
mvvmContext.ViewModelType = GetType(ViewModelWithParametrizedCommand)
Dim fluent = mvvmContext.OfType(Of ViewModelWithParametrizedCommand)()
Dim parameter As Object = 5
fluent.BindCommand(commandButton, Sub(x) x.DoSomething(Nothing), Function(x) parameter)
還可以在CanExecute條件中添加參數。
運行demo:帶CanExecute條件的參數化命令。
C#:
//ViewModel
public class ViewModelWithParametrizedConditionalCommand {
public void DoSomething(int p) {
var msgBoxService = this.GetService<IMessageBoxService>();
msgBoxService.ShowMessage(string.Format(
"The parameter is {0}.", p));
}
public bool CanDoSomething(int p) {
return (2 + 2) == p;
}
}
//View
mvvmContext.ViewModelType = typeof(ViewModelWithParametrizedConditionalCommand);
var fluent = mvvmContext.OfType<ViewModelWithParametrizedConditionalCommand>();
int parameter = 4;
fluent.BindCommand(commandButton, x => x.DoSomething, x => parameter);
VB.NET:
'ViewModel
Public Class ViewModelWithParametrizedConditionalCommand
Public Sub DoSomething(ByVal p As Integer)
Dim msgBoxService = Me.GetService(Of IMessageBoxService)()
msgBoxService.ShowMessage(String.Format("The parameter is {0}.", p))
End Sub
Public Function CanDoSomething(ByVal p As Integer) As Boolean
Return (2 + 2) = p
End Function
End Class
'View
mvvmContext.ViewModelType = GetType(ViewModelWithParametrizedConditionalCommand)
Dim fluent = mvvmContext.OfType(Of ViewModelWithParametrizedConditionalCommand)()
Dim parameter As Integer = 4
fluent.BindCommand(commandButton, Sub(x) x.DoSomething(Nothing), Function(x) parameter)
多參數
使用對象或元組數據結構來傳遞多個參數。
C#:
class Parameters{
public int Parameter1 { get; set }
public string Parameter2 { get; set }
...
}
// ...
mvvmContext.OfType<MouseDownAwareViewModel>()
.WithEvent<MouseEventArgs>(label, "MouseDown")
.EventToCommand(x => x.ReportLocation, args => new Parameters{ Parameter1 = 1, Parameter2 = "2" });
VB.NET:
Friend Class Parameters
Public Property Parameter1() As Integer
Get
Set(ByVal value As Integer)
End Set
End Get
public String Parameter2
Get
Set(ByVal value As Integer)
End Set
End Get
...
' ...
mvvmContext.OfType(Of MouseDownAwareViewModel)().WithEvent(Of MouseEventArgs)(label, "MouseDown").EventToCommand(Function(x) x.ReportLocation, Function(args) New Parameters With {.Parameter1 = 1, .Parameter2 = "2"})
異步命令
如果需要執行延遲或連續的操作,請使用異步命令。要創建一個異步命令,聲明一個System.Threading.Tasks.Task類型的公共方法(也可以使用async/await語法),將UI元素綁定到命令的代碼保持不變,框架在命令運行時禁用此元素。
執行demo:Async命令。
C#:
//ViewModel
public class ViewModelWithAsyncCommand {
public async Task DoSomethingAsync() {
// do some work here
await Task.Delay(1000);
}
}
//View
mvvmContext.ViewModelType = typeof(ViewModelWithAsyncCommand);
var fluent = mvvmContext.OfType<ViewModelWithAsyncCommand>();
fluent.BindCommand(commandButton, x => x.DoSomethingAsync);
VB.NET:
'ViewModel Public Class ViewModelWithAsyncCommand Public Async Sub DoSomethingAsync() As Task ' do some work here Await Task.Delay(1000) End Sub End Class 'View mvvmContext.ViewModelType = GetType(ViewModelWithAsyncCommand) Dim fluent = mvvmContext.OfType(Of ViewModelWithAsyncCommand)() fluent.BindCommand(commandButton, Sub(x) x.DoSomethingAsync(Nothing))
任務支持取消標記并允許您檢查IsCancellationRequested屬性,并在該屬性返回true時中止任務。如果將此代碼添加到async命令中,請使用BindCancelCommand方法創建一個UI元素來停止正在執行的async命令。DevExpress MVVM框架鎖定了這個取消按鈕,只有在運行相關的異步命令時才啟用它。
執行demo: Async取消命令。
C#:
//ViewModel
public class ViewModelWithAsyncCommandAndCancellation {
public async Task DoSomethingAsynchronously() {
var dispatcher = this.GetService<IDispatcherService>();
var asyncCommand = this.GetAsyncCommand(x => x.DoSomethingAsynchronously());
for(int i = 0; i <= 100; i++) {
if(asyncCommand.IsCancellationRequested)
break;
// do some work here
await Task.Delay(25);
await UpdateProgressOnUIThread(dispatcher, i);
}
await UpdateProgressOnUIThread(dispatcher, 0);
}
public int Progress {
get;
private set;
}
//update the "Progress" property bound to the progress bar within a View
async Task UpdateProgressOnUIThread(IDispatcherService dispatcher, int progress) {
await dispatcher.BeginInvoke(() => {
Progress = progress;
this.RaisePropertyChanged(x => x.Progress);
});
}
}
//View
mvvmContext.ViewModelType = typeof(ViewModelWithAsyncCommandAndCancellation);
var fluent = mvvmContext.OfType<ViewModelWithAsyncCommandAndCancellation>();
fluent.BindCommand(commandButton, x => x.DoSomethingAsynchronously);
fluent.BindCancelCommand(cancelButton, x => x.DoSomethingAsynchronously);
fluent.SetBinding(progressBar, p => p.EditValue, x => x.Progress);
VB.NET:
'ViewModel Public Class ViewModelWithAsyncCommandAndCancellation Public Async Sub DoSomethingAsynchronously() As Task Dim dispatcher = Me.GetService(Of IDispatcherService)() Dim asyncCommand = Me.GetAsyncCommand(Sub(x) x.DoSomethingAsynchronously()) For i As Integer = 0 To 100 If asyncCommand.IsCancellationRequested Then Exit For End If ' do some work here Await Task.Delay(25) Await UpdateProgressOnUIThread(dispatcher, i) Next i Await UpdateProgressOnUIThread(dispatcher, 0) End Sub Private privateProgress As Integer Public Property Progress() As Integer Get Return privateProgress End Get Private Set(ByVal value As Integer) privateProgress = value End Set End Property 'update the "Progress" property bound to the progress bar within a View Private Async Sub UpdateProgressOnUIThread(ByVal dispatcher As IDispatcherService, ByVal progress As Integer) As Task Await dispatcher.BeginInvoke(Sub() Me.Progress = progress Me.RaisePropertyChanged(Sub(x) x.Progress) End Sub) End Sub End Class 'View mvvmContext.ViewModelType = GetType(ViewModelWithAsyncCommandAndCancellation) Dim fluent = mvvmContext.OfType(Of ViewModelWithAsyncCommandAndCancellation)() fluent.BindCommand(commandButton, Sub(x) x.DoSomethingAsynchronously) fluent.BindCancelCommand(cancelButton, Sub(x) x.DoSomethingAsynchronously) fluent.SetBinding(progressBar, Sub(p) p.EditValue, Sub(x) x.Progress)
WithCommand Fluent API方法還支持可取消的異步命令。
C#:
mvvmContext.ViewModelType = typeof(ViewModelWithAsyncCommandAndCancellation); // Initialize the Fluent API var fluent = mvvmContext.OfType<ViewModelWithAsyncCommandAndCancellation>(); // Binding for buttons fluent.WithCommand(x => x.DoSomethingAsynchronously) .Bind(commandButton) .BindCancel(cancelButton);
VB.NET:
mvvmContext.ViewModelType = GetType(ViewModelWithAsyncCommandAndCancellation) ' Initialize the Fluent API Dim fluent = mvvmContext.OfType(Of ViewModelWithAsyncCommandAndCancellation)() ' Binding for buttons fluent.WithCommand(Sub(x) x.DoSomethingAsynchronously).Bind(commandButton).BindCancel(cancelButton)
命令觸發器
觸發器允許您執行與命令關聯的其他View操作。有三種觸發器類型,取決于觸發觸發器的條件:
- “Before”觸發器——允許您在目標命令執行之前執行操作。
C#:
mvvmContext.ViewModelType = typeof(ViewModelWithSimpleCommand);
var fluent = mvvmContext.OfType<ViewModelWithSimpleCommand>();
fluent.BindCommand(commandButton, x => x.DoSomething);
fluent.WithCommand(x => x.DoSomething)
.Before(() => XtraMessageBox.Show("The target command is about to be executed"));
VB.NET:
mvvmContext.ViewModelType = GetType(ViewModelWithSimpleCommand)
Dim fluent = mvvmContext.OfType(Of ViewModelWithSimpleCommand)()
fluent.BindCommand(commandButton, Function(x) x.DoSomething)
fluent.WithCommand(Sub(x) x.DoSomething)
.Before(Function() XtraMessageBox.Show("The target command is about to be executed"))
- " After "觸發器——允許您在目標命令完成后執行操作。
C#:
mvvmContext.ViewModelType = typeof(ViewModelWithSimpleCommand);
var fluent = mvvmContext.OfType<ViewModelWithSimpleCommand>();
fluent.BindCommand(commandButton, x => x.DoSomething);
fluent.WithCommand(x => x.DoSomething)
.After(() => XtraMessageBox.Show("The target command has been executed"));
VB.NET:
mvvmContext.ViewModelType = GetType(ViewModelWithSimpleCommand)
Dim fluent = mvvmContext.OfType(Of ViewModelWithSimpleCommand)()
fluent.BindCommand(commandButton, Function(x) x.DoSomething)
fluent.WithCommand(Function(x) x.DoSomething).After(Function() XtraMessageBox.Show("The target command has been executed"))
- “CanExecute”條件觸發器——允許您在目標命令的CanExecute條件發生變化時執行操作。
C#:
var fluent = mvvmContext.OfType<ViewModelWithSimpleCommandAndCanExecute>();
fluent.BindCommand(commandButton, x => x.DoSomething);
// When the CanExecute condition changes, the message shows up
fluent.WithCommand(x => x.DoSomething)
.OnCanExecuteChanged(() => XtraMessageBox.Show("The CanExecute condition has changed"));
VB.NET:
Dim fluent = mvvmContext.OfType(Of ViewModelWithSimpleCommandAndCanExecute)()
fluent.BindCommand(commandButton, Function(x) x.DoSomething)
' When the CanExecute condition changes, the message shows up
fluent.WithCommand(Function(x) x.DoSomething)
.OnCanExecuteChanged(Function() XtraMessageBox.Show("The CanExecute condition has changed"))
注意,每個綁定到目標命令的UI元素都會執行觸發器,當單擊任何按鈕時,下面的代碼示例將顯示一個消息框。
C#:
mvvmContext1.OfType<BulkEditViewModel>()
.WithCommand(vm => vm.RemoveFields())
.Bind(button1)
.Bind(button2)
.After(() => MessageBox.Show("Test"));
VB.NET:
mvvmContext1.OfType(Of BulkEditViewModel)()
.WithCommand(Function(vm) vm.RemoveFields())
.Bind(button1)
.Bind(button2)
.After(Function() MessageBox.Show("Test"))
Non-POCO命令
上面描述的POCO類命令允許您使用最直接且不會出錯的語法,DevExpress MVVM框架還支持其他命令類型來確保遺留項目的無障礙遷移。
DevExpress delegate命令對象
委托命令是System.Windows.Input.ICommand接口的實現。
運行demo: Simple Commands
C#:
DelegateCommand command = new DelegateCommand(() => {
XtraMessageBox.Show("Hello!");
});
commandButton.BindCommand(command);
VB.NET:
Dim command As New DelegateCommand(Sub() XtraMessageBox.Show("Hello!"))
commandButton.BindCommand(command)
運行demo:帶有CanExecute條件的命令
C#:
Func<bool> canExecute = () => (2 + 2 == 4);
DelegateCommand command = new DelegateCommand(() => {
XtraMessageBox.Show("Hello!");
}, canExecute);
commandButton.BindCommand(command);
VB.NET:
Dim canExecute As Func(Of Boolean) = Function() (2 + 2 = 4)
Dim command As New DelegateCommand(Sub() XtraMessageBox.Show("Hello!"), canExecute)
commandButton.BindCommand(command)
運行demo:帶參數命令
C#:
DelegateCommand<object> command = new DelegateCommand<object>((v) => {
XtraMessageBox.Show(string.Format("The parameter is {0}.", v));
});
object parameter = 5;
commandButton.BindCommand(command, () => parameter);
VB.NET:
Dim command As New DelegateCommand(Of Object)(Sub(v) XtraMessageBox.Show(String.Format("The parameter is {0}.", v)))
Dim parameter As Object = 5
commandButton.BindCommand(command, Function() parameter)
運行demo:參數化CanExecute條件的命令
C#:
Func<int, bool> canExecute = (p) => (2 + 2 == p);
DelegateCommand<int> command = new DelegateCommand<int>((v) => {
XtraMessageBox.Show(string.Format("The parameter is {0}.", v));
}, canExecute);
int parameter = 4;
commandButton.BindCommand(command, () => parameter);
VB.NET:
Dim canExecute As Func(Of Integer, Boolean) = Function(p) (2 + 2 = p)
Dim command As New DelegateCommand(Of Integer)(Sub(v) XtraMessageBox.Show(String.Format("The parameter is {0}.", v)), canExecute)
Dim parameter As Integer = 4
commandButton.BindCommand(command, Function() parameter)
自定義命令類
這些對象是具有至少一個Execute方法的任意自定義類型的對象,如果需要您可以添加CanExecute方法和CanExecuteChanged事件。
運行demo:Simple Commands
C#:
CommandObject command = new CommandObject();
commandButton.BindCommand(command);
public class CommandObject {
public void Execute(object parameter) {
XtraMessageBox.Show("Hello!");
}
}
VB.NET:
Private command As New CommandObject()
commandButton.BindCommand(command)
Public Class CommandObject
Public Sub Execute(ByVal parameter As Object)
XtraMessageBox.Show("Hello!")
End Sub
End Class
運行demo:帶參數命令
C#:
CommandObjectWithParameter command = new CommandObjectWithParameter();
int parameter = 4;
commandButton.BindCommand(command, () => parameter);
public class CommandObjectWithParameter {
public void Execute(object parameter) {
XtraMessageBox.Show(string.Format(
"The parameter is {0}.", parameter));
}
public bool CanExecute(object parameter) {
return object.Equals(2 + 2, parameter);
}
}
VB.NET:
Dim command As New CommandObjectWithParameter()
Dim parameter As Integer = 4
commandButton.BindCommand(command, Sub() parameter)
Public Class CommandObjectWithParameter
Public Sub Execute(ByVal parameter As Object)
XtraMessageBox.Show(String.Format("The parameter is {0}.", parameter))
End Sub
Public Function CanExecute(ByVal parameter As Object) As Boolean
Return Object.Equals(2 + 2, parameter)
End Function
End Class                
            
 QQ交談
QQ交談 在線咨詢
在線咨詢 
                 
                
 渝公網安備
            50010702500608號
渝公網安備
            50010702500608號
             
            
 客服熱線
客服熱線