using DevExpress.Mvvm.DataAnnotations;
//. . .
string test;
[BindableProperty]
public virtual string Test
{
get { return test; }
set { test = value; }
}
属性依赖
属性依赖项是一种方法,当其相关属性更改或将要更改时,该方法将自动执行。
public virtual string Test { get; set; }
protected void OnTestChanged() {
//do something
}
为了实现属性依赖,属性依赖的方法必须要命名为On Changing 或On Changed .
public virtual string Test { get; set; }
protected void OnTestChanged() {
//do something
}
//this command will be executed only if "p" equals 4
public void DoSomething(int p) {
MessageBox.Show(string.Format("The parameter passed to command is {0}.", p));
}
public bool CanDoSomething(int p) {
return (2 + 2) == p;
}
可以通过Command特性的CanExecuteMethodName参数修改可以执行方法的名字
[Command(CanExecuteMethodName = "DoSomethingCriteria")]
public void DoSomething(int p) {
MessageBox.Show(string.Format("The parameter passed to command is {0}.", p));
}
public bool DoSomethingCriteria(int p) {
return (2 + 2) == p;
}
//a bindable property
public virtual bool IsModified { get; protected set; }
//a command
public void Save() {
//. . .
}
//a CanExecute condition
public bool CanSave() {
return IsModified;
}
//the OnChanged method calls the RaiseCanExecuteChanged method for the "Save" command
//this forces the command to update its CanExecute condition
public void OnIsModifiedChanged() {
this.RaiseCanExecuteChanged(x=>x.Save());
}
服务Service
用于为MVVM应用程序中的视图提供特定的UI感知功能的界面
为了解析服务,框架需要重写接口类型的虚拟属性,而且这些属性的名字都必须以Service结尾。
public virtual IMyNotificationService MyService {
get { throw new NotImplementedException(); }
}
public virtual IMyNotificationService AnotherService {
get { throw new NotImplementedException(); }
}
可以使用ServiceProperty特性来显示标注其他服务属性
using DevExpress.Mvvm.DataAnnotations;
//. . .
[ServiceProperty]
public virtual IMyNotificationService MyProvider {
get { throw new NotImplementedException(); }
}
[ServiceProperty(Key="Service1")]
public virtual IMyNotificationService Service {
get { throw new NotImplementedException(); }
}
[ServiceProperty(Key = "Service2")]
public virtual IMyNotificationService AnotherService {
get { throw new NotImplementedException(); }
}
public class SampleViewModel : BindableBase {
string titleValue;
public string Title {
get { return titleValue; }
set { SetProperty(ref titleValue, value, "Title");}
}
}
//View
mvvmContext.ViewModelType = typeof(MultViewModel);
mvvmContext.SetBinding(editor1, e => e.EditValue, "Operand1");
mvvmContext.SetBinding(editor2, e => e.EditValue, "Operand2");
mvvmContext.SetBinding(resultLabel, l => l.Text, "ResultText");
//ViewModel
public class MultViewModel {
public MultViewModel() {
UpdateResultText();
}
public virtual int Operand1 { get; set; }
public virtual int Operand2 { get; set; }
public virtual int Result { get; set; }
public virtual string ResultText { get; set; }
// OnChanged callback is created for the Operand1 property from this method.
protected void OnOperand1Changed() {
UpdateResult();
}
// OnChanged callback is created for the Operand2 property from this method.
protected void OnOperand2Changed() {
UpdateResult();
}
// OnChanged callback is created for the Result property from this method.
protected void OnResultChanged() {
UpdateResultText();
}
void UpdateResult() {
Result = Operand1 * Operand2;
}
void UpdateResultText() {
ResultText = string.Format("The result of operands multiplication is: {0:n0}", Result);
}
}
//View
mvvmContext.ViewModelType = typeof(SumViewModel);
mvvmContext.SetBinding(editor1, e => e.EditValue, "Operand1");
mvvmContext.SetBinding(editor2, e => e.EditValue, "Operand2");
mvvmContext.SetBinding(resultLabel, l => l.Text, "ResultText");
//ViewModel
public class SumViewModel {
[BindableProperty(OnPropertyChangedMethodName = "NotifyResultAndResultTextChanged")]
public virtual int Operand1 { get; set; }
[BindableProperty(OnPropertyChangedMethodName = "NotifyResultAndResultTextChanged")]
public virtual int Operand2 { get; set; }
// We raise change notifications for the Result and ResultText properties manually
public int Result {
get { return Operand1 + Operand2; }
}
public string ResultText {
get { return string.Format("The result of operands summarization is: {0:n0}", Result); }
}
protected void NotifyResultAndResultTextChanged() {
this.RaisePropertyChanged(x => x.Result); // change-notification for the Result
this.RaisePropertyChanged(x => x.ResultText); // change-notification for the ResultText
}
}
Meta-POCO绑定
POCO减轻了很多代码量,但重命名后会导致相关绑定失败。
使用Meta-POCO可以解决这个问题,
public virtual int Result { get; set; }
public virtual int Operand1 { get; set; }
public virtual int Operand2 { get; set; }
public string ResultText {
get { return string.Format("The result of operands summarization is: {0:n0}", Result); }
}
[System.ComponentModel.DataAnnotations.MetadataType(typeof(Metadata))]
//ViewModel
public class SumViewModel_MetaPOCO {
public SumViewModel_MetaPOCO() {
}
protected void NotifyResultAndResultTextChanged() {
Result = Operand1 + Operand2;
}
// Metadata class for the SumViewModel_MetaPOCO
public class Metadata : IMetadataProvider {
void IMetadataProvider.BuildMetadata(MetadataBuilder builder) {
//Metadata
}
}
}
protected void NotifyResultAndResultTextChanged() {
Result = Operand1 + Operand2;
this.RaisePropertyChanged(x => x.Result); // change-notification for the Result
this.RaisePropertyChanged(x => x.ResultText); // change-notification for the ResultText
}
public class Test {
public int ID { get; set; }
public string Text { get; set; }
}
public class TestViewModel {
public TestViewModel() {
Entities = new ObservableCollection();
}
public virtual Test SelectedEntity { get; set; }
public virtual ObservableCollection Entities { get; set; }
public void Add() {
Entities.Add(new Test() { ID = Entities.Count, Text = string.Format("Test {0}", Entities.Count) });
}
public void Remove(string title) {
Test element = Entities.Single(x => x.Text == title);
if (element != null) Entities.Remove(element);
}
}
//View
mvvmContext.ViewModelType = typeof(UIViewModel);
// Data binding for the IsActive property
mvvmContext.SetBinding(checkEdit, c => c.Checked, x => x.IsActive);
// Property-change Trigger for the IsActive property
mvvmContext.SetTrigger(x => x.IsActive, (active) =>
{
label.Text = active ? "Active" : "Inactive";
});
//ViewModel
public class UIViewModel {
public virtual bool IsActive { get; set; }
}
// Set type of POCO-ViewModel
mvvmContext.ViewModelType = typeof(UIViewModel);
// Data binding for the IsActive property
var fluentAPI = mvvmContext.OfType();
fluentAPI.SetBinding(checkEdit, c => c.Checked, x => x.IsActive);
// Property-change Trigger for the IsActive property
fluentAPI.SetTrigger(x => x.IsActive, (active) =>
{
label.Text = active ? "Active" : "Inactive";
});
自定义ViewModel
自定义的ViewModel怎么兼容DevExpress MVVM框架
MvvmContext设置为此特定的ViewModel类型,以与旧版ViewModel一起使用。
var legacyViewModel = new LegacyViewModel("Legacy ViewModel");
// initialize the MVVMContext with the specific ViewModel's instance
mvvmContext.SetViewModel(typeof(LegacyViewModel), legacyViewModel);
完成后数据绑定语法兼容
mvvmContext.SetBinding(editor, e => e.EditValue, "Title");
另一种方法
可以绑定控件到子ViewModels,怎么做呢
首先,定义一个嵌套的ViewModel和一个获得该ViewModel属性。
public ViewModel() {
// Create Nested ViewModel as POCO-ViewModel
Child = ViewModelSource.Create();
}
public NestedViewModel Child { get; private set; }
然后就可以使用数据绑定,绑定的属性是嵌套ViewModel的一部分。
mvvmContext.SetBinding(editor, e => e.EditValue, "Child.Title");
也可以使用fluent API实现绑定
var fluent = mvvmContext.OfType();
fluent.SetBinding(editor, e => e.EditValue, x => x.Child.Title)
值转换Value Converters
在介绍POCO的时候了解了编辑器Editor绑定Title属性
mvvmContext.SetBinding(editor, e => e.EditValue, "Title");
var fluent = mvvmContext.OfType();
fluent.SetBinding(trackBar, e => e.Value, x => x.Progress);
fluent.SetBinding(editor, e => e.Text, x => x.Progress);
fluent.SetBinding(check, e => e.CheckState, x => x.ModelState,
modelState =>
{
// Convert the ViewModel.State to the editor's CheckState
switch(modelState) {
case ViewModel.State.Active:
return CheckState.Checked;
case ViewModel.State.Inactive:
return CheckState.Unchecked;
default:
return CheckState.Indeterminate;
}
},
checkState =>
{
// Convert back from editor's CheckState to the ViewModel.State
switch(checkState) {
case CheckState.Checked:
return ViewModel.State.Active;
case CheckState.Unchecked:
return ViewModel.State.Inactive;
default:
return ViewModel.State.Suspended;
}
});
fluent.SetBinding(check, e => e.Text, x => x.ModelState, modelState =>
string.Format("Click to change the current ViewModel state from {0} to {1}", modelState, (ViewModel.State)((1 + (int)modelState) % 3)));
格式化绑定
mvvmContext.SetBinding(label, l => l.Text, "Price");
public class ViewModel {
public virtual int Price { get; set; }
}
mvvmContext.SetBinding(label, l => l.Text, "Price", "Price: {0:C}");
多个属性绑定
绑定多个值
mvvmContext.SetMultiBinding(editForFullName, e => e.Text, new string[] { "FirstName", "LastName" });
//If this function returns false, the command is disabled
Func canExecute = () => (2 + 2 == 4);
DelegateCommand command = new DelegateCommand(() =>
{
MessageBox.Show("Hello! I'm running, because the `canExecute` condition is `True`. Try to change this condition!");
}, canExecute);
给命令方法传递参数
DelegateCommand
一个参数和一个可执行方法
Func canExecute = (p) => (2 + 2 == p);
// This command is created parameterized and with 'canExecute' parameter.
DelegateCommand command = new DelegateCommand((v) =>
{
MessageBox.Show(string.Format(
"Hello! The parameter passed to command is {0}." + Environment.NewLine +
"And I'm running, because the `canExecute` condition is `True` for this parameter." + Environment.NewLine +
"Try to change this parameter!", v));
}, canExecute);
int parameter = 4;
commandButton.BindCommand(command, () => parameter);
using System.Windows.Input;
public class QuestionnaireViewModel
{
public QuestionnaireViewModel()
{
this.SubmitCommand = new DelegateCommand(
this.OnSubmit, this.CanSubmit );
}
//add the PresentationCore assembly to the project to use the ICommand interface
public ICommand SubmitCommand { get; private set; }
private void OnSubmit(object arg) {...}
private bool CanSubmit(object arg) { return true; }
}
POCO命令
DevExpress MVVM框架中,定义为此类中没有参数或只有一个参数的void方法
public class ViewModelWithParametrizedCommand {
public void DoSomething(object p) {
MessageBox.Show(string.Format("Hello! The parameter passed to command is {0}. Try to change this parameter!", p));
}
}
绑定命令需要MvvmContext的API
mvvmContext.ViewModelType = typeof(ViewModelWithSimpleCommand);
mvvmContext.BindCommand(commandButton, x => x.DoSomething());
POCO也支持CanExecute
//View
mvvmContext.ViewModelType = typeof(ViewModelWithParametrizedConditionalCommand);
int parameter = 4;
mvvmContext.BindCommand(commandButton, (x, p) => x.DoSomething(p), x => parameter);
//ViewModelWithParametrizedConditionalCommand
public class ViewModelWithParametrizedConditionalCommand {
// Parameterized POCO-command will be created from this method.
public void DoSomething(int p) {
MessageBox.Show(string.Format(
"Hello! The parameter passed to command is {0}." + Environment.NewLine +
"And I'm running, because the `canExecute` condition is `True` for this parameter." + Environment.NewLine +
"Try to change this parameter!", p));
}
// Parameterized `CanExecute` method for the `DoSomething` command.
public bool CanDoSomething(int p) {
return (2 + 2) == p;
}
}
POCO工作流,(1)是一个POCO命令执行关于收集实体(例如,选择的网格控制行)某些动作。当前选择的实体将保留为可绑定的SelectedEntity(2)属性的值,每当此属性更改其值时,都会自动触发其OnSelectedEntityChanged回调(3)此回调触发DoSomething命令的标准DevExpress POCO RaiseCanExecuteChanged方法(4)调用此方法时,将重新检查CanDoSomething方法的返回值(5)并更新DoSomething 命令的可用性。
//2 - Bindable Property
public virtual MyEntity SelectedEntity{ get; set; }
//3 - Changed callback
protected void OnSelectedEntityChanged(){
//4 - Checks the CanExecute return value
this.RaiseCanExecuteChanged(x=>x.DoSomething());
}
//1 - POCO Command
public void DoSomething() {
// command body
}
//5 - CanExecute function for the POCO Command
public bool CanDoSomething() {
//CanExecute condition (Amount>500$)
return (SelectedEntity != null) && (SelectedEntity.Amount > 500);
}
异步命令
异步命令需要用到System.Threading.Tasks.Task类型方法
public class ViewModelWithAsyncCommand {
// Asynchronous POCO-command will be created from this method.
public Task DoSomethingAsynchronously() {
return Task.Factory.StartNew(() =>
{
System.Threading.Thread.Sleep(1000); // do some work here
});
}
}
绑定异步命令和之前没有区别,BindCommand
mvvmContext.ViewModelType = typeof(ViewModelWithAsyncCommand);
mvvmContext.BindCommand(commandButton, x => x.DoSomethingAsynchronously());
该异步方法可以取消
public class ViewModelWithAsyncCommandAndCancellation {
// An asynchronous POCO-command will be created from this method.
public Task DoSomethingAsynchronously() {
var dispatcher = this.GetService();
return Task.Factory.StartNew(() => {
var asyncCommand = this.GetAsyncCommand(x => x.DoSomethingAsynchronously());
for(int i = 0; i <= 100; i++) {
if(asyncCommand.IsCancellationRequested) // cancellation check
break;
System.Threading.Thread.Sleep(25); // do some work here
UpdateProgressOnUIThread(dispatcher, i);
}
UpdateProgressOnUIThread(dispatcher, 0);
});
}
// Property for progress
public int Progress { get; private set; }
void UpdateProgressOnUIThread(IDispatcherService dispatcher, int progress) {
dispatcher.BeginInvoke(() => {
Progress = progress;
this.RaisePropertyChanged(x => x.Progress);
});
}
}
绑定UI,支持取消操作,MvvmContext组件的BindCancelCommand
mvvmContext.ViewModelType = typeof(ViewModelWithAsyncCommandAndCancellation);
// Binding the async command
mvvmContext.BindCommand(commandButton, x => x.DoSomethingAsynchronously());
// Binding the cancellation for the previous command
mvvmContext.BindCancelCommand(cancelButton, x => x.DoSomethingAsynchronously());
//ViewModel
public class LegacyCommandWithParameter {
public void Execute(object parameter) {
MessageBox.Show(string.Format(
"Hello! I'm a Legacy command and the parameter passed to me is {0}." + Environment.NewLine +
"I'm running because the `canExecute` condition is `True` for this parameter." + Environment.NewLine +
"Try to change this parameter!", parameter));
}
public bool CanExecute(object parameter) {
return object.Equals(2 + 2, parameter);
}
}
//View
// This is parameterized legacy-command with both the Execute (object) and the CanExecute (object) methods.
LegacyCommandWithParameter command = new LegacyCommandWithParameter();
int parameter = 4;
// UI binding for button with `queryParameter` function
commandButton.BindCommand(command, () => parameter);
//POCO ViewModel
protected virtual IMessageBoxService MessageBoxService {
get { throw new System.NotImplementedException(); }
}
获得服务后就可以使用方法
public void SayHello() {
MessageBoxService.Show("Hello!");
}
最后需要注册服务,服务可以本地注册也可以全局注册。
//Global service
DevExpress.Mvvm.ServiceContainer.Default.RegisterService(new SomeService());
//Local service
serviceContainer.RegisterService(new SomeFilterService(ModuleType.MyCustomFilter));
//Static method that registers the global DevExpress XtraDialogService
MVVMContext.RegisterXtraDialogService();
//Registers the Service1 service in the default service container (global service)
mvvmContext1.RegisterDefaultService(new Service1());
//Registers the local Service1 for use within the current View only
mvvmContext1.RegisterService(new Service2());
//ViewModel
public virtual IMyNotificationService Service {
get { throw new NotImplementedException(); }
}
public virtual IMyNotificationService AnotherService {
get { throw new NotImplementedException(); }
}
这是可以绑定到UI元素(例如,按钮)的DoSomething方法。它将显示两条带有相同文本的消息。
//ViewModel
public void DoSomething() {
Service.Notify("Hello");
AnotherService.Notify("Hello");
}
[ServiceProperty(Key="Service1")]
public virtual IMyNotificationService Service {
get { throw new NotImplementedException(); }
}
[ServiceProperty(Key = "Service2")]
public virtual IMyNotificationService AnotherService {
get { throw new NotImplementedException(); }
}
对于非POCO ViewModel,可以将服务密钥设置为GetService扩展方法的参数。
public IServiceInterface MyService {
get { return this.GetService("MyServiceKey"); }
}
现在,您必须使用这些唯一的密钥注册自定义服务。
mvvmContext1.RegisterService("Service1", new CustomService1());
mvvmContext1.RegisterService("Service2", new CustomService2());
mvvmContext1.AttachBehavior>(checkEdit1, behavior => {
behavior.Caption = "CheckEdit State Changing";
behavior.Text = "This checkEdit's checked-state is about to be changed. Are you sure?";
behavior.Buttons = ConfirmationButtons.YesNo;
behavior.ShowQuestionIcon = true;
}, "EditValueChanging");
Fluent API
mvvmContext1.WithEvent(checkEdit1, "EditValueChanging").Confirmation(behavior => {
behavior.Caption = "CheckEdit State changing";
behavior.Text = "This checkEdit's checked-state is about to be changed. Are you sure?";
});
public void TransmitMessage1() {
Messenger.Default.Send("This message is sent from Sender 1", "sender1");
}
public void TransmitMessage2() {
Messenger.Default.Send("This message is sent from Sender 2", "sender2");
}
mvvmContext.ViewModelType = typeof(ViewModel);
var fluentAPI = mvvmContext.OfType();
fluentAPI.SetBinding(editor, e => e.EditValue, x => x.Title);
//ViewModel
public virtual string Title { get; set; }
绑定嵌套属性
mvvmContext.ViewModelType = typeof(ViewModel);
var fluent = mvvmContext.OfType();
fluent.SetBinding(editor, e => e.EditValue, x => x.Child.Title);
//ViewModel
public NestedViewModel Child { get; private set; }
//NestedViewModel
public virtual string Title { get; set; }
UI触发器
mvvmContext.ViewModelType = typeof(UIViewModel);
var fluentAPI = mvvmContext.OfType();
fluentAPI.SetTrigger(x => x.IsActive, (active) =>
{
label.Text = active ? "Active" : "Inactive";
});
//UIViewModel
public virtual bool IsActive { get; set; }
命令
参数命令和CanExecute
mvvmContext.ViewModelType = typeof(ViewModel);
int parameter = 4;
var fluentAPI = mvvmContext.OfType();
fluentAPI.BindCommand(commandButton, (x, p) => x.DoSomething(p), x => parameter);
//ViewModel
public class ViewModel {
public void DoSomething(int p) {
//. . .
}
public bool CanDoSomething(int p) {
return (2 + 2) == p;
}
}
异步命令
mvvmContext.ViewModelType = typeof(ViewModel);
var fluentAPI = mvvmContext.OfType();
fluentAPI.BindCommand(commandButton, x => x.DoSomethingAsynchronously());
fluentAPI.BindCancelCommand(cancelButton, x => x.DoSomethingAsynchronously());
//ViewModel
public class ViewModelWithAsyncCommandAndCancellation
public Task DoSomethingAsynchronously() {
return Task.Factory.StartNew(() =>
{
var asyncCommand = this.GetAsyncCommand(x => x.DoSomethingAsynchronously());
for(int i = 0; i <= 100; i++) {
if(asyncCommand.IsCancellationRequested) // cancellation check
break;
//. . .
}
});
}
}
WithCommand一个命令绑定多个UI元素
//binding to one UI element
fluent.WithCommand(x => x.DoSomething())
.Bind(btnDoSomething);
//binding to multiple UI elements
fluent.WithCommand(x => x.DoSomething())
.Bind(btn1DoSomething)
.Bind(btn2DoSomething);
fluent.WithCommand(x => x.DoSomethingAsynchronously())
.Bind(btnDo)
.BindCancel(btnCancel);
mvvmContext.WithEvent(editor, "EditValueChanging")
.Confirmation(behavior =>
{
behavior.Caption = "CheckEdit State changing";
behavior.Text = "This checkEdit's checked-state is about to be changed. Are you sure?";
});
var service = DevExpress.Utils.MVVM.Services.DocumentManagerService.Create(tabbedView1);
var viewService = service as DevExpress.Utils.MVVM.UI.IViewService;
mvvmContext1.RegisterService(service);
public class MyNavigationViewModel {
protected IDocumentManagerService DocumentManagerService {
get { return this.GetService(); }
}
//Lists all available view types
public string[] Modules {
get { return new string[] { "View1", "View2", "View3" }; }
}
//Bind this command to required UI elements to create and display a document
public void Show(string moduleName) {
var document = DocumentManagerService.CreateDocument(moduleName, null, this);
if(document != null) {
document.Title = moduleName;
document.Show();}
}
}
public class ViewModel : ViewModelBase {
//. . .
}
var myViewModel = new ViewModel();
mvvmContext1.SetViewModel(typeof(ViewModel), myViewModel);
不建议使用此方法,会失去POCO模型提供的所有功能。
ViewModelCreate事件
该方法旨在与依赖项注入框架(例如Ninject)一起使用。
public class SamuraiViewModel {
public IWeapon Weapon { get; private set; }
public SamuraiViewModel(IWeapon weapon) {
this.Weapon = weapon;
}
public void Attack() {
Weapon.Hit();
}
}
// Bind a dependency for IWeapon
kernel.Bind().To();
// Set up MVVMContext
var fluent = mvvmContext1.OfType();
fluent.BindCommand(simpleButton1, x => x.Attack());
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml&q
Mockito单元测试实例:
public class SettingServiceTest {
private List<PersonDTO> personList = new ArrayList<PersonDTO>();
@InjectMocks
private SettingPojoService settin
public class DeleteExtraSpace {
/**
* 题目:给定字符串,删除开始和结尾处的空格,并将中间的多个连续的空格合并成一个。
* 方法1.用已有的String类的trim和replaceAll方法
* 方法2.全部用正则表达式,这个我不熟
* 方法3.“重新发明轮子”,从头遍历一次
*/
public static v
今天早上打开MyEclipse时,自动关闭!弹出An error has occurred.See the log file错误提示!
很郁闷昨天启动和关闭还好着!!!打开几次依然报此错误,确定不是眼花了!
打开日志文件!找到当日错误文件内容:
--------------------------------------------------------------------------