Devexpress Winform MVVM

归纳总结备忘

(Devexpress Winform MVVM Practice)

 我自己的使用

前言

MVVM

       MVVM是Model-View-ViewModel,是一种专为WPF开发而设计的架构设计模式,类似MVC。MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI 和业务逻辑分开。 ViewModel 根据databindings,commond,notification等与view层连接,可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑,充当视图层与数据层的通信桥梁。
在这里插入图片描述
好处:

  • 逻辑低耦合
  • 可重用性
  • 方便单元测试

Devexpress

       DevExpress全称Developer Express,是全球著名的控件开发公司,其.NET界面控件DXperience Universal Suite(Dev宇宙版)全球知名,获奖无数。DevExpress控件以界面美观和功能强大著称,拥有大量的示例和帮助文档,开发者能够快速上手。在国内,DevExpress亦拥有大量的用户,资料比较完善,交流方便。DevExpress广泛应用于ECM企业内容管理、 成本管控、进程监督、生产调度,在企业/政务信息化管理中占据一席重要之地。

官网:https://www.devexpress.com/

       如上所说,mvvm专为WPF设计,而没有第三方MVVM框架的 winform平台缺乏灵活的绑定以及绑定commad等基本特性,必须手动实现,而devexpress为这些特性提供了完整支持,是开发更关心本身业务逻辑。

正文

databindings及 UI Triggers

在devexpress中viewModel所有的 virtual 属性都将是可绑定的,在更改值时会自动传递PropertyChanged消息(在未有devexpress的winform程序类必须继承 INotifyPropertyChanged并实现该接口才能实现双向绑定),当没有virtual属性时,只有主动调用 this.RaisePropertyChanged(x => x.你的属性)才会向视图层传递PropertyChanged消息。
下面例子:
viewmodel

public class ViewModel {

		public  virtual  string Test {get;private set ; }//标准
		
		[Bindable(false)]
		public  virtual  string Test1 {get; private set ; }  //取消 bindable property generation支持
		
		string testCore;
		[BindableProperty]  //带backing  field的属性将被忽略,使用该显示标记以使该属性支持databinding
		public virtual string Test2
		{
		    get { return testCore; }
		    set { testCore = value; }
		}
}
 

view层

    TextEdit editor = new TextEdit();
    editor.Parent = this;
 	mvvmContext.ViewModelType = typeof(ViewModel);
    // Data binding for the Title property (via MVVMContext API)
    var fluentAPI = mvvmContext.OfType<ViewModel>();//支持 fluentAPI特性
    fluentAPI.SetBinding(editor, e => e.EditValue, x => x.Test);//双向绑定
     fluentAPI.SetTrigger(x => x.Test, (active) =>
    {
        label.Text = active;  //UI Triggers 单向绑定
    });
 

 

Command

委托Command

		MVVMContext mvvmContext = new MVVMContext();
   		mvvmContext.ContainerControl = this;
		SimpleButton commandButton = new SimpleButton();
		commandButton.Parent = this;
		Func<int, bool> canExecute = (p) => (2 + 2 == p);
            // This command is created as parameterized and with `canExecute` parameter.
        DelegateCommand<int> command = new DelegateCommand<int>((v) => {
                XtraMessageBox.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;
            // UI binding for button with the `queryParameter` function
            commandButton.BindCommand(command, () => parameter);
 

 

POCO Commands

viewmodel

       public class ViewModelWithParametrizedConditionalCommand { //viewmodel
            // A parameterized POCO-command will be created from this method.
            public void DoSomething(int p) {
                XtraMessageBox.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));
            }
            // A parameterized `CanExecute` method for the `Say` command.
            public bool CanDoSomething(int p) {
                return (2 + 2) == p;  //自行修改条件
            }
        }
 

