Prism for WPF 搭建一个简单的模块化开发框架(三) 给TreeView加样式做成菜单

时间:2023-03-08 16:40:28

原文:Prism for WPF 搭建一个简单的模块化开发框架(三) 给TreeView加样式做成菜单

昨天晚上把TreeView的样式做了一下,今天给TreeView绑了数据,实现了切换页面功能

上代码把,样式代码

<Style  x:Key="MenuTreeViewItem" TargetType="{x:Type TreeViewItem}">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="MinHeight" Value="40" />
<Setter Property="Foreground" Value="White" />
<Setter Property="Background" Value="{DynamicResource Sys.TreeViewItem.BackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource Sys.TreeViewItem.BorderBrush}" />
<Setter Property="BorderThickness" Value="0,0,0,1" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Margin" Value="0" />
<Setter Property="FontSize" Value="20"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<StackPanel>
<Border x:Name="Bd" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}"
MinHeight="{TemplateBinding MinHeight}" UseLayoutRounding="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
<Grid Margin="{TemplateBinding Margin}" VerticalAlignment="Stretch" >
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="18" Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="21"/>
</Grid.ColumnDefinitions>
<!--图标Text="" -->
<TextBlock x:Name="ItemIcon" Text="{Binding itemIcon}" Foreground="{TemplateBinding Foreground}" FontSize="13" Margin="4" Height="13" VerticalAlignment="Center" Style="{DynamicResource FIcon}"/>
<!--展开收缩按钮-->
<ToggleButton x:Name="ExpanderBtn" Grid.Column="2"
IsChecked="{Binding Path=IsExpanded, RelativeSource={x:Static RelativeSource.TemplatedParent}, Mode=TwoWay}"
ClickMode="Press" >
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<Border>
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
</ControlTemplate>
</ToggleButton.Template>
<ToggleButton.Content>
<TextBlock x:Name="ExpanderIcon" Foreground="{TemplateBinding Foreground}" FontSize="13" Margin="4" Height="15" VerticalAlignment="Center" Text="" Style="{DynamicResource FIcon}"/>
</ToggleButton.Content>
</ToggleButton>
<!--内容-->
<ContentPresenter x:Name="PART_Header" Grid.Column="1" ContentSource="Header"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Grid>
</Border>
<ItemsPresenter Margin="5,0,0,0" x:Name="ItemsHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="False">
<Setter TargetName="ItemsHost" Property="Visibility" Value="Collapsed" />
</Trigger>
<Trigger Property="IsExpanded" Value="True">
<Setter TargetName="ExpanderIcon" Property="Text" Value="" />
</Trigger>
<Trigger Property="HasItems" Value="False">
<Setter TargetName="ExpanderIcon" Property="Visibility" Value="Hidden" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{DynamicResource Sys.TreeViewItem.MouseOver.BackgroundBrush}" />
<Setter Property="Foreground" Value="{DynamicResource Sys.TreeViewItem.MouseOver.Foreground}" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{DynamicResource Sys.TreeViewItem.MouseOver.BackgroundBrush}" />
<Setter Property="Foreground" Value="{DynamicResource Sys.TreeViewItem.MouseOver.Foreground}" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="True" />
<Condition Property="Selector.IsSelectionActive" Value="True" />
</MultiTrigger.Conditions>
<Setter Property="Background" Value="{DynamicResource Sys.TreeViewItem.MouseOver.BackgroundBrush}" />
<Setter Property="Foreground" Value="{DynamicResource Sys.TreeViewItem.MouseOver.Foreground}" />
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style> <!--TreeView样式-->
<Style x:Key="MenuTreeView" TargetType="{x:Type TreeView}">
<Setter Property="ScrollViewer.CanContentScroll" Value="True" />
<Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True"></Setter>
<Setter Property="VirtualizingStackPanel.VirtualizationMode" Value="Recycling" />
<Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False" />
<Setter Property="Background" Value="{DynamicResource Sys.TreeView.BackgroundBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource Sys.TreeView.BorderBrush}"/>
<Setter Property="ItemContainerStyle" Value="{StaticResource MenuTreeViewItem}"></Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsItemsHost="True" Margin="0"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>

这里样式就把之前的每个item显示【折叠按钮】、【文本】

改为了显示【图标】、【文本】、【折叠按钮】

出来的效果是这样的,减小了父子两级的缩进,因为父子两级都是相同的样式背景色,并没有把缩进完全去掉

做了个Border做视觉上的分割

Prism for WPF 搭建一个简单的模块化开发框架(三) 给TreeView加样式做成菜单Prism for WPF 搭建一个简单的模块化开发框架(三) 给TreeView加样式做成菜单

下面实现数据绑定

实现了INotifyPropertyChanged接口,一不小心就用上了传说中的MVVM模式?

Model代码

public class MenuViewModel : INotifyPropertyChanged
{
private ObservableCollection<ItemTreeData> itemTreeDataList = new ObservableCollection<ItemTreeData>();
public ObservableCollection<ItemTreeData> ItemTreeDataList
{
get { return itemTreeDataList; }
set
{
itemTreeDataList = value;
NotifyPropertyChanged("ItemTreeDataList");
}
} public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
} public class ItemTreeData // 自定义Item的树形结构
{
public int itemId { get; set; } // ID
public string itemName { get; set; } // 名称
public string itemView { get; set; }
public string itemRegion { get; set; }
public string itemIcon { get; set; } // private ObservableCollection<ItemTreeData> _children = new ObservableCollection<ItemTreeData>();
public ObservableCollection<ItemTreeData> Children
{ // 树形结构的下一级列表
get
{
return _children;
}
set
{
_children = value;
}
} public bool IsExpanded { get; set; } // 节点是否展开
public bool IsSelected { get; set; } // 节点是否选中
}

