前些時間研究了WPF的一些框架,感受基於Prism框架的MVVM模式對系統的UI與邏輯分離很好,因此就按照以前Winform的框架設計,用WPF作了一套,感受比Winform要強不少。框架
MVVM模式和MVC模式同樣,主要目的是分離視圖(View)和模型(Model),有幾大優勢ide
1. 低耦合。視圖(View)能夠獨立於Model變化和修改,一個ViewModel能夠綁定到不一樣的"View"上,當View變化的時候Model能夠不變,當Model變化的時候View也能夠不變。測試
2. 可重用性。你能夠把一些視圖邏輯放在一個ViewModel裏面,讓不少view重用這段視圖邏輯。ui
3. 獨立開發。開發人員能夠專一於業務邏輯和數據的開發(ViewModel),設計人員能夠專一於頁面設計,使用Expression Blend能夠很容易設計界面並生成xaml代碼。this
4. 可測試。界面素來是比較難於測試的,而如今測試能夠針對ViewModel來寫。spa
MVVM功能圖:設計
如今我作了個實例,整合模仿Office 2010的Ribbon效果:3d
(1)Blue:code
(2)Silvercomponent
(3)Black
我把框架的View設計和框架設計簡單介紹一下。
View的XAML源碼:
<!--<Window x:Class="TLAgent.Ribbon.App.Demo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> </Grid> </Window>--> <Fluent:RibbonWindow x:Class="TLAgent.Ribbon.App.Demo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Fluent="clr-namespace:Fluent;assembly=Fluent" xmlns:ad="http://schemas.xceed.com/wpf/xaml/avalondock" xmlns:vm="clr-namespace:TLAgent.Ribbon.App.Demo" Title="TLAgent.Ribbon.Application" Width="500" Height="250" Background="#FFEBEDF0" x:Name="window" WindowState="Maximized" WindowStartupLocation="CenterScreen" Icon="/TLAgent.Ribbon.App.Demo;component/Images/usergroup.ico"> <Grid x:Name="layoutRoot"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Fluent:Ribbon Grid.Row="0"> <!--Add QuickAccess--> <Fluent:Ribbon.QuickAccessItems> <Fluent:QuickAccessMenuItem Target="{Binding ElementName=ButtonGreen}"/> <Fluent:QuickAccessMenuItem Target="{Binding ElementName=ButtonGray}"/> </Fluent:Ribbon.QuickAccessItems> <!--Add Tabs--> <Fluent:RibbonTabItem Header="Home" ReduceOrder="(P),(P),(P),(P),(P)"> <Fluent:RibbonGroupBox Header="Add / Remove"> <Fluent:Button Header="Add" Command="{Binding AddFunctionCommand}" Icon="Images\Green.png" LargeIcon="Images\GreenLarge.png" Name="ButtonGreen" /> <Fluent:Button Header="Remove" Command="{Binding OpenCommand}" Icon="Images\Gray.png" LargeIcon="Images\GrayLarge.png" Name="ButtonGray" /> </Fluent:RibbonGroupBox> <Fluent:RibbonGroupBox Header="Modify"> <Fluent:Button Header="Add" Command="{Binding AddFunctionCommand}" Icon="Images\Green.png" LargeIcon="Images\GreenLarge.png" Name="ButtonGreen1" /> <Fluent:Button Header="Remove" Command="{Binding OpenCommand}" Icon="Images\Gray.png" LargeIcon="Images\GrayLarge.png" Name="ButtonGray1" /> </Fluent:RibbonGroupBox> </Fluent:RibbonTabItem> <Fluent:RibbonTabItem Header="用戶管理" ReduceOrder="(P),(P),(P),(P),(P)"> <Fluent:RibbonGroupBox Header="User Management"> <Fluent:Button Header="New User" Icon="Images\Pink.png" LargeIcon="Images\PinkLarge.png" Name="ButtonAddUser" /> <Fluent:Button Header="Modify User" Icon="Images\Orange.png" LargeIcon="Images\OrangeLarge.png" Name="ButtonModiryUser" /> </Fluent:RibbonGroupBox> </Fluent:RibbonTabItem> <!--Backstage Items--> <Fluent:Ribbon.Menu> <Fluent:Backstage Background="Gray"> <Fluent:BackstageTabControl> <Fluent:Button Header="退出系統" Command="{Binding ExitSystemCommand}" Icon="Images\close.png"/> </Fluent:BackstageTabControl> </Fluent:Backstage> </Fluent:Ribbon.Menu> </Fluent:Ribbon> <ad:DockingManager x:Name="dockManager" Grid.Row="1"> <ad:DockingManager.Theme> <ad:ExpressionBlueTheme/> </ad:DockingManager.Theme> <ad:LayoutRoot> <ad:LayoutPanel Orientation="Vertical"> <ad:LayoutDocumentPane/> <ad:LayoutAnchorablePane Name="ToolsPane" DockHeight="150"> </ad:LayoutAnchorablePane> </ad:LayoutPanel> </ad:LayoutRoot> </ad:DockingManager> <StatusBar VerticalAlignment="Bottom" Height="23" Grid.Row="2" > <StatusBarItem VerticalContentAlignment="Center"> <TextBlock x:Name="TxtMessage" Foreground="{Binding ForeColor}" FontWeight="Bold" Text="{Binding ExecuteMessage}"/> </StatusBarItem> </StatusBar> </Grid> <!--<Window.DataContext> <vm:WorkspaceViewModel /> </Window.DataContext>--> </Fluent:RibbonWindow>
ViewModel源碼:
/************************************************************************ AvalonDock Copyright (C) 2007-2013 Xceed Software Inc. This program is provided to you under the terms of the New BSD License (BSD) as published at http://avalondock.codeplex.com/license For more features, controls, and fast professional support, pick up AvalonDock in Extended WPF Toolkit Plus at http://xceed.com/wpf_toolkit Stay informed: follow @datagrid on Twitter or Like facebook.com/datagrids **********************************************************************/ using System; using System.ComponentModel; using System.Linq; using System.Windows.Media; using Microsoft.Practices.Prism.Commands; using Microsoft.Practices.Prism.ViewModel; using System.Windows; using TLAgent.WPF.Theme; using Xceed.Wpf.AvalonDock; using Xceed.Wpf.AvalonDock.Layout; namespace TLAgent.Ribbon.App.Demo { class WorkspaceViewModel : NotificationObject { public DelegateCommand AddFunctionCommand { get; set; } public DelegateCommand ExitSystemCommand { get; set; } public DelegateCommand OpenCommand { get; set; } private DockingManager _dockingManager; public WorkspaceViewModel() { _dockingManager = MainWindow.DockingManager; AddFunctionCommand = new DelegateCommand(this.OnNew); ExitSystemCommand = new DelegateCommand(this.OnExit); OpenCommand = new DelegateCommand(this.OnOpen); } private void OnOpen() { //string frameworkPath = string.Format("/Fluent;component/Themes/Office2010/{0}.xaml", ThemeStyle.Silver);//主框架的樣式文件 //Application.Current.Resources.MergedDictionaries.Clear(); //設置界面控件的樣式 //設置界面框架的樣式 //Application.Current.Resources.MergedDictionaries.Add((ResourceDictionary)(Application.LoadComponent(new Uri(frameworkPath, UriKind.RelativeOrAbsolute)))); } private string _executeMessage; public string ExecuteMessage { get { return _executeMessage; } set { _executeMessage = value; this.RaisePropertyChanged("ExecuteMessage"); } } private Brush _foreColor; public Brush ForeColor { get { return _foreColor; } set { _foreColor = value; this.RaisePropertyChanged("ForeColor"); } } private void OnNew() { string functionName = "項目管理"; CreateSystemTab(functionName); ForeColor = new SolidColorBrush(Colors.White); var leftAnchorGroup = _dockingManager.Layout.LeftSide.Children.FirstOrDefault(); if (leftAnchorGroup == null) { leftAnchorGroup = new LayoutAnchorGroup(); _dockingManager.Layout.LeftSide.Children.Add(leftAnchorGroup); } leftAnchorGroup.Children.Add(new LayoutAnchorable() { Title = "New Anchorable" }); ExecuteMessage = "成功新建,Tabs:" + functionName; } private void CreateSystemTab(string tabName) { var firstDocumentPane =_dockingManager.Layout.Descendents().OfType<LayoutDocumentPane>().FirstOrDefault(); if (firstDocumentPane != null) { LayoutDocument doc2 = new LayoutDocument(); AddUserWindow control1 = new AddUserWindow(); doc2.Title = tabName; doc2.Content = control1; doc2.IsActive = true; firstDocumentPane.Children.Add(doc2); } } private void OnExit() { MessageBoxResult result = MessageBox.Show("肯定要退出系統嗎?", "確認消息", MessageBoxButton.OKCancel, MessageBoxImage.Question); if (result == MessageBoxResult.OK) { Application.Current.Shutdown(); var serializer = new Xceed.Wpf.AvalonDock.Layout.Serialization.XmlLayoutSerializer(_dockingManager); serializer.Serialize(@".\AvalonDock.config"); } } public event PropertyChangedEventHandler PropertyChanged; } }
更換主題:提供統一的接口就能夠實現整合框架和控件主題更換。
/// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : RibbonWindow { public static DockingManager DockingManager; public MainWindow() { InitializeComponent(); DockingManager = dockManager; ThemeManager.ChangeTheme(dockManager, ThemeStyle.Black);//更換主題接口 this.DataContext = new WorkspaceViewModel(); this.Loaded += new RoutedEventHandler(MainWindow_Loaded); this.Unloaded += new RoutedEventHandler(MainWindow_Unloaded); }
項目解決方案圖:
如今只稍微提一下,後續有時間再把更詳細的設計方法說明。