view

 	MVVMContext mvvmContext = new MVVMContext();
     mvvmContext.ContainerControl = this;
  
      SimpleButton commandButton = new SimpleButton();
      commandButton.Text = "Execute Command";
      commandButton.Dock = DockStyle.Top;
      commandButton.Parent = this;
      mvvmContext.ViewModelType = typeof(ViewModelWithParametrizedConditionalCommand);
      //
      int parameter = 4;
      // UI binding for button with the `queryParameter` function
      var fluentAPI = mvvmContext.OfType<ViewModelWithParametrizedConditionalCommand>();
      fluentAPI.BindCommand(commandButton, (x, p) => x.DoSomething(p), x => parameter);
 

异步command

两个按钮控制进度条滚动开始停止
viewmodel

    public class ViewModelWithAsyncCommandAndCancellation {
            // An asynchronous POCO-command will be created from this method.
            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;
                        System.Threading.Thread.Sleep(25); // do some work here
                        UpdateProgressOnUIThread(i);
                    }
                    UpdateProgressOnUIThread(0);
                });
            }
            // Property for progress
            public int Progress { get; private set; }
            protected IDispatcherService DispatcherService {
                get { return this.GetService<IDispatcherService>(); }
            }
            void UpdateProgressOnUIThread(int progress) {
                DispatcherService.BeginInvoke(() => {
                    Progress = progress;
                    this.RaisePropertyChanged(x => x.Progress);
                });
            }
        }
 

view

			 MVVMContext mvvmContext = new MVVMContext();
            mvvmContext.ContainerControl = this;
        
            ProgressBarControl progressBar = new ProgressBarControl();
            progressBar.Dock = DockStyle.Top;
        
            SimpleButton commandButton = new SimpleButton();
            commandButton.Text = "Start Command Execution";
            commandButton.Dock = DockStyle.Top;
        
            SimpleButton cancelButton = new SimpleButton();
            cancelButton.Text = "Cancel Command Execution";
            cancelButton.Dock = DockStyle.Top;
        
            cancelButton.Parent = this;
            commandButton.Parent = this;
            progressBar.Parent = this;

            mvvmContext.ViewModelType = typeof(ViewModelWithAsyncCommandAndCancellation);
            var fluentAPI = mvvmContext.OfType<ViewModelWithAsyncCommandAndCancellation>();
            // UI binding for the button
            fluentAPI.BindCommand(commandButton, x => x.DoSomethingAsynchronously());
            // UI binding for cancelation
            fluentAPI.BindCancelCommand(cancelButton, x => x.DoSomethingAsynchronously());
            // UI binding for progress
            fluentAPI.SetBinding(progressBar, p => p.EditValue, x => x.Progress);
 

WithCommand extension

四种常见使用情况

viewmodel

    public class ViewModelWithAsyncCommandAndCancellation {
            // An asynchronous POCO-command will be created from this method.
            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;
                        System.Threading.Thread.Sleep(25); // do some work here
                        UpdateProgressOnUIThread(i);
                    }
                    UpdateProgressOnUIThread(0);
                });
            }
            // Property for progress
            public int Progress { get; private set; }
            protected IDispatcherService DispatcherService {
                get { return this.GetService<IDispatcherService>(); }
            }
            void UpdateProgressOnUIThread(int progress) {
                DispatcherService.BeginInvoke(() => {
                    Progress = progress;
                    this.RaisePropertyChanged(x => x.Progress);
                });
            }
        }
 

