之前時間一直在使用Caliburn.Micro這種應(yīng)用了MVVM模式的WPF框架做開發(fā),是時候總結(jié)一下了。 Caliburn.Micro(https://blog.csdn.net/lzuacm/article/details/78886436)是一個輕量級的WPF框架,簡化了WPF中的不少用法,推薦做WPF開發(fā)時優(yōu)先使用。 真正快速而熟練地掌握一門技術(shù)就可以嘗試著用最快的速度去構(gòu)建一個玩具項目(Toy project),然后不斷地優(yōu)化、重構(gòu)之。比如本文將介紹如何使用Caliburn.Micro v3.2開發(fā)出一個簡單的計算器,里面用到了C#中的async異步技術(shù),Caliburn.Micro中的Conductor等等~ >>>1.在VS中創(chuàng)建WPF項目<<<
>>>2.使用NuGet包管理工具為當前項目安裝Caliburn.Micro <<< 對于Caliburn.Micro 1.x和2.x版,只能使用.dll,需手動給項目加Reference。而3.0以后的版本可使用NuGet包管理工具來管理,安裝和卸載既方便又徹底,推薦使用。(ps: NuGet之于Visual Studio(C++, C#等), 猶pip之于Python, npm之于node, maven之于Java, gem之于Ruby等等) 刪除項目根目錄下的MainWindow.xaml 按下圖調(diào)整 App.xaml 刪除語句 StartupUri="MainWindow.xmal"。
填充Application.Resources
<Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary> <local:Bootstrapper x:Key="bootstrapper"/> </ResourceDictionary> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources> 4 . 創(chuàng)建Bootstrapper類 然后讓其繼承自BootstrapperBase類,并加上構(gòu)造函數(shù),另外再重寫函數(shù)OnStartup即可。
using System.Windows; using Caliburn.Micro; using CaliburnMicro_Calculator.ViewModels;
namespace CaliburnMicro_Calculator { public class Bootstrapper : BootstrapperBase { public Bootstrapper() { Initialize(); }
protected override void OnStartup(object obj, StartupEventArgs e) { DisplayRootViewFor<ShellViewModel>(); } } }
5 . 在項目目錄下新建Models, ViewModels, Views這3個文件夾 在ViewModel文件夾中添加ShellViewModel.cs,并創(chuàng)建Left, Right和Result這3個屬性。 需要注意的是 ShellViewModel.cs需要繼承類 Screen 和 INotifyPropertyChanged(用于感知并同步所綁定屬性的變化),ShellViewModel具體代碼為:
using System.ComponentModel; using System.Threading; using System.Windows; using System.Windows.Controls; using Caliburn.Micro;
namespace CaliburnMicro_Calculator.ViewModels { public class ShellViewModel : Screen, INotifyPropertyChanged { private double _left; private double _right; private double _result;
public double Left { get { return _left; } set { _left = value; NotifyOfPropertyChange(); } }
public double Right { get { return _right; } set { _right = value; NotifyOfPropertyChange(); } }
public double Result { get { return _result; } set { _result = value; NotifyOfPropertyChange(); } } } 說明: 最開始布局xaml時,設(shè)計位置時采用的是左(operand 1), 中(operand 2), 右(result),于是屬性值使用了Left, Right和Result。 >>>4.設(shè)計XAML并綁定屬性 < < < 在Views文件夾中創(chuàng)建Window,命名為ShellView.xaml,在Views文件夾下創(chuàng)建子文件夾Images,用于存放+,-,*,/這4種操作對應(yīng)的小圖標,其具體代碼如下: <Window x:Class="CaliburnMicro_Calculator.Views.ShellView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas./markup-compatibility/2006" xmlns:local="clr-namespace:CaliburnMicro_Calculator.Views" xmlns:cal="http://www." mc:Ignorable="d" Title="Calculator" SizeToContent="Height" Width="240">
<StackPanel Background="Beige"> <StackPanel Orientation="Horizontal"> <Label Margin="10" Target="{Binding ElementName=left}"> Operand _1: </Label> <TextBox Margin="10" Width="72" x:Name="left"/> </StackPanel> <StackPanel Orientation="Horizontal"> <Label Margin="10" Target="{Binding ElementName=right}"> Operand _2: </Label> <TextBox Margin="10" Width="72" x:Name="right"/> </StackPanel> <StackPanel Orientation="Horizontal"> <Button Margin="10" x:Name="btnPlus" cal:Message.Attach="[Event Click]=[Action Plus(left.Text, right.Text):result.Text]"> <Image Source="Images/op1.ICO"/> </Button>
<Button Margin="10" x:Name="btnMinus" cal:Message.Attach="[Event Click]=[Action Minus(left.Text, right.Text):result.Text]"> <Image Source="Images/op2.ICO"/> </Button>
<Button Margin="10" x:Name="btnMultiply" cal:Message.Attach="[Event Click]=[Action Multipy(left.Text, right.Text):result.Text]"> <Image Source="Images/op3.ICO"/> </Button>
<Button Margin="10" x:Name="btnDivide" IsEnabled="{Binding Path=CanDivide}" cal:Message.Attach="[Event Click]=[Action Divide(left.Text, right.Text):result.Text]"> <Image Source="Images/op4.ICO"/> </Button>
</StackPanel> <StackPanel Orientation="Horizontal"> <Label Margin="10"> Answer: </Label> <TextBox Margin="10" Width="72" Text ="{Binding Path=Result, StringFormat={}{0:F4}}" IsReadOnly="True" /> </StackPanel> </StackPanel> </Window> 說明:對操作數(shù)Operand _1和Operand _2,按Alt鍵+數(shù)字可以選中該處,這是WPF的一個特殊用法。由于計算結(jié)果不希望被修改,于是加上了屬性IsReadOnly="True" 。 由于暫時只打算實現(xiàn)+, -, *, /四種操作,于是我們只需創(chuàng)建相應(yīng)的4個函數(shù)即可,由于除數(shù)是0這個操作不允許,于是需再加個判斷函數(shù)CanDivide。
Caliburn.Micro中綁定事件的寫法是:
cal:Message.Attach="[Event E]=[Action A]" (E是操作,比如Click, MouseDown, KeyDown等等,A是ViewModel中具體的函數(shù)。) 向ShellViewModel中加入事件中要做的事,此時ShellViewModel為: using System.ComponentModel; using System.Threading; using System.Windows; using System.Windows.Controls; using Caliburn.Micro;
namespace CaliburnMicro_Calculator.ViewModels { public class ShellViewModel : Screen, INotifyPropertyChanged { private double _left; private double _right; private double _result;
public double Left { get { return _left; } set { _left = value; NotifyOfPropertyChange(); } }
public double Right { get { return _right; } set { _right = value; NotifyOfPropertyChange(); } }
public double Result { get { return _result; } set { _result = value; NotifyOfPropertyChange(); } } public bool CanDivide(double left, double right) { return right != 0; }
public async void Divide(double left, double right) { Thread.Sleep(600); if (CanDivide(left, right) == true) Result = left / right; else MessageBox.Show("Divider cannot be zero.", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning); }
public async void Plus(double left, double right) { Result = left + right; }
public async void Minus(double left, double right) { Result = left - right; }
public async void Multipy(double left, double right) { Result = left * right; } } } 此時計算器的功能已基本完成,但我們可以對ViewModel進行適當?shù)恼{(diào)整: 1.創(chuàng)建新的ViewModel - CalculatorViewModel,將原來的ShellViewModel中具體的計算邏輯移入到CalculatorViewModel中; 2.此時讓ShellViewModel繼承Conductor<Object>,于是ShellViewModel擁有了管理Screen實例的功能(ViewModel中使用ActivateItem函數(shù),而View中使用X:Name="ActivateItem"標簽),其具體代碼為: using System.ComponentModel; using System.Threading; using System.Windows; using System.Windows.Controls; using Caliburn.Micro;
namespace CaliburnMicro_Calculator.ViewModels { public class ShellViewModel : Conductor<object> { public ShellViewModel() { } public void ShowCalculator() { ActivateItem(new CalculatorViewModel()); } } } 此時,CalculatorViewModel的具體代碼為: using System.ComponentModel; using System.Threading; using System.Windows; using Caliburn.Micro;
namespace CaliburnMicro_Calculator.ViewModels { public class CalculatorViewModel: Screen, INotifyPropertyChanged { private double _left; private double _right; private double _result;
public double Left { get { return _left; } set { _left = value; NotifyOfPropertyChange(); } }
public double Right { get { return _right; } set { _right = value; NotifyOfPropertyChange(); } }
public double Result { get { return _result; } set { _result = value; NotifyOfPropertyChange(); } }
public CalculatorViewModel() { }
public bool CanDivide(double left, double right) { return right != 0; }
public async void Divide(double left, double right) { Thread.Sleep(600); if (CanDivide(left, right) == true) Result = left / right; else MessageBox.Show("Divider cannot be zero.", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning); }
public async void Plus(double left, double right) { Result = left + right; }
public async void Minus(double left, double right) { Result = left - right; }
public async void Multipy(double left, double right) { Result = left * right; } } } 3 . 對于View,只需把CalculatorViewModel對應(yīng)的CalculatorView作為ContentControl控件嵌入ShellView即可。此時ShellView的代碼調(diào)整為: <Window x:Class="CaliburnMicro_Calculator.Views.ShellView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas./markup-compatibility/2006" xmlns:local="clr-namespace:CaliburnMicro_Calculator.Views" xmlns:cal="http://www." mc:Ignorable="d" Title="Calculator" SizeToContent="Height" Width="240">
<Grid MinHeight="200"> <Button Content="Show Calculator" x:Name="ShowCalculator" Grid.Row="0"></Button> <ContentControl x:Name="ActiveItem"></ContentControl> </Grid> </Window> 另外提一點,向ViewModel A中嵌入ViewModel B,一般來說需要做的操作是: 在A的view中使用ContentControl,綁定B的ViewModel只需使用語句cal:View.Model="{Binding BViewModel}"即可,而B的view是UserControl就可以啦。 此時CalculatorView是一個UserControl,其代碼為: <UserControl x:Class="CaliburnMicro_Calculator.Views.CalculatorView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas./markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:CaliburnMicro_Calculator.Views" xmlns:cal="http://www." mc:Ignorable="d" Width="240">
<StackPanel Background="Beige"> <StackPanel Orientation="Horizontal"> <Label Margin="10" Target="{Binding ElementName=left}"> Operand _1: </Label> <TextBox Margin="10" Width="72" x:Name="left"/> </StackPanel> <StackPanel Orientation="Horizontal"> <Label Margin="10" Target="{Binding ElementName=right}"> Operand _2: </Label> <TextBox Margin="10" Width="72" x:Name="right"/> </StackPanel> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <Button Margin="10" x:Name="btnPlus" cal:Message.Attach="[Event Click]=[Action Plus(left.Text, right.Text):result.Text]"> <Image Source="Images/op1.ICO"/> </Button>
<Button Margin="10" x:Name="btnMinus" cal:Message.Attach="[Event Click]=[Action Minus(left.Text, right.Text):result.Text]"> <Image Source="Images/op2.ICO"/> </Button>
<Button Margin="10" x:Name="btnMultiply" cal:Message.Attach="[Event Click]=[Action Multipy(left.Text, right.Text):result.Text]"> <Image Source="Images/op3.ICO"/> </Button>
<Button Margin="10" x:Name="btnDivide" IsEnabled="{Binding Path=CanDivide}" cal:Message.Attach="[Event Click]=[Action Divide(left.Text, right.Text):result.Text]"> <Image Source="Images/op4.ICO"/> </Button>
</StackPanel> <StackPanel Orientation="Horizontal"> <Label Margin="10"> Answer: </Label> <TextBox Margin="10" Width="72" Text ="{Binding Path=Result, StringFormat={}{0:F4}, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="True" /> </StackPanel> </StackPanel> </UserControl> 好啦,就醬,由于本例中邏輯并不復(fù)雜,Model暫時用不上,對于復(fù)雜一點的項目,Model主要負責(zé)數(shù)據(jù)的讀取,如文件操作、數(shù)據(jù)庫操作、service調(diào)用等,以后有機會舉例具體來說。 如果需要持久化(persistent),則還需給給每對M-VM(Model和ViewModel)加入State,這個實際工程中也用得特別多。 Calculator主頁:
點擊按鈕“ShowCalculator”即可看到具體的計算器~ 乘法舉例:
除法舉例:
最后附上代碼: CaliburnMicro-Calculator: A simple Calculator using Caliburn.Micro https://github.com/yanglr/CaliburnMicro-Calculator, 歡迎fork和star,如有改進意見歡迎提交pull request~ 原文地址: https://blog.csdn.net/lzuacm/article/details/80559517
更多精彩文章,歡迎訪問本人博客https://enjoy233.cnblogs.com 或 知乎搜索「Bravo Yeung」. 歡迎轉(zhuǎn)發(fā)到朋友圈,公眾號轉(zhuǎn)載請后臺聯(lián)系本人申請授權(quán)~
|