一、页面添加TreeView和TabControl控件
1.在MainWindow.xaml页面上添加TreeView控件,设置ItemsSource属性为ViewModel中的TreeList属性,添加<TreeView.ItemTemplate>,在该节点下添加<HierarchicalDataTemplate>,绑定ViewModel中的Tree List下子项中的Children属性,菜单名称绑定Header属性
1 <TreeView Background="Transparent" BorderThickness="0" ItemsSource="{Binding TreeList}"> 2 <TreeView.ItemTemplate> 3 <HierarchicalDataTemplate ItemsSource="{Binding Children}"> 4 <Grid> 5 <Grid.ColumnDefinitions> 6 <ColumnDefinition Width="30" Name="c1"/> 7 <ColumnDefinition/> 8 </Grid.ColumnDefinitions> 9 <TextBlock Text="{Binding IconCode}" 10 FontFamily="{StaticResource Iconfont}" 11 VerticalAlignment="Center" 12 HorizontalAlignment="Center" 13 FontSize="18" 14 SnapsToDevicePixels="True"/> 15 16 <TextBlock Text="{Binding Header}" Grid.Column="1" Margin="5,0,0,0" FontSize="13"/> 17 </Grid> 18 <HierarchicalDataTemplate.Triggers> 19 <DataTrigger Binding="{Binding IconCode}" Value="{x:Null}"> 20 <Setter TargetName="c1" Property="Width" Value="13"/> 21 </DataTrigger> 22 </HierarchicalDataTemplate.Triggers> 23 </HierarchicalDataTemplate> 24 </TreeView.ItemTemplate> 25 </TreeView>
在<Window.Resources>资源文件中设置style样式,TargetType为TreeViewItem,并设置样式背景等属性
设置Template属性ControlTemplate,TargetType为TreeViewItem,
1 <Style TargetType="TreeViewItem"> 2 <Setter Property="IsExpanded" Value="{Binding IsExpanded,Mode=TwoWay}"/> 3 <Setter Property="Background" Value="Transparent"/> 4 <Setter Property="HorizontalContentAlignment" Value="Left"/> 5 <Setter Property="VerticalContentAlignment" Value="Center"/> 6 <Setter Property="Padding" Value="8,5"/> 7 <Setter Property="Foreground" Value="White"/> 8 <Setter Property="FontSize" Value="12"/> 9 <Setter Property="BorderThickness" Value="0"/> 10 <Setter Property="Template"> 11 <Setter.Value> 12 <ControlTemplate TargetType="TreeViewItem"> 13 <Grid Background="Transparent" Name="root"> 14 <Grid.ColumnDefinitions> 15 <ColumnDefinition/> 16 <ColumnDefinition MaxWidth="30"/> 17 </Grid.ColumnDefinitions> 18 <Grid.RowDefinitions> 19 <RowDefinition Height="Auto" MinHeight="36"/> 20 <RowDefinition /> 21 </Grid.RowDefinitions> 22 <!--响应当前节点的鼠标双击动作,并且关联到VM中的命令--> 23 <Grid.InputBindings> 24 <MouseBinding MouseAction="LeftClick" 25 Command="{Binding OpenViewCommand}" 26 CommandParameter="{Binding}"/> 27 </Grid.InputBindings> 28 29 <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" 30 BorderThickness="{TemplateBinding BorderThickness}" 31 Background="{TemplateBinding Background}" 32 CornerRadius="0" Grid.ColumnSpan="2" 33 Padding="{TemplateBinding Padding}" 34 SnapsToDevicePixels="true" 35 TextBlock.Foreground="{TemplateBinding Foreground}"> 36 <ContentPresenter x:Name="PART_Header" 37 ContentSource="Header" 38 HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 39 VerticalAlignment="{TemplateBinding VerticalContentAlignment}" 40 SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"> 41 </ContentPresenter> 42 </Border> 43 44 <ToggleButton x:Name="Expander" Grid.Column="1" ClickMode="Press" Foreground="{Binding ElementName=Bd,Path=(TextBlock.Foreground)}" 45 IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" 46 Template="{StaticResource ArrowButtonTemplate}"/> 47 <!--当前子项的 Children集合--> 48 <ItemsPresenter x:Name="ItemsHost" Margin="18,0,0,0" Grid.ColumnSpan="2" Grid.Row="1" /> 49 </Grid> 50 51 <ControlTemplate.Triggers> 52 <Trigger Property="IsExpanded" Value="false"> 53 <Setter Property="Visibility" TargetName="ItemsHost" Value="Collapsed"/> 54 </Trigger> 55 <DataTrigger Binding="{Binding Children.Count}" Value="0"> 56 <Setter Property="Visibility" TargetName="Expander" Value="Hidden"/> 57 </DataTrigger> 58 <Trigger Property="IsSelected" Value="true"> 59 <Setter Property="Background" TargetName="Bd" Value="#F7F9FA"/> 60 <Setter Property="Foreground" Value="#0b3d90"/> 61 <Setter Property="Foreground" Value="#0b3d90" TargetName="Expander"/> 62 </Trigger> 63 </ControlTemplate.Triggers> 64 </ControlTemplate> 65 </Setter.Value> 66 </Setter> 67 </Style>
2.增加TabControl控件,设置相关属性;在TabControl控件下增加子节点<TabControl.ItemContainerStyle>,<TabControl.ItemTemplate >文本标题,<TabControl.ContentTemplate>页面内容
1 <TabControl x:Name="TabControlItem" 2 Grid.Row="1" 3 ItemsSource="{Binding Pages}" 4 SelectionChanged="TabControlItem_SelectionChanged" 5 Background="Transparent" 6 BorderThickness="0,1,0,0" 7 BorderBrush="White"> 8 <TabControl.ItemContainerStyle> 9 <Style TargetType="TabItem"> 10 <Setter Property="IsSelected" Value="{Binding IsSelected}"/> 11 <Setter Property="Background" Value="#336590C1"/> 12 <Setter Property="Margin" Value="2,0"/> 13 <Setter Property="Foreground" Value="#777"/> 14 <Setter Property="Template"> 15 <Setter.Value> 16 <!--控件模板--> 17 <ControlTemplate TargetType="TabItem"> 18 <Grid Background="{TemplateBinding Background}" Height="30"> 19 <Grid.ColumnDefinitions> 20 <ColumnDefinition/> 21 <ColumnDefinition Width="auto" MinWidth="10"/> 22 </Grid.ColumnDefinitions> 23 <ContentPresenter ContentSource="Header" VerticalAlignment="Center" Margin="10,5,5,5"/> 24 <Grid Grid.Column="1" Width="30" Margin="0,0,3,0"> 25 <Button Grid.Column="1" 26 Style="{StaticResource TabCloseButtonStyle}" 27 Foreground="{TemplateBinding Foreground}" 28 Margin="3,0" 29 Command="{Binding CloseTabCommand}" 30 CommandParameter="{Binding}"/> 31 </Grid> 32 </Grid> 33 </ControlTemplate> 34 </Setter.Value> 35 </Setter> 36 <Style.Triggers> 37 <Trigger Property="IsMouseOver" Value="True"> 38 <Setter Property="Background" Value="#EEE"/> 39 </Trigger> 40 <Trigger Property="IsSelected" Value="True"> 41 <Setter Property="Background" Value="#FF6590C1"/> 42 <Setter Property="Foreground" Value="#FFDEFBFF"/> 43 </Trigger> 44 </Style.Triggers> 45 </Style> 46 </TabControl.ItemContainerStyle> 47 <!--Header部分--> 48 <TabControl.ItemTemplate > 49 <!--数据模板--> 50 <DataTemplate> 51 <TextBlock Text="{Binding Header}"/> 52 </DataTemplate> 53 </TabControl.ItemTemplate> 54 <!--内容部分--> 55 <TabControl.ContentTemplate> 56 <!--数据模板--> 57 <DataTemplate> 58 <ContentControl Content="{Binding PageView}"/> 59 </DataTemplate> 60 </TabControl.ContentTemplate> 61 </TabControl>
二、封装ViewModel中属性双向绑定通知,命令事件,页面方法注入管理等基类
1、页面方法注入管理类ActionManager,View中的行为方法存放进来,由窗口对象调用 ,注册一个方法,这个方法中有打开弹窗的逻辑
1 /// <summary> 2 /// 页面方法注入管理类 3 /// </summary> 4 public class ActionManager 5 { 6 // 委托 7 static Dictionary<string, Delegate> actionMap = new Dictionary<string, Delegate>(); 8 9 // 希望能将View中的行为方法存放进来 注册 10 // 由窗口对象调用 ,注册一个方法,这个方法中有打开弹窗的逻辑 11 // 优化:由不同实例进行注册的时候 进行区分 12 // 这个方法可以让我们接受两种类型的委托对象 Action Func 13 public static void Register(string key, Delegate action) 14 { 15 if (!actionMap.ContainsKey(key)) 16 actionMap.Add(key, action); 17 } 18 19 // VM中需要进行调用 20 public static void Execute(string key, object data) 21 { 22 if (actionMap.ContainsKey(key)) 23 actionMap[key].DynamicInvoke(data); 24 } 25 26 public static bool ExecuteAndResult(string key, object data) 27 { 28 if (actionMap.ContainsKey(key)) 29 { 30 var action = (actionMap[key] as Func<object, bool>); 31 if (action == null) 32 return false; 33 34 return action.Invoke(data); 35 } 36 return false; 37 } 38 39 public static void Unregister(string key) 40 { 41 42 } 43 }
2.通知基础类NotifyBase,继承自接口INotifyPropertyChanged(using System.ComponentModel)
1 /// <summary> 2 /// 通知基础类 3 /// </summary> 4 public class NotifyBase : INotifyPropertyChanged 5 { 6 public event PropertyChangedEventHandler? PropertyChanged; 7 8 public void SetProperty<T>(ref T field, T value, [CallerMemberName] string propName = "") 9 { 10 if (field == null || !field.Equals(value)) 11 { 12 field = value; 13 PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); 14 } 15 } 16 }
3.命令方法基础类Command<T> ,继承自接口ICommand(using System.Windows.Input),封装委托方法的执行
1 /// <summary> 2 /// 命令方法基础类 3 /// </summary> 4 /// <typeparam name="T"></typeparam> 5 public class Command<T> : ICommand 6 { 7 public event EventHandler? CanExecuteChanged; 8 9 public bool CanExecute(object? parameter) 10 { 11 if (DoCanExecute == null) return true; 12 return DoCanExecute(parameter); 13 } 14 15 16 public void Execute(object? parameter) 17 { 18 DoExecuteNoneParam?.Invoke(); 19 20 dynamic p = parameter; 21 DoExecuteWithParam?.Invoke(p); 22 } 23 24 public void RasieCanExecuteChanged() 25 { 26 CanExecuteChanged?.Invoke(this, new EventArgs()); 27 } 28 29 30 public Action DoExecuteNoneParam { get; set; } 31 public Action<T> DoExecuteWithParam { get; set; } 32 33 34 public Func<object, bool> DoCanExecute { get; set; } 35 36 /// <summary> 37 /// 38 /// </summary> 39 /// <param name="doExecute"></param> 40 public Command(Action doExecute) 41 { 42 DoExecuteNoneParam = doExecute; 43 } 44 45 /// <summary> 46 /// 47 /// </summary> 48 /// <param name="doExecute"></param> 49 public Command(Action<T> doExecute) 50 { 51 DoExecuteWithParam = doExecute; 52 } 53 54 //public Command(Action<int?> executePageUpdatedCommand) 55 //{ 56 // // DoExecuteNoneParam = executePageUpdatedCommand; 57 //} 58 }
三、ViewModel方法的编写和绑定,实现页面中绑定的命令方法,和属性,list数据获取
1.菜单类MenuItemModel
1 /// <summary> 2 /// 菜单 3 /// </summary> 4 public class MenuItemModel 5 { 6 public int Id { get; set; } 7 public int Parent { get; set; } 8 public string? IconCode { get; set; } 9 public string? Header { get; set; } 10 public string? TargetView { get; set; } 11 12 public ICommand? OpenViewCommand { get; set; } 13 14 public List<MenuItemModel> Children { get; set; } = new List<MenuItemModel>(); 15 16 }
2.页面类PageItemModel,继承基类NotifyBase,实现属性的双向绑定,更新保存等
1 public class PageItemModel : NotifyBase 2 { 3 private bool _isSelected; 4 public bool IsSelected 5 { 6 get { return _isSelected; } 7 set { SetProperty(ref _isSelected, value); } 8 } 9 public string Header { get; set; } 10 public object PageView { get; set; } 11 12 public ICommand CloseTabCommand { get; set; } 13 14 public int Parent { get; set; } 15 }
3.MainViewModel,继承基类NotifyBase,实现属性的双向绑定,更新保存等
封装打开菜单的方法,通过反射找到菜单对应的页面,页面为dll.文件夹.页面名称的格式
1 /// <summary> 2 /// 打开选择菜单项 3 /// </summary> 4 /// <param name="menu"></param> 5 private void OpenView(MenuItemModel menu) 6 { 7 var page = Pages.ToList().FirstOrDefault(p => p.Header == menu.Header); 8 9 if (page == null) 10 { 11 Type type = Assembly.GetExecutingAssembly().GetType(menu.TargetView); 12 object p = Activator.CreateInstance(type); 13 14 Pages.Add(new PageItemModel 15 { 16 Parent = menu.Parent, 17 Header = menu.Header, 18 PageView = p, 19 IsSelected = true, 20 CloseTabCommand = new Command<PageItemModel>(ClosePage) 21 }); 22 23 this.MenuSecondHeader = menu.Header; 24 } 25 else 26 page.IsSelected = true; 27 }
页面 集合
public ObservableCollection<PageItemModel> Pages { get; set; } = new ObservableCollection<PageItemModel>();
完整代码
1 public class MainViewModel : NotifyBase 2 { 3 // 菜单 集合 4 public List<MenuItemModel> TreeList { get; set; } 5 // 页面 集合 6 public ObservableCollection<PageItemModel> Pages { get; set; } 7 = new ObservableCollection<PageItemModel>(); 8 9 //内容标题一级菜单 10 // public ObservableCollection<string> MenuFirstHeader { get; set; } 11 12 //内容标题二级菜单 13 private string _menuSecondHeader; 14 public string MenuSecondHeader 15 { 16 get { return _menuSecondHeader; } 17 set 18 { 19 _menuSecondHeader = value; 20 SetProperty<string>(ref _menuSecondHeader, value); 21 } 22 } 23 24 public MainViewModel() 25 { 26 //菜单初始化 27 InitMenu(); 28 } 29 30 /// <summary> 31 /// 菜单初始化 32 /// </summary> 33 private void InitMenu() 34 { 35 #region 菜单初始化 36 TreeList = new List<MenuItemModel>(); 37 { 38 MenuItemModel tim = new MenuItemModel(); 39 tim.Parent = 0; 40 tim.Id = 1; 41 tim.Header = "工艺设计"; 42 // XAML里使用 43 tim.IconCode = "\ue610"; // 字体图标编码,阿里的Iconfont平台打包的图标库 44 TreeList.Add(tim); 45 MenuItemModel tim1 = new MenuItemModel(); 46 tim1.Header = "系统设置"; 47 // XAML里使用 48 tim1.IconCode = "\ue626"; // 字体图标编码,阿里的Iconfont平台打包的图标库 49 tim1.Parent = 0; 50 tim1.Id = 2; 51 TreeList.Add(tim1); 52 tim1.Children.Add(new MenuItemModel 53 { 54 Parent=2, 55 Header = "用户管理", 56 TargetView = "MESWPFUI.Views.Pages.SystemSet.UserManage", 57 OpenViewCommand = new Command<MenuItemModel>(OpenView) 58 }); 59 tim.Children.Add(new MenuItemModel 60 { 61 Parent = 1, 62 Header = "加工工艺", 63 TargetView = "MESWPFUI.Views.Pages.BlankPage", 64 OpenViewCommand = new Command<MenuItemModel>(OpenView) 65 }); 66 tim.Children.Add(new MenuItemModel 67 { 68 Parent = 1, 69 Header = "EBOM", 70 TargetView = "BlankPage", 71 OpenViewCommand = new Command<MenuItemModel>(OpenView) 72 }); 73 tim.Children.Add(new MenuItemModel 74 { 75 Parent = 1, 76 IconCode = "\ue661", 77 Header = "设备看板", 78 TargetView = "MESWPFUI.Views.Pages.ProcessDesign.DevicePage", 79 OpenViewCommand = new Command<MenuItemModel>(OpenView) 80 }); 81 82 tim.Children.Add(new MenuItemModel 83 { 84 Parent = 1, 85 Header = "PBOM", 86 TargetView = "PBomPage", 87 OpenViewCommand = new Command<MenuItemModel>(OpenView) 88 }); 89 MenuItemModel subMenu = new MenuItemModel(); 90 subMenu.Parent = 1; 91 subMenu.Id = 3; 92 subMenu.Header = "二级菜单"; 93 subMenu.Children.Add( 94 new MenuItemModel 95 { 96 Parent = 3, 97 Header = "三级菜单", 98 TargetView = "MESWPFUI.Views.Pages.ProcessDesign.DevicePage", 99 OpenViewCommand = new Command<MenuItemModel>(OpenView) 100 } 101 ); 102 tim.Children.Add(subMenu); 103 } 104 #endregion 105 } 106 107 /// <summary> 108 /// 打开选择菜单项 109 /// </summary> 110 /// <param name="menu"></param> 111 private void OpenView(MenuItemModel menu) 112 { 113 var page = Pages.ToList().FirstOrDefault(p => p.Header == menu.Header); 114 115 if (page == null) 116 { 117 Type type = Assembly.GetExecutingAssembly().GetType(menu.TargetView); 118 object p = Activator.CreateInstance(type); 119 120 Pages.Add(new PageItemModel 121 { 122 Parent = menu.Parent, 123 Header = menu.Header, 124 PageView = p, 125 IsSelected = true, 126 CloseTabCommand = new Command<PageItemModel>(ClosePage) 127 }); 128 129 this.MenuSecondHeader = menu.Header; 130 } 131 else 132 page.IsSelected = true; 133 } 134 135 /// <summary> 136 /// 关闭菜单选项卡 137 /// </summary> 138 /// <param name="menu"></param> 139 private void ClosePage(PageItemModel menu) 140 { 141 Pages.Remove(menu); 142 } 143 }
浙公网安备 33010602011771号