view

 			MVVMContext mvvmContext = new MVVMContext();
            mvvmContext.ContainerControl = this;
        
            ProgressBarControl progressBar = new ProgressBarControl();
            progressBar.Dock = DockStyle.Top;
             
             SimpleButton commandButton2 = new SimpleButton();
            commandButton2.Text = "Execute Command 2";
            commandButton2.Dock = DockStyle.Top;
            commandButton2.Parent = this;
            commandButton2.Visible = false;
        
            SimpleButton commandButton1 = new SimpleButton();
            commandButton1.Text = "Execute Command 1";
            commandButton1.Dock = DockStyle.Top;
            commandButton1.Parent = this;
        
            SimpleButton commandButton = new SimpleButton();
            commandButton.Text = "Start Command Execution";
            commandButton.Dock = DockStyle.Top;
        
            SimpleButton cancelButton = new SimpleButton();
            cancelButton.Text = "Cancel Command Execution";
            cancelButton.Dock = DockStyle.Top;
        
            cancelButton.Parent = this;
            commandButton.Parent = this;
            progressBar.Parent = this;

            mvvmContext.ViewModelType = typeof(ViewModelWithAsyncCommandAndCancellation);
            var fluentAPI = mvvmContext.OfType<ViewModelWithAsyncCommandAndCancellation>();
            // UI binding for buttons
            fluentAPI.WithCommand(x => x.DoSomethingAsynchronously())  //功能如上一例子
                .Bind(commandButton)
                .BindCancel(cancelButton);

		    fluentAPI.WithCommand(x => x.DoSomething())  //多控件绑定一command
		        .Bind(commandButton1)
		        .Bind(commandButton2);
		   fluentAPI.WithCommand(x => x.DoSomething()) //单绑定
       			 .Bind(commandButton1);
		  fluentAPI.WithCommand(x => x.DoSomething())//OnCanExecuteChanged,Before,After  三种command triggers
		        .OnCanExecuteChanged(() => XtraMessageBox.Show("The CanExecute condition has changed"));
			//	 .Before(() => XtraMessageBox.Show("The target command is about to be executed"));
		//		.After(() => XtraMessageBox.Show("The target command has been executed"));

            // UI binding for progress
            fluentAPI.SetBinding(progressBar, p => p.EditValue, x => x.Progress);
 

Attaching Behaviors

Confirmation behavior.

checkBox修改的再确认
view

            MVVMContext mvvmContext = new MVVMContext();
            mvvmContext.ContainerControl = this;
        
            CheckEdit editor = new CheckEdit();
            editor.Dock = DockStyle.Top;
            editor.Text = "Please, try to change checked state of this editor";
            editor.Parent = this;
        
            #endregion SetUp

            #region #confirmationBehaviorFluentAPI
            // UI binding for the generic ConfirmationBehavior behavior with some specific parameters
            mvvmContext.WithEvent<ChangingEventArgs>(editor, "EditValueChanging")
                .Confirmation(behavior =>
                {
                    behavior.Caption = "CheckEdit State changing";
                    behavior.Text = "This checkEdit's checked-state is about to be changed. Are you sure?";
                });
 

Event To Command.

事件转命令,效果与POCO Commands例子一致
viewmodel

       public class ViewModelWithParametrizedConditionalCommand { //viewmodel
            // A parameterized POCO-command will be created from this method.
            public void DoSomething(int p) {
                XtraMessageBox.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));
            }
            // A parameterized `CanExecute` method for the `Say` command.
            public bool CanDoSomething(int p) {
                return (2 + 2) == p;  //自行修改条件
            }
        }
 

view

 	MVVMContext mvvmContext = new MVVMContext();
     mvvmContext.ContainerControl = this;
  
      SimpleButton commandButton = new SimpleButton();
      commandButton.Text = "Execute Command";
      commandButton.Dock = DockStyle.Top;
      commandButton.Parent = this;
      mvvmContext.ViewModelType = typeof(ViewModelWithParametrizedConditionalCommand);
      //
      int parameter = 4;
      // UI binding for button with the `queryParameter` function
      var fluentAPI = mvvmContext.OfType<ViewModelWithParametrizedConditionalCommand>();
		fluentAPI.WithEvent(commandButton, "Click")
		               .EventToCommand((x) => x.DoSomething(new int()), x => parameter);
 

Key(s)-To-Command

按键转命令
viewModel

 public class KeyAwareViewModel {
            protected IMessageBoxService MessageBoxService {
                get { return this.GetService<IMessageBoxService>(); }
            }
            public void OnAKey() {
                MessageBoxService.ShowMessage("Key Command: A");
            }
            public void OnAltAKey() {
                MessageBoxService.ShowMessage("Key Command: Alt+A");
            }
            public void OnKey(Keys keys) {
                MessageBoxService.ShowMessage("Key Command: " + keys.ToString());
            }
            public void OnKeyArgs(KeyEventArgs args) {
                string message = string.Join(", ",
                    "KeyValue: " + args.KeyValue.ToString(),
                    "KeyData: " + args.KeyData.ToString(),
                    "KeyCode: " + args.KeyCode.ToString(),
                    "Modifiers: " + args.Modifiers.ToString());
                MessageBoxService.ShowMessage("Args = {" + message + "}");
            }
        }
 

