由於菜單和Tab控件一塊兒用,因此就拿出來一塊寫express
定義一個名爲ViewItem類,定義Tab的屬性ide
1 /// <summary> 2 /// tab item 3 /// </summary> 4 public class ViewItem 5 { 6 /// <summary> 7 /// 標題 8 /// </summary> 9 public string Header 10 { 11 get; 12 set; 13 } 14 15 /// <summary> 16 /// 主鍵名稱 17 /// </summary> 18 public string KeyName 19 { 20 get; 21 set; 22 } 23 24 /// <summary> 25 /// the show Control 26 /// </summary> 27 public object ViewControl 28 { 29 get; 30 set; 31 } 32 }
TabControl用的是Dev的控件,因此先要引用三個Dev的DLL,分別是DevExpress.Xpf.Core,DevExpress,Xpf.Docking,DevExpress.Xpf.Layout.Core。工具
新建一個UserControl,命名爲MenuControl,在主窗體的Window中須要添加一下引用測試
1 xmlns:dxd="http://schemas.devexpress.com/winfx/2008/xaml/docking"2 xmlns:c="clr-namespace:RemindWin" 3 xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core" 4 Name="MainView_View"
其中,c引用的是MenuControl所在的文件夾,由於要用到MenuControl,因此須要引用MenuControl所在的文件夾。併爲窗體命名爲「MainView_View」(後面用到)。字體
定義一個DockPanel,在其的頂端設置菜單欄,內容放TabControl,也能夠在下面放FootBar,這裏省略,主窗體代碼以下:this
1 <Grid x:Name="MainView"> 2 <DockPanel LastChildFill="True"> 4 <c:MenuControl DockPanel.Dock="Top" HorizontalAlignment="Left"/> 5 <dxd:DockLayoutManager x:Name="dockManager" UseLayoutRounding="True" AllowCustomization="False"> 6 <dxd:LayoutGroup Orientation="Vertical" > 7 <dxd:DocumentGroup Name="documentContainer" 8 SelectedTabIndex="{Binding SelectedTabIndex,Mode=TwoWay}" 9 ItemsSource="{Binding ItemList}" 10 DestroyOnClosingChildren="False" 11 Background="Azure" 12 ClosePageButtonShowMode="InActiveTabPageAndTabControlHeader"> 13 <dxd:DocumentGroup.ItemStyle> 14 <Style TargetType="dxd:DocumentPanel"> 15 <Setter Property="CloseCommand" Value="{Binding DataContext.CloseCommand,ElementName=MainView}" /> 16 <Setter Property="Caption" Value="{Binding Header}"></Setter> 17 <Setter Property="AllowFloat" Value="False"></Setter> 18 <Setter Property="AllowMaximize" Value="False"></Setter> 19 </Style> 20 </dxd:DocumentGroup.ItemStyle> 21 <dxd:DocumentGroup.ItemContentTemplate> 22 <DataTemplate> 23 <ContentControl Content="{Binding ViewControl}"></ContentControl> 24 </DataTemplate> 25 </dxd:DocumentGroup.ItemContentTemplate> 26 </dxd:DocumentGroup> 27 </dxd:LayoutGroup> 28 </dxd:DockLayoutManager> 29 </DockPanel> 30 </Grid>
binding了Model中的ItemList,這是Tab集合,SelectedTabIndex是所選Tab的索引,爲Int型,每一個Tab有一個Close事件,Binding後臺代碼中的CloseCommand事件,屬性Caption,banding了ViewItem中的Header,每一個Tab的內容ContentControl綁定了ViewItem中的ViewControl,其中須要注意Grid的Name與<Setter>裏面的元素一致。spa
添加主窗體的ViewModel,命名MainViewModel,定義ItemList和SelectedTabIndex(引用了SimpleMvvmToolkit)3d
1 #region Properties 2 3 // 4 private ObservableCollection<ViewItem> _itemList = new ObservableCollection<ViewItem>(); 5 /// <summary> 6 /// Tab頁集合 7 /// </summary> 8 public ObservableCollection<ViewItem> ItemList 9 { 10 get 11 { 12 return _itemList; 13 } 14 set 15 { 16 _itemList = value; 17 } 18 } 19 20 //所選tab索引 21 private int _selectedTabIndex = 0; 22 /// <summary> 23 /// 所選tab索引 24 /// </summary> 25 public int SelectedTabIndex 26 { 27 get 28 { 29 return _selectedTabIndex; 30 } 31 set 32 { 33 _selectedTabIndex = value; 34 NotifyPropertyChanged(x => x.SelectedTabIndex); 35 } 36 } 37 38 #endregion
定義添加Tab的方法,定義了兩個,一個ViewModel是爲類型的方法,一個ViewModel以參數形式的方法,第二種主要是針對一些須要傳參的ViewModel,這裏有一個規則,若是一個須要添加一個已有的Tab頁,會自動跳到該Tab頁,而不是出現兩個同樣的Tab頁,這裏主要依靠KeyName作判斷(也可添加兩個同樣的Tab,代碼需簡單修改)code
1 /// <summary> 2 /// 添加Tab頁 3 /// </summary> 4 /// <typeparam name="TView">View</typeparam> 5 /// <typeparam name="KMode">ViewModel</typeparam> 6 /// <param name="header">對應的標題</param> 7 /// <param name="systemFunction">View的類型</param> 8 public void OpenNewItem<TView, KMode>(string header, string systemFunction) 9 where TView : System.Windows.Controls.UserControl, new() 10 where KMode : new() 11 { 12 string keyName = systemFunction.ToString(); 13 var q = ItemList.Where(x => x.KeyName == keyName).FirstOrDefault(); 14 int index = 0; 15 if (q == null) 16 { 17 KMode vModel = new KMode(); 18 TView view = new TView(); 19 ViewItem item = new ViewItem(); 20 item.Header = header; 21 item.KeyName = keyName; 22 item.ViewControl = view; 23 ItemList.Add(item); 24 view.DataContext = vModel; 25 index = ItemList.Count() - 1; 26 } 27 else 28 { 29 index = ItemList.IndexOf(q); 30 } 31 SelectedTabIndex = index; 32 } 33 34 /// <summary> 35 /// 添加Tab頁 36 /// </summary> 37 /// <typeparam name="TView">View</typeparam> 38 /// <param name="header">對應的標題</param> 39 /// <param name="systemFunction">View的類型</param> 40 /// <param name="_model">可帶參數的ViewModel</param> 41 public void OpenNewItemPara<TView>(string header, string systemFunction,object _model) 42 where TView : System.Windows.Controls.UserControl, new() 43 { 44 string keyName = systemFunction.ToString(); 45 var q = ItemList.Where(x => x.KeyName == keyName).FirstOrDefault(); 46 int index = 0; 47 if (q == null) 48 { 49 TView view = new TView(); 50 ViewItem item = new ViewItem(); 51 item.Header = header; 52 item.KeyName = keyName; 53 item.ViewControl = view; 54 ItemList.Add(item); 55 view.DataContext = _model; 56 index = ItemList.Count() - 1; 57 } 58 else 59 { 60 index = ItemList.IndexOf(q); 61 } 62 SelectedTabIndex = index; 63 }
定義關閉Tab的事件和方法xml
1 /// <summary> 2 /// 關閉Tab頁 3 /// </summary> 4 public DelegateCommand CloseCommand 5 { 6 get 7 { 8 return new DelegateCommand(CloseTab); 9 } 10 } 11 12 private void CloseTab() 13 { 14 if (SelectedTabIndex >= 0) 15 { 16 var q = ItemList[SelectedTabIndex]; 17 ItemList.RemoveAt(SelectedTabIndex); 18 ((System.Windows.Controls.UserControl)q.ViewControl).DataContext = null; 19 q.ViewControl = null; 20 q = null; 21 GC.Collect(); 22 GC.WaitForPendingFinalizers(); 23 GC.Collect(); 24 } 25 }
別忘了,在MainWindow.xaml.cs的構造中添加這句話
1 this.DataContext = new MainViewModel();
定義MenuItem
1 /// <summary> 2 /// 主菜單項。可添加更多屬性實現菜單的個性配置,如字體、圖片、響應等 3 /// </summary> 4 public class MenuItem : ModelBase<MenuItem> 5 { 6 public MenuItem(string item) 7 { 8 Header = item; 9 Childrens = new List<MenuItem>(); 10 } 11 12 public MenuItem(string header, string keyName) 13 { 14 Header = header; 15 KeyName = keyName; 16 } 17 18 /// <summary> 19 /// 菜單名稱,作爲菜單主鍵 20 /// </summary> 21 public string KeyName 22 { 23 get; 24 set; 25 } 26 27 /// <summary> 28 /// 菜單名 29 /// </summary> 30 public string Header { get; set; } 31 32 /// <summary> 33 /// 圖片 34 /// </summary> 35 public BitmapImage Icon { get; set; } 36 37 /// <summary> 38 /// 是否可用 39 /// </summary> 40 private bool m_IsEnabled = true; 41 /// <summary> 42 /// 菜單可用性。判斷權限後設置此屬性實現菜單的可用性控制 43 /// </summary> 44 new public bool IsEnabled 45 { 46 get 47 { 48 return m_IsEnabled; 49 } 50 set 51 { 52 m_IsEnabled = value; 53 NotifyPropertyChanged(x => x.IsEnabled); 54 } 55 } 56 57 /// <summary> 58 /// 子菜單 59 /// </summary> 60 public List<MenuItem> Childrens { get; set; } 61 62 /// <summary> 63 /// 事件 64 /// </summary> 65 public DelegateCommand<string> SelectedCommand { get; set; } 66 67 68 }
在MenuControl中定義各類格式和屬性
1 <UserControl.Resources> 2 <HierarchicalDataTemplate x:Key="ItemTemplate" 3 ItemsSource="{Binding Childrens}"> 4 <StackPanel Orientation="Horizontal" VerticalAlignment="Center"> 5 <TextBlock Text="{Binding Header}" 6 IsEnabled="{Binding IsEnabled}" VerticalAlignment="Center"/> 7 </StackPanel> 8 </HierarchicalDataTemplate> 9 <ControlTemplate x:Key="MenuSeparatorTemplate"> 10 <Separator /> 11 </ControlTemplate> 12 </UserControl.Resources> 13 14 <Menu DockPanel.Dock="Top" 15 VerticalAlignment="Top" 16 ItemsSource="{Binding MenuList}" 17 ItemTemplate="{StaticResource ItemTemplate}"> 18 19 <!-- 菜單背景色--> 20 <Menu.Background> 21 <LinearGradientBrush StartPoint="0,0" EndPoint="0,1.5"> 22 <GradientStop Color="#f2f2f2" Offset="0.5"/> 23 <GradientStop Color="#F8F9FB" Offset="01"/> 24 </LinearGradientBrush> 25 </Menu.Background> 26 <Menu.ItemContainerStyle> 27 <Style TargetType="MenuItem"> 28 <Setter Property="IsEnabled" 29 Value="{Binding IsEnabled}" /> 30 <Setter Property="Command" 31 Value="{Binding SelectedCommand}" /> 32 <Setter Property="CommandParameter" 33 Value="{Binding Header}" /> 34 <Setter Property="FontSize" Value="14"/> 35 36 <Style.Triggers> 37 <DataTrigger Binding="{Binding }" 38 Value="{x:Null}"> 39 <Setter Property="Template" 40 Value="{StaticResource MenuSeparatorTemplate}" /> 41 </DataTrigger> 42 </Style.Triggers> 43 44 </Style> 45 </Menu.ItemContainerStyle> 46 </Menu>
定義菜單的ViewModel,名爲MenuControlModel,定義屬性
1 #region Properties 2 3 private List<MenuItem> m_MainMenus = new List<MenuItem>(); 4 public List<MenuItem> MenuList 5 { 6 get 7 { 8 return m_MainMenus; 9 } 10 set 11 { 12 m_MainMenus = value; 13 NotifyPropertyChanged(x => x.MenuList); 14 } 15 } 16 17 #endregion
定義遞歸設置全部菜單的命令
1 /// <summary> 2 /// 遞歸設置全部菜單的命令 3 /// </summary> 4 /// <param name="Menus"></param> 5 void SetMenuCommand(List<MenuItem> Menus) 6 { 7 foreach (var item in Menus) 8 { 9 item.SelectedCommand = new DelegateCommand<string>(OpenMenu); 10 if (item.Childrens.Count > 0) SetMenuCommand(item.Childrens); 11 } 12 }
定義點擊菜單Item時執行的方法,其中第一個判斷是找到主窗體,再進行操做,若是菜單的Name中包含「測試」兩個字符,則使用第一個添加Tab的方法,不然,用第二種。Control_Test和TestModel分別是UserControl和其ViewModel。
1 /// <summary> 2 /// 打開方法 3 /// </summary> 4 /// <param name="MenuName"></param> 5 private void OpenMenu(string MenuName) 6 { 7 foreach (System.Windows.Window win in System.Windows.Application.Current.Windows) 8 { 9 if (win.Name == "MainView_View") 10 { 11 if (MenuName.Contains("測試")) 12 { 13 (win.DataContext as MainViewModel).OpenNewItem<UserControls.Control_Test, UserControls.TestModel>(MenuName, MenuName); 14 } 15 else 16 { 17 UserControls.TestModelSec m = new UserControls.TestModelSec(MenuName); 18 (win.DataContext as MainViewModel).OpenNewItemPara<UserControls.Control_Test>(MenuName, MenuName, m); 19 } 20 break; 21 } 22 } 23 }
初始化菜單,在「主菜單」中有四個子菜單,其中「操做」的子菜單中還有子菜單
1 private void LoadMenuData() 2 { 3 MenuItem mainMenu = new MenuItem("主菜單"); 4 MenuItem projectMenu = new MenuItem("項目"); 5 MenuItem toolMenu = new MenuItem("工具"); 6 MenuItem otherMenu = new MenuItem("其餘"); 7 MenuItem helpMenu = new MenuItem("幫助"); 8 9 mainMenu.Childrens.Add(new MenuItem("打開")); 10 mainMenu.Childrens.Add(new MenuItem("新建")); 11 12 MenuItem operateMenu = new MenuItem("操做"); 13 14 15 MenuItem saveMenu = new MenuItem("保存"); 16 MenuItem deleteMenu = new MenuItem("刪除"); 17 MenuItem readMenu = new MenuItem("讀取"); 18 operateMenu.Childrens.Add(saveMenu); 19 operateMenu.Childrens.Add(deleteMenu); 20 operateMenu.Childrens.Add(readMenu); 21 22 mainMenu.Childrens.Add(operateMenu); 23 24 mainMenu.Childrens.Add(new MenuItem("測試三")); 25 26 helpMenu.Childrens.Add(new MenuItem("測試一")); 27 helpMenu.Childrens.Add(new MenuItem("測試二")); 28 helpMenu.Childrens.Add(new MenuItem("測試三")); 29 30 MenuList.Add(mainMenu); 31 MenuList.Add(projectMenu); 32 MenuList.Add(toolMenu); 33 MenuList.Add(otherMenu); 34 MenuList.Add(helpMenu); 35 36 SetMenuCommand(MenuList); 37 }
在構造中,執行初始化菜單的方法
1 public MenuControlModel() 2 { 3 LoadMenuData(); 4 }
Tab每項的內容必須爲UserControl,UserControl無需與Model進行banding。測試的UserControl和其Model沒有寫,只是一個簡單的例子~