Devexpress Winform MVVM

概括總結備忘
express

(Devexpress Winform MVVM Practice)

 我本身的使用設計模式

前言

MVVM

       MVVM是Model-View-ViewModel,是一種專爲WPF開發而設計的架構設計模式,相似MVC。MVVM 就是將其中的View 的狀態和行爲抽象化,讓咱們將視圖 UI 和業務邏輯分開。 ViewModel 根據databindings,commond,notification等與view層鏈接,能夠取出 Model 的數據同時幫忙處理 View 中因爲須要展現內容而涉及的業務邏輯,充當視圖層與數據層的通訊橋樑。
在這裏插入圖片描述
好處:markdown

  • 邏輯低耦合
  • 可重用性
  • 方便單元測試

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消息。
下面例子:
viewmodelasync

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層mvvm

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

相關文章
相關標籤/搜索