概括總結備忘
express
我本身的使用設計模式
MVVM是Model-View-ViewModel,是一種專爲WPF開發而設計的架構設計模式,相似MVC。MVVM 就是將其中的View 的狀態和行爲抽象化,讓咱們將視圖 UI 和業務邏輯分開。 ViewModel 根據databindings,commond,notification等與view層鏈接,能夠取出 Model 的數據同時幫忙處理 View 中因爲須要展現內容而涉及的業務邏輯,充當視圖層與數據層的通訊橋樑。
好處:markdown
DevExpress全稱Developer Express,是全球著名的控件開發公司,其.NET界面控件DXperience Universal Suite(Dev宇宙版)全球知名,獲獎無數。DevExpress控件以界面美觀和功能強大著稱,擁有大量的示例和幫助文檔,開發者可以快速上手。在國內,DevExpress亦擁有大量的用戶,資料比較完善,交流方便。DevExpress普遍應用於ECM企業內容管理、 成本管控、進程監督、生產調度,在企業/政務信息化管理中佔據一席重要之地。架構
官網:https://www.devexpress.com/框架
如上所說,mvvm專爲WPF設計,而沒有第三方MVVM框架的 winform平臺缺少靈活的綁定以及綁定commad等基本特性,必須手動實現,而devexpress爲這些特性提供了完整支持,是開發更關心自己業務邏輯。異步
在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 單向綁定 });
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);
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);
兩個按鈕控制進度條滾動開始中止
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);
四種常見使用狀況
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);
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?"; });
事件轉命令,效果與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);
按鍵轉命令
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));