view

            MVVMContext mvvmContext = new MVVMContext();
            mvvmContext.ContainerControl = this;
        
            UserControl panel = new UserControl();
            panel.Dock = DockStyle.Top;
            panel.Parent = this;
        
            MemoEdit memo = new MemoEdit();
            memo.Dock = DockStyle.Fill;
            memo.ReadOnly = true;
            memo.MinimumSize = new Size(0, 100);
            memo.Parent = panel;

            memo.Text = "Click here and press the A or Alt+A keys to execute a command";
            //
            mvvmContext.ViewModelType = typeof(KeyAwareViewModel);
            // UI binding for the KeyToCommand behavior
            mvvmContext.OfType<KeyAwareViewModel>()
                .WithKey(memo, Keys.A)  //单按键
                .KeyToCommand(x => x.OnAKey());
            mvvmContext.OfType<KeyAwareViewModel>()
                .WithKey(memo, Keys.A | Keys.Alt) //   使用|代表复选
                .KeyToCommand(x => x.OnAltAKey());
		    mvvmContext.OfType<KeyAwareViewModel>()
		        .WithKeys(memo, new Keys[] { Keys.A, Keys.B, Keys.C })  //多选择单按键
		        .KeysToCommand(x => x.OnKey(Keys.None), args => args.KeyCode);
		    // UI binding for the KeysToCommand behavior
		    mvvmContext.OfType<KeyAwareViewModel>()
		        .WithKeys(memo, new Keys[] { Keys.Shift | Keys.A, Keys.Shift | Keys.B, Keys.Shift | Keys.C })//多选择多按键
		        .KeysToCommand(x => x.OnKeyArgs((KeyEventArgs)null), args => (KeyEventArgs)args);
 

 

出处:https://blog.csdn.net/weixin_43862847/article/details/86750608

 


 

最后在附加一个我自己的使用吧!

ViewModel中的代码

        public void KeyDown(System.Windows.Forms.KeyEventArgs args)
        {
            if (args.KeyCode == System.Windows.Forms.Keys.Enter)
            {
                args.Handled = true;   //将Handled设置为true,指示已经处理过KeyPress事件
                Login();
            }
        }

 

View中的代码

            //fluent.WithEvent<KeyEventArgs>(textEdit2, "KeyDown").EventToCommand(vm => vm.KeyDown(new KeyEventArgs(Keys.Enter)));//①
            //fluent.WithEvent<KeyEventArgs>(textEdit2, "KeyDown").EventToCommand(vm => vm.KeyDown((KeyEventArgs)null));//②
            fluent.WithKeys(textEdit2, new Keys[] { Keys.Shift | Keys.A, Keys.Shift | Keys.B, Keys.Shift | Keys.C })//多选择多按键  //③
                .KeysToCommand(x => x.KeyDown((KeyEventArgs)null), args => (KeyEventArgs)args);

以上三种方式,你都可以使用,具体自己看着办。

第①和②二种方式效果一样,当光标在textEdit2中的时候,按任何键盘都会进入KeyDown的方法中

第③种方式,则是必须满足:  new Keys[] 中设置的按键才会触发KeyDown的方法

多说一句

1)在设置为窗体的事件的时候,需要设置窗体的KeyPreview属性为true

2)在Event To Command的时候,需要注意:我们平常使用的是:private void button1_Click(object sender, EventArgs e) 注意的方法签名,而在ViewModel的时候只有private void button1_Click(EventArgs e) 就可以了,调用:fluentAPI.WithEvent(commandButton, "Click").EventToCommand((x) => x.DoSomething(null));

posted on 2019-03-21 18:06  jack_Meng  阅读(3940)  评论(0编辑  收藏  举报

导航