组装数据是在个模块的IModule接口下做的,先随意写点数据,itemName就是菜单的显示文本,itemView是要导航的页面,

如果又需要,可以做参数的属性,Prism的导航也是支持传参的

public void Initialize()
{
ObservableCollection<ItemTreeData> chi = new ObservableCollection<ItemTreeData>();
chi.Add(new ItemTreeData() { itemId = 1, itemName = "Map1", itemIcon = "\xe63c", itemRegion = RegionNames.Map, itemView = "View1" });
chi.Add(new ItemTreeData() { itemId = 1, itemName = "Map2", itemIcon = "\xe63c", itemRegion = RegionNames.Map, itemView = "View2" });
chi.Add(new ItemTreeData() { itemId = 1, itemName = "Map3", itemIcon = "\xe63c" });
chi.Add(new ItemTreeData() { itemId = 1, itemName = "Map4", itemIcon = "\xe63c" });
chi.Add(new ItemTreeData() { itemId = 1, itemName = "Map5", itemIcon = "\xe63c" });
MenuViewModel vm = new MenuViewModel();
vm.ItemTreeDataList.Add(new ItemTreeData() { itemId = 1, itemName = "Map", itemIcon = "\xe63c", Children = chi });
vm.ItemTreeDataList.Add(new ItemTreeData() { itemId = 1, itemName = "地图显示", itemIcon = "\xe63c" });
vm.ItemTreeDataList.Add(new ItemTreeData() { itemId = 1, itemName = "Map", itemIcon = "\xe62f" });
vm.ItemTreeDataList.Add(new ItemTreeData() { itemId = 1, itemName = "Map", itemIcon = "\xe643" });
vm.ItemTreeDataList.Add(new ItemTreeData() { itemId = 1, itemName = "Map", itemIcon = "\xe643" });
vm.ItemTreeDataList.Add(new ItemTreeData() { itemId = 1, itemName = "Map", itemIcon = "\xe643" });
vm.ItemTreeDataList.Add(new ItemTreeData() { itemId = 1, itemName = "Map", itemIcon = "\xe643" }); GlobalData.NavModules.Add(new NavModuleInfo() {
region = RegionNames.Main,
module = ModuleNames.Map,
title = ModuleTitle.Map,
icon = "\xe63c",
img = Images.CreateImageSourceFromImage(Properties.Resources.avtar),
menuVm = vm
});
this.moduleTracker.RecordModuleInitialized(ModuleNames.Map);
regionManager.RegisterViewWithRegion(RegionNames.Main, typeof(MapModule_MainView));
}

数据有了,样式有了,下面该关联起来了,其实这步很简单了

在菜单页面订阅一个导航切换的事件,每当模块发生改变时,加载新的模块的菜单数据

这里主要用到Prism的IEventAggregator实现模块之间的通信

 [ImportingConstructor]
public MenuView(IRegionManager regionManager, IEventAggregator eventAggregator, IModuleManager moduleManager)
{
this.eventAggregator = eventAggregator;
//ChangeModuleToMenuEvent cmtmEvent = this.eventAggregator.GetEvent<ChangeModuleToMenuEvent>();
////这里订阅一个改变模块的事件,模块改变时修改menu
//cmtmEvent.Subscribe(OnChangeModuleEvent); NavigateToScreenEvent ntsEvent = GlobalData.EventAggregator.GetEvent<NavigateToScreenEvent>();
ntsEvent.Subscribe(OnLinkageNavigateEvent);
HNavigateToScreenEvent hntsEvent = GlobalData.EventAggregator.GetEvent<HNavigateToScreenEvent>();
hntsEvent.Subscribe(OnLinkageHNavigateEvent);
} public void OnLinkageNavigateEvent(CommandRegionEventArgs e)
{
if (_contentLoaded)
{
LayoutRoot.DataContext = e.menuVm;
}
else {
menuVm = e.menuVm;
}
}

基础的东西都准备好了,现在就算见证奇迹的时刻了,

再加两句代码,在SelectedItemChanged的时候做页面导航

 private void treeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
ItemTreeData treeData = (ItemTreeData)treeView.SelectedItem;
if (treeData!=null && !string.IsNullOrEmpty(treeData.itemRegion) && !string.IsNullOrEmpty(treeData.itemView))
{
regionManager.RequestNavigate(treeData.itemRegion, treeData.itemView);
}
}

导航这块没遇到啥坑,也就不记录了,

哎,基础差,知识少,理解浅。难得这么顺畅

 

总感觉有些显得小气,分辨率太高 1920*1080?比例分布不均匀?

Prism for WPF 搭建一个简单的模块化开发框架(三) 给TreeView加样式做成菜单

Prism for WPF 搭建一个简单的模块化开发框架(三) 给TreeView加样式做成菜单

Prism for WPF 搭建一个简单的模块化开发框架(三) 给TreeView加样式做成菜单

现在看这大红色儿太刺眼了

到这里框架前台部分基本上已经成型了,后面应该就算调调样式,考虑一下数据怎么取,直接取还是service?还是wcf?

拿什么做服务?现在不都讲究跨平台吗?服务这块最好是拿出来做一个手机或者网页,或者客户端都能用的