WPF实现完美的树形结构和右键菜单(下)
上篇讲了TreeView的样式 这篇讲TreeView的数据绑定与邮件菜单的实现 看下图:
先来看看TreeView的数据绑定
<TreeView x:Name="TrvList" Grid.Row="1" Margin="4 8 0 0" VerticalAlignment="Stretch" ContextMenu="{DynamicResource ContractContextMenu}" ItemsSource="{Binding Path=ChatComany.ChatOrgnizationList}"> <TreeView.ItemContainerStyle> <Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource MetroTreeViewItemStyle}"> <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" /> <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=OneWay}" /> <Setter Property="FontWeight" Value="Normal" /> <Setter Property="VerticalAlignment" Value="Center"/> </Style> </TreeView.ItemContainerStyle> <i:Interaction.Triggers> <i:EventTrigger EventName="ContextMenuOpening"> <i:InvokeCommandAction Command="{Binding ContextMenuOpeningCommand}" CommandParameter="{Binding ElementName=TrvList, Path=SelectedItem}"/> </i:EventTrigger> <i:EventTrigger EventName="MouseDoubleClick"> <i:InvokeCommandAction Command="{Binding TreeViewDoubleClickCommand}" CommandParameter="{Binding ElementName=TrvList, Path=SelectedItem}"/> </i:EventTrigger> </i:Interaction.Triggers> <TreeView.Resources> <ContextMenu x:Key="ContractContextMenu" ItemsSource="{Binding Menu}" MenuItem.Click="ContextMenu_Click"> <ContextMenu.ItemTemplate> <HierarchicalDataTemplate DataType="{x:Type models:ChatMenu}" ItemsSource="{Binding Path=Children}"> <TextBlock Text="{Binding Header}"/> </HierarchicalDataTemplate> </ContextMenu.ItemTemplate> </ContextMenu> <HierarchicalDataTemplate DataType="{x:Type model:ChatOrgnization}" ItemsSource="{Binding Path=Children}"> <StackPanel Orientation="Horizontal" AllowDrop="True" HorizontalAlignment="Left" Height="25"> <TextBlock Text="{Binding OrgnizationName}" FontSize="13" Width="130" TextTrimming="CharacterEllipsis" VerticalAlignment="Center"/> <TextBlock FontSize="13" Visibility="{Binding VisibilityCount, Converter={StaticResource BooleanToVisibilityConverter}}" VerticalAlignment="Center"> <TextBlock.Text> <MultiBinding Converter="{StaticResource CountConverter}"> <Binding Path="ActivedCount"/> <Binding Path="Count"/> </MultiBinding> </TextBlock.Text> </TextBlock> </StackPanel> </HierarchicalDataTemplate> <HierarchicalDataTemplate DataType="{x:Type model:ChatContractInfo}" ItemsSource="{Binding Path=Children}"> <Grid Height="40"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <Rectangle Width="30" Height="30" x:Name="BordHend" RadiusX="5" RadiusY="5"> <Rectangle.Style> <Style TargetType="Rectangle"> <Setter Property="Fill" Value="{DynamicResource SmallMalAct}"/> <Style.Triggers> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding Sex}" Value="1"/> <Condition Binding="{Binding IsActive}" Value="true"/> </MultiDataTrigger.Conditions> <Setter Property="Fill" Value="{DynamicResource SmallMalAct}"/> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding Sex}" Value="1"/> <Condition Binding="{Binding IsActive}" Value="False"/> </MultiDataTrigger.Conditions> <Setter Property="Fill" Value="{DynamicResource SmallMalNotAct}"/> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding Sex }" Value="0"/> <Condition Binding="{Binding IsActive}" Value="true"/> </MultiDataTrigger.Conditions> <Setter Property="Fill" Value="{DynamicResource SmallFemAct}"/> </MultiDataTrigger> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding Sex }" Value="0"/> <Condition Binding="{Binding IsActive}" Value="false"/> </MultiDataTrigger.Conditions> <Setter Property="Fill" Value="{DynamicResource SmallFemNotAct}"/> </MultiDataTrigger> <DataTrigger Binding="{Binding HeadPath, Converter={StaticResource CheckFileExConverter}}" Value="false"> <Setter Property="Fill"> <Setter.Value> <ImageBrush ImageSource="{Binding HeadPath, Converter={StaticResource AddSysPathConver}}"/> </Setter.Value> </Setter> </DataTrigger> </Style.Triggers> </Style> </Rectangle.Style> </Rectangle> <TextBlock Grid.Column="1" Margin="20 0 0 0" Text="{Binding ContractInfoName}" FontSize="13" Foreground="#333333" VerticalAlignment="Center" HorizontalAlignment="Left" Width="90" TextTrimming="CharacterEllipsis"/> <Border Margin="0 0 20 0" Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Center" Background="#A0D468" CornerRadius="10" Width="30" Height="18" Visibility="Collapsed"> <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="#ffffff" Text="{Binding MessageCount,Converter={StaticResource LimitCountConverter}}"/> </Border> </Grid> </HierarchicalDataTemplate> </TreeView.Resources> </TreeView>
上面有几个点非常关键
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource MetroTreeViewItemStyle}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=OneWay}" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</TreeView.ItemContainerStyle>
标示红的是TreeView的两个非常重要的属性,一个是节点是否展开,一个是选项是否选择。
部门和人员用到了的两个类型模板。程序会根据不同类型选择不同的数据模板。
<HierarchicalDataTemplate DataType="{x:Type model:ChatContractInfo}" ItemsSource="{Binding Path=Children}">
<HierarchicalDataTemplate DataType="{x:Type model:ChatOrgnization}" ItemsSource="{Binding Path=Children}">
在来看看右键菜单:
<TreeView.Resources> <ContextMenu x:Key="ContractContextMenu" ItemsSource="{Binding Menu}" MenuItem.Click="ContextMenu_Click"> <ContextMenu.ItemTemplate> <HierarchicalDataTemplate DataType="{x:Type models:ChatMenu}" ItemsSource="{Binding Path=Children}"> <TextBlock Text="{Binding Header}"/> </HierarchicalDataTemplate> </ContextMenu.ItemTemplate> </ContextMenu>
......
这里当初为什么考虑事件,而没有用命令,是因为右键菜单的是一个Popup,在数据处理的时候不是很好传值。
上面基本上实现的TreeView的数据绑定和右键菜单。在来简单的看一个后来的数据结构
using Microsoft.Practices.Prism.ViewModel; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; namespace Sinosun.TChat.Model { public class ChatTreeViewItem : NotificationObject { bool isExpanded; bool isSelected; private ObservableCollection<object> children; private ChatOrgnization parent; public ChatTreeViewItem(ChatOrgnization parent) { this.parent = parent; } public ChatTreeViewItem() { Children = new ObservableCollection<object>(); } public ObservableCollection<object> Children { get { return children; } set { children = value; } } public ChatOrgnization Parent { get { return parent; } set { parent = value; } } public bool IsExpanded { get { return isExpanded; } set { if (value != isExpanded) { isExpanded = value; RaisePropertyChanged<bool>(() => this.IsExpanded); } } } public bool IsSelected { get { return isSelected; } set { if (value != isSelected) { isSelected = value; RaisePropertyChanged<bool>(() => this.IsSelected); } } } } }
上面定义了一个TreeView的基类然后部门和人员类都继承这个类。这样一个树形结构基本上就实现了。