WPF中目录结构的创建与数据绑定(TreeView、ListBox、MVVM)多种方法实现
在进行数据展示页面设计时,通常需要多级目录进行导航选择,通常会用到TreeView和ListBox。具体使用时需要根据目录结构来确定采用静态、动态和数据绑定的方式。
一、最简单的静态方式:适用于,目录结构简单、数量确定
前台代码
<Window x:Class="WpfTutorialSamples.TreeView_control.TreeViewSample" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="TreeViewSample" Height="200" Width="250"> <Grid Margin="10"> <TreeView> <TreeViewItem Header="Level 1" IsExpanded="True"> <TreeViewItem Header="Level 2.1" /> <TreeViewItem Header="Level 2.2" IsExpanded="True"> <TreeViewItem Header="Level 3.1" /> <TreeViewItem Header="Level 3.2" /> </TreeViewItem> <TreeViewItem Header="Level 2.3" /> </TreeViewItem> </TreeView> </Grid> </Window>
效果
直接在 XAML 中声明 TreeViewItem 对象,在我们想要显示它们的相同结构中,其中第一个标记是 TreeView 控件的子项,其子对象也是其父对象的子标记。为了指定我们想要为每个节点显示的文本,我们使用Header属性。默认情况下,TreeViewItem 不会展开,但为了向您展示示例的结构,我使用了 IsExpanded属性来展开两个父项。
后台使用时通过控件的Name属性就可获取节点的数据。
二、动态添加节点、绑定数据方式
这种方式非常灵活,可自由控制节点数量,但实现起来相对复杂。
下面以动态生成年、月、日中的表单目录为例说明
1、首先要在后台定义节点的数据类(推荐在调用页面的后台代码中)
public class Node { //构造函数 public Node() { this.Nodes = new List<Node>(); } public int ID { get; set; }//内码 public string Name { get; set; }//名称 public bool IsParent { get; set; }//是否父级 public int OwnerID { get; set; }//上一级内码 public string path { get; set; }//文件路径 public string month { get; set; }//内码 public string day { get; set; }//内码 public List<Node> Nodes { get; set; }//节点集合 }
类中的属性要结合目录动态控制的需要和表单结构定义,动态控制节点级数,List<Node> Nodes 节点集合是必须的
2、后台定义方法实现目录结构生成、节点定义、数据绑定
创建目录结构
List<Node> nodesList = null; /// <summary> /// 数据初始化 /// </summary> private void Init() { try { List<string> namey = new List<string>(); List<string> namem = new List<string>(); //获取年目录 if (Directory.Exists(@"D:\HMI_Data\ArchiveData") == true)//检查年份文件夹 { string[] Y = Directory.GetDirectories(@"D:\HMI_Data\ArchiveData");//获取文件夹名称带路径 for (int i = 0; i < Y.Length; i++) { nodesList = new List<Node>(); Node a = new Node(); string[] YY = Y[i].Split('\\');//分割路径 a.Name = YY[YY.Length - 1];//获取文件夹名 a.ID = (i+1) * 1000; a.IsParent = true; a.OwnerID = 0; //获取月文件夹名 string[] M = Directory.GetFileSystemEntries(Y[i]);//获取文件名称带路径 //生成月目录 for (int ii = 0; ii < M.Length; ii++) { Node aa = new Node(); string[] MM = M[ii].Split('\\');//分割路径 string MMM = MM[MM.Length - 1];//获取文件夹名 //aa.Name = MMM.TrimEnd('.', 'd', 'b'); aa.Name = MMM; aa.ID = (ii+1) * 100+ a.ID; aa.IsParent = true; aa.OwnerID = a.ID; //获取文件名 string[] D = Directory.GetFileSystemEntries(M[ii]);//获取文件名称带路径 if (D != null) { for (int iii = 0; iii < D.Length; iii++) { Node aaa = new Node(); string[] DD = D[iii].Split('\\');//分割路径 string DDD = DD[DD.Length - 1];//获取文件夹名 if (DDD.StartsWith("Data")) { aaa.Name = DDD.TrimEnd('.', 't', 'x', 't'); aaa.ID = (iii + 1) * 10 + aa.ID; aaa.IsParent = false; aaa.OwnerID = aa.ID; aaa.path = D[iii]; aa.Nodes.Add(aaa); } } } a.Nodes.Add(aa); } nodesList.Add(a); } } } catch { MessageBox.Show("数据表单目录获取失败!", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } }
绑定并创建目录
/// <summary> /// 绑定树 /// </summary> List<Node> Bind(List<Node> nodes) { List<Node> outputList = new List<Node>();//定义节点集合outputList for (int i = 0; i<nodes.Count; i++)//按节点数量循环 { if (nodes[i].OwnerID == 0)//判断是否根节点 { outputList.Add(nodes[i]);//是根节点的,节点集合中加上节点 } else { FindDownward(nodes, nodes[i].OwnerID).Nodes.Add(nodes[i]);//不是根节点的向下查找 } } return outputList; } /// <summary> /// 向下查找 /// </summary> Node FindDownward(List<Node> nodes, int id) { if (nodes == null) { return null;//节点集合为空返回null } for (int i = 0; i<nodes.Count; i++)//按nodes节点集合数量循环 { if (nodes[i].ID == id)//判断节点的id是否与父节点相同 { return nodes[i];//是父节点返回节点 } Node node = FindDownward(nodes[i].Nodes, id);//向下查找 if (node != null)//节点不等于返回节点 { return node; } } return null;//返回空值 } 在结构函数中调用 public main() { InitializeComponent(); Init(); List<Node> outputList = Bind(nodesList);// 绑定树 this.TreeView.ItemsSource = outputList;//绑定TreeView.Items数据源 } TreeView为前台控件名 3、前台控件调用代码 <TreeView Name="TreeView" Background="{x:Null}" Foreground="#FF60EC37" FontSize="18"> <TreeView.Resources> <HierarchicalDataTemplate DataType="{x:Type mode:Node}" ItemsSource="{Binding Nodes}"> <StackPanel Orientation="Horizontal" Margin="0,2,0,2"> <Button Content="{Binding Name}" ToolTip="{Binding Name}" Tag="{Binding Path=path}" Background="{x:Null}" Click="Button_Click" FontSize="18" Foreground="#FF60EC37" BorderThickness="0" Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"> </Button> </StackPanel> </HierarchicalDataTemplate> </TreeView.Resources> </TreeView>
4、获取节点数据,控制打开选中的表单
前台调用时,在节点标签中定义了一个Button控件将其Tag属性绑定到了后台获取的文件路径上,同时在点击事件中控制打开指定文件
private void Button_Click(object sender, RoutedEventArgs e) { Button pU = sender as Button; if (pU.Tag.ToString() != "") { string f = pU.Tag.ToString(); } //表单打开控制代码 ...................... }
三、动态添加(MVVM)方式
数据绑定:
更新内容:补充在MVVM模式上的TreeView控件数据绑定的代码。
xaml代码:
<TreeView Name="syntaxTree" ItemsSource="{Binding TreeNodes}"> <TreeView.ItemTemplate> <HierarchicalDataTemplate DataType="{x:Type local:TreeNode}" ItemsSource="{Binding Path=ChildNodes}"> <TextBlock Text="{Binding NodeName}"/> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView>
TreeView中的ItemsSource绑定的是一个名为TreeNodes的TreeNode的列表,即List<TreeNode>TreeNodes。HierarchicalDataTemplate中的ItemsSource绑定的TreeNodes中的每个节点的ChildNodes属性。
ViewModel.cs中的代码(有删减):
public class ViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private List<TreeNode> treenodes = new List<TreeNode>(); public List<TreeNode> TreeNodes { get { return treenodes; } set { treenodes = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("TreeNodes")); } } public ViewModel() { // Nodes是我已经获得的一组节点 TreeNodes = getChildNodes(0,Nodes); } private List<TreeNode> getChildNodes(int parentID, List<TreeNode> nodes) { List<TreeNode> mainNodes = nodes.Where(x => x.ParentID == parentID).ToList(); List<TreeNode> otherNodes = nodes.Where(x => x.ParentID != parentID).ToList(); foreach (TreeNode node in mainNodes) node.ChildNodes = getChildNodes(node.NodeID, otherNodes); return mainNodes; } }
使用MVVM模式,那么xaml.cs文件就会变得非常简单了,基本只有一句代码了:
this.DataContext = new ViewModel();
该模式的好处就是使得UI设计和后端代码分开,只通过数据绑定进行连接。尝试用了几次,真的还蛮方便。
TreeView数据绑定需要使用层次结构数据模板(HierarchicalDataTemplate)来显示分层数据。XAML代码如下:
<TreeView Name="chapterTree" Grid.Column="0"> <TreeView.ItemTemplate> <HierarchicalDataTemplate ItemsSource="{Binding Path=ChildNodes}"> <StackPanel> <Label Content="{Binding Path=NodeName}"/> </StackPanel> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView>
其中,ItemsSource绑定的对象ChildNodes应该是一个集合类型:List<TreeNode>,Label中绑定的是TreeNode的NodeName属性,TreeNode类定义如下所示:
public class TreeNode { public int NodeID { get; set; } public int ParentID { get; set; } public string NodeName { get; set; } public List<TreeNode> ChildNodes { get; set; } public TreeNode() { ChildNodes = new List<TreeNode>(); } }
因为是树形结构,因此TreeNode需要有NodeID属性和ParentID属性,即某个树节点node本身的ID和它所属的父节点的ID。以目录为例,则xaml.cs中的代码如下。首先是写入了数据,在我实际项目中,这些数据是从DB中查询的,这里为了简化,直接Input数据了。
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); InputData(); chapterTree.ItemsSource = getNodes(0, nodes); } private List<TreeNode> nodes; private void InputData() { nodes = new List<TreeNode>() { new TreeNode(){ParentID=0, NodeID=1, NodeName = "Chapter1" }, new TreeNode(){ParentID=0, NodeID=2, NodeName="Chapter2"}, new TreeNode(){ParentID=0,NodeID=3, NodeName="Chapter3"}, new TreeNode(){ParentID=1, NodeID=4, NodeName="Section1.1"}, new TreeNode(){ParentID=1, NodeID=5, NodeName="Section1.2"}, new TreeNode(){ParentID=2, NodeID=6, NodeName="Section2.1"}, new TreeNode(){ParentID=3, NodeID=7, NodeName="Section3.1"}, new TreeNode(){ParentID=6, NodeID=8, NodeName="SubSection2.1.1"}, new TreeNode(){ParentID=6, NodeID=9, NodeName="SubSection2.1.2"}, new TreeNode(){ParentID=2, NodeID=10,NodeName="Section2.2"}, new TreeNode(){ParentID=3, NodeID=11, NodeName="Section3.2"} }; } private List<TreeNode> getNodes(int parentID, List<TreeNode> nodes) { List<TreeNode> mainNodes = nodes.Where(x => x.ParentID == parentID).ToList(); List<TreeNode> otherNodes = nodes.Where(x => x.ParentID != parentID).ToList(); foreach (TreeNode node in mainNodes) node.ChildNodes = getNodes(node.NodeID, otherNodes); return mainNodes; } }
需要注意的就是NodeID是不断增加的,每个节点都有自己的ID,而其ParentID就看它是属于哪个父节点的了。getNodes()是一个递归方法,就是不断读取某个节点的子节点。运行结果如图所示:
后台动态添加TreeView:
一开始,没用数据绑定,就直接在xaml.cs中使用treeview,虽然后来用了数据绑定之后发现还是绑定更方便,但是这种在后台构建treeview的方法没准哪天也能用到,就记录一下吧。
XAML文件,使用一个TreeView控件
<Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="60*"/> <ColumnDefinition Width="100*"/> </Grid.ColumnDefinitions> <TreeView Name="chapterTree" Grid.Column="0"/> </Grid>
XAML.CS文件,同样使用递归方法,就是不断的新建treeviewitem控件。
public partial class DynamicTreeView : Window { public DynamicTreeView() { InitializeComponent(); InputData(); ShowTreeView(); } private List<TreeNode> nodes; private void InputData() { nodes = new List<TreeNode>() { new TreeNode(){ParentID=0, NodeID=1, NodeName = "Chapter1" }, new TreeNode(){ParentID=0, NodeID=2, NodeName="Chapter2"}, new TreeNode(){ParentID=0,NodeID=3, NodeName="Chapter3"}, new TreeNode(){ParentID=1, NodeID=4, NodeName="Section1.1"}, new TreeNode(){ParentID=1, NodeID=5, NodeName="Section1.2"}, new TreeNode(){ParentID=2, NodeID=6, NodeName="Section2.1"}, new TreeNode(){ParentID=3, NodeID=7, NodeName="Section3.1"}, new TreeNode(){ParentID=6, NodeID=8, NodeName="SubSection2.1.1"}, new TreeNode(){ParentID=6, NodeID=9, NodeName="SubSection2.1.2"}, new TreeNode(){ParentID=2, NodeID=10,NodeName="Section2.2"}, new TreeNode(){ParentID=3, NodeID=11, NodeName="Section3.2"} }; } private void ShowTreeView() { TreeViewItem tv1 = new TreeViewItem(); chapterTree.Items.Add(tv1); GetNodes(0, tv1); } private void GetNodes(int parentID, TreeViewItem tv) { List<TreeNode> mainNodes = nodes.Where(x => x.ParentID == parentID).ToList(); foreach(var item in mainNodes) { TreeViewItem tv1 = new TreeViewItem(); tv1.Header = item.NodeName; tv.Items.Add(tv1); GetNodes(item.NodeID, tv1); } } }
运行图:总归是没有databinding方便。刚开始学习WPF,一开始没想过要用数据绑定,总感觉只要能实现自己想要的东西就可以了,不用管实现过程,后来使用了之后发现,使用数据绑定构建MVVM架构的应用还是挺好的。
前面介绍了TreeView控件MVVM模式下数据绑定的方法。在这篇文章中,将总结给节点添加事件的方法,这样说有些不对,总之实现的效果就是点击某个节点,将出现对应于该节点的页面或者数据。
Example#1: 实现下图功能,点击左侧treeview姓名节点,在右侧会出现响应的detailed information. 可以将ID的textbox中的text属性绑定到treeview中SelectedItem
先构造两个类,一个是User,一个是TreeNode。User是TreeNode的一个属性。
绑定:
<TreeView Grid.Column="0" FontSize="15" ItemsSource="{Binding Path=Nodes}" x:Name="treeview"> <TreeView.ItemTemplate> <HierarchicalDataTemplate DataType="{x:Type local:TreeNode}" ItemsSource="{Binding Path=ChildNodes}"> <Label Content="{Binding Path=NodeName}"/> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView> <TextBox Text="{Binding ElementName=treeview, Path=SelectedItem.user.Key}" Canvas.Left="70" Width="200" Canvas.Top="8" FontSize="15"/> <TextBox Text="{Binding ElementName=treeview, Path=SelectedItem.user.Age}" Canvas.Left="70" Width="200" Canvas.Top="8" FontSize="15"/>
上面两句TextBox控件,就是将项目中名为“treeview"的控件的SelectedItem.user.Key和SeletedItem.user.Age的值绑定到Text属性中。这样点”Lily"节点,右侧就会出现相应的信息。
Example#2:
上面的例子比较简单,第二个例子将button控件作为treeviewitem,并给button控件绑定一个Command。
场景描述:左侧是treeview,其中每个treeviewitem的元素都是button控件,点击每个节点,中间的listview中会出现符合条件的学生的姓名,比如,是Grade1的学生有Lucy, Tom和Lily三人。是Grade2Class1的学生有Sam和Jack两人。点击listview中的学生姓名,右侧会显示学生的ID和Age信息。
TreeView部分的XAML代码:
<TreeView Grid.Column="0" FontSize="15" ItemsSource="{Binding Path=Nodes}"> <TreeView.ItemTemplate> <HierarchicalDataTemplate DataType="{x:Type local:TreeNode}" ItemsSource="{Binding Path=ChildNodes}"> <Button Content="{Binding NodeName}" Command="{Binding DataContext.TreeViewCommand, RelativeSource={RelativeSource AncestorType=local:MainWindow}}" CommandParameter="{Binding Path=NodeID}" Background="White" BorderThickness="0"/> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView>
可以看见,TreeViewItem元素从Label变成了Button。其中Button控件绑定了一个TreeViewCommand。这里需要指明绑定的是DataContext下的TreeViewCommand,否则默认的是TreeNode类型中的TreeViewCommand属性。因此下面这句是非常关键的。
Command="{Binding DataContext.TreeViewCommand, RelativeSource={RelativeSource AncestorType=local:MainWindow}}" CommandParameter="{Binding Path=NodeID}"
ListView部分的XAML代码:
<ListView Name="listview" Grid.Column="1" ItemsSource="{Binding Users}" IsSynchronizedWithCurrentItem="True" BorderBrush="DarkGray" BorderThickness="5"> <ListView.View> <GridView> <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Path=Name}" Width="auto"/> </GridView> </ListView.View> </ListView>
ViewModel代码:
其中有两个类,DelegateCommand和DelegateCommandEventArgs,是继承自ICommand,然后委托方法的。
四、Listbox(MVVM)方式:可实现动态的节点生成、通过双向数据绑定可动态显示表单内容
效果图
在页面幅面允许的情况下个人认为是不错的选择,相比TreeView更加灵活一些
1、首先当然是定义model类
class DataTableModel : ViewModelBase { /// <summary> /// ID /// </summary> public string ID { get { return _ID; } set { _ID = value; } } private string _ID; /// <summary> /// DB区变量值 /// </summary> public string dbValue { get { return _dbValue; } set { _dbValue = value; } } private string _dbValue; /// <summary> /// M区变量值 /// </summary> public string mValue { get { return _mValue; } set { _mValue = value; } } private string _mValue; /// <summary> /// I区变量值 /// </summary> public string iValue { get { return _iValue; } set { _iValue = value; } } private string _iValue; /// <summary> /// Q区变量值 /// </summary> public string qValue { get { return _qValue; } set { _qValue = value; } } private string _qValue; /// <summary> /// 记录时间 /// </summary> public string Times { get { return _Times; } set { _Times = value; } } private string _Times; /// <summary> /// 年 /// </summary> public string year { get { return _year; } set { _year = value; } } private string _year; /// <summary> /// 月 /// </summary> public string month { get { return _month; } set { _month = value; } } private string _month; /// <summary> /// 日 /// </summary> public string day { get { return _day; } set { _day = value; } } private string _day; /// <summary> /// 数据表 /// </summary> public DataTable Data { get { return _data; } set { _data = value; } } private DataTable _data; /// <summary> /// 子节点数据集 /// </summary> public ObservableCollection<DataTableModel> DataTablePropertyList { get { return _DataTablePropertyList; } set { _DataTablePropertyList = value; //RaisePropertyChanged(); } } private ObservableCollection<DataTableModel> _DataTablePropertyList = new ObservableCollection<DataTableModel>(); }
2、创建view
<UserControl x:Class="PLC_Monitor_Interface.MVVM.DataTableView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:PLC_Monitor_Interface.MVVM" mc:Ignorable="d" d:DesignHeight="685" d:DesignWidth="1280" Loaded="UserControl_Loaded" Unloaded="UserControl_Unloaded"> <UserControl.Resources> <Style TargetType="ListBox"/> <Style TargetType="ScrollBar"/> <!--年目录显示样式--> <Style x:Key="year" TargetType="ListBoxItem"> <Setter Property="Background" Value="#FF585D5E"/> <Setter Property="Padding" Value="0"/> <Setter Property="Margin" Value="6,5,6,5"/> <Setter Property="Height" Value="50"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListBoxItem}"> <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="True"> <ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> </Border> <ControlTemplate.Triggers> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="Selector.IsSelectionActive" Value="False"/> <Condition Property="IsSelected" Value="True"/> </MultiTrigger.Conditions> <Setter Property="Background" TargetName="Bd" Value="#FF3BAABC"/> <Setter Property="BorderBrush" TargetName="Bd" Value="#FF12273A"/> </MultiTrigger> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="Selector.IsSelectionActive" Value="True"/> <Condition Property="IsSelected" Value="True"/> </MultiTrigger.Conditions> <Setter Property="Background" TargetName="Bd" Value="#FF3BAABC"/> <Setter Property="BorderBrush" TargetName="Bd" Value="#FF26A0DA"/> </MultiTrigger> <Trigger Property="IsEnabled" Value="False"> <Setter Property="TextElement.Foreground" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <!--月目录显示样式--> <Style x:Key="month" TargetType="ListBoxItem"> <Setter Property="Background" Value="#FF585D5E"/> <Setter Property="Padding" Value="0"/> <Setter Property="Margin" Value="6,5,6,5"/> <Setter Property="Height" Value="50"/> <!--<EventSetter Event="Selected" Handler="RootItem_Selected"/>--> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListBoxItem}"> <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="True"> <ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> </Border> <ControlTemplate.Triggers> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="Selector.IsSelectionActive" Value="False"/> <Condition Property="IsSelected" Value="True"/> </MultiTrigger.Conditions> <Setter Property="Background" TargetName="Bd" Value="#FF3BAABC"/> <Setter Property="BorderBrush" TargetName="Bd" Value="#FF12273A"/> </MultiTrigger> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="Selector.IsSelectionActive" Value="True"/> <Condition Property="IsSelected" Value="True"/> </MultiTrigger.Conditions> <Setter Property="Background" TargetName="Bd" Value="#FF3BAABC"/> <Setter Property="BorderBrush" TargetName="Bd" Value="#FF26A0DA"/> </MultiTrigger> <Trigger Property="IsEnabled" Value="False"> <Setter Property="TextElement.Foreground" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <!--日目录显示样式--> <Style x:Key="day" TargetType="ListBoxItem"> <Setter Property="Background" Value="#FF585D5E"/> <Setter Property="Padding" Value="0"/> <Setter Property="Margin" Value="6,5,0,5"/> <Setter Property="Height" Value="50"/> <!--<EventSetter Event="Selected" Handler="RootItem_Selected"/>--> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListBoxItem}"> <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="True"> <ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> </Border> <ControlTemplate.Triggers> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="Selector.IsSelectionActive" Value="False"/> <Condition Property="IsSelected" Value="True"/> </MultiTrigger.Conditions> <Setter Property="Background" TargetName="Bd" Value="#FF3BAABC"/> <Setter Property="BorderBrush" TargetName="Bd" Value="#FF0D2738"/> </MultiTrigger> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="Selector.IsSelectionActive" Value="True"/> <Condition Property="IsSelected" Value="True"/> </MultiTrigger.Conditions> <Setter Property="Background" TargetName="Bd" Value="#FF3BAABC"/> <Setter Property="BorderBrush" TargetName="Bd" Value="#FF26A0DA"/> </MultiTrigger> <Trigger Property="IsEnabled" Value="False"> <Setter Property="TextElement.Foreground" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <!--三级目录显示样式--> <Style x:Key="Catalogue2" TargetType="ListBoxItem"> <Setter Property="BorderBrush" Value="#FF3BAABC"/> <Setter Property="BorderThickness" Value="0.5,0.25"/> <Setter Property="Padding" Value="0"/> <Setter Property="Margin" Value="0"/> <!--<EventSetter Event="Selected" Handler="RootItem_Selected"/>--> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListBoxItem}"> <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="True" Height="Auto"> <ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> <!--模拟写控件显示样式--> <!--<Style x:Key="InputBox" TargetType="cs:A_Write_1" BasedOn="{StaticResource {x:Type cs:A_Write_1}}"> <Setter Property="Width" Value="100"/> <Setter Property="Height" Value="38"/> <Setter Property="Background" Value="{x:Null}"/> <Setter Property="BorderThickness" Value="0.8"/> <Setter Property="BorderBrush" Value="#FFF76D13"/> <Setter Property="WriteCornerRadius" Value="5"/> <Setter Property="FontSize" Value="16"/> <Setter Property="Foreground" Value="White"/> <Setter Property="HorizontalAlignment" Value="Center"/> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="HorizontalContentAlignment" Value="Center"/> <Setter Property="VerticalContentAlignment" Value="Center"/> </Style>--> <!--一级目录数据显示样式--> <Style x:Key="MyItemsControlStyle" TargetType="ItemsControl" > <Setter Property="Width" Value="100"/> <!--<Setter Property="Height" Value="38"/>--> <Setter Property="Background" Value="{x:Null}"/> <!--<Setter Property="BorderThickness" Value="0.8"/>--> <Setter Property="BorderBrush" Value="#FFF76D13"/> <Setter Property="FontSize" Value="16"/> <Setter Property="Foreground" Value="White"/> <Setter Property="HorizontalAlignment" Value="Center"/> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="HorizontalContentAlignment" Value="Left"/> <Setter Property="VerticalContentAlignment" Value="Center"/> </Style> <Style x:Key="RadioButtonStyle" TargetType="{x:Type RadioButton}"> <Setter Property="FocusVisualStyle"> <Setter.Value> <Style> <Setter Property="Control.Template"> <Setter.Value> <ControlTemplate> <Rectangle SnapsToDevicePixels="True" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/> </ControlTemplate> </Setter.Value> </Setter> </Style> </Setter.Value> </Setter> <!--<Setter Property="Margin" Value="0 0 0 0"/> <Setter Property="FontSize" Value="26"/>--> <Setter Property="VerticalContentAlignment" Value="Center"/> <Setter Property="HorizontalContentAlignment" Value="Left"/> <Setter Property="BorderBrush" Value="Transparent"/> <Setter Property="Foreground" Value="White"/> <Setter Property="BorderThickness" Value="0"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type RadioButton}"> <Grid x:Name="templateRoot" Background="#FF585D5E" SnapsToDevicePixels="True"> <Border x:Name="border" BorderBrush="Red" BorderThickness="0" SnapsToDevicePixels="True"/> <Border x:Name="bd2" /> <ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Grid.Column="0" ContentStringFormat="{TemplateBinding ContentStringFormat}" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> </Grid> <ControlTemplate.Triggers> <Trigger Property="HasContent" Value="True"> <Setter Property="FocusVisualStyle"> <Setter.Value> <Style> <Setter Property="Control.Template"> <Setter.Value> <ControlTemplate> <Rectangle Margin="14,0,0,0" SnapsToDevicePixels="True" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/> </ControlTemplate> </Setter.Value> </Setter> </Style> </Setter.Value> </Setter> <Setter Property="Padding" Value="0,0,0,0"/> </Trigger> <Trigger Property="IsChecked" Value="true"> <Setter Property="Foreground" Value="White"/> <Setter Property="Background" Value="#FF3BAABC" TargetName="border"/> </Trigger> <Trigger Property="IsChecked" Value="{x:Null}"/> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> <Setter Property="MinHeight" Value="44"/> <Setter Property="HorizontalAlignment" Value="Left"/> </Style> </UserControl.Resources> <Grid x:Name="cvs1" Background="#FF12273A"> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition Width="Auto"/> <!--<ColumnDefinition Width="160"/>--> </Grid.ColumnDefinitions> <Border x:Name="border1" Margin="3,3,3,0" CornerRadius="10" BorderBrush="#FF3BAABC" BorderThickness="1,0,1,1" Background="#FF0D2738"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Border Background="#FF3BAABC" CornerRadius="3" Margin="-1,0" Height="42" > <Grid Grid.Row="1" Margin="5,0,0,0"> <Grid.ColumnDefinitions> <ColumnDefinition Width="80"/> <ColumnDefinition Width="100"/> <ColumnDefinition Width="80"/> <ColumnDefinition Width="80"/> <ColumnDefinition Width="120"/> <ColumnDefinition Width="120"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <TextBlock Grid.Row="0" x:Name="csh" Text="序号" Height="{Binding Height, ElementName=csh}" FontFamily="Arial" FontSize="16" Foreground="#FFFDFDFA" TextWrapping="Wrap" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,5,0,0" TextAlignment="Center" Tag="tag4390"/> <TextBlock Grid.Row="0" x:Name="dqz" Text="DB区变量值" Height="{Binding Height, ElementName=csh}" Width="{Binding Width, ElementName=csh}" Margin="{Binding Margin, ElementName=csh}" FontFamily="Arial" FontSize="16" Foreground="#FFFDFDFA" TextWrapping="Wrap" HorizontalAlignment="Center" VerticalAlignment="Center" TextAlignment="Center" Grid.Column="1" Tag="tag4391"/> <TextBlock Grid.Row="0" x:Name="mrz" Text="M区变量值" Height="{Binding Height, ElementName=csh}" FontFamily="Arial" FontSize="16" Foreground="#FFFDFDFA" TextWrapping="Wrap" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,5,0,0" TextAlignment="Center" Grid.Column="2" Tag="tag4392"/> <TextBlock Grid.Row="0" x:Name="ccz" Text="I区变量值" Height="{Binding Height, ElementName=csh}" FontFamily="Arial" FontSize="16" Foreground="#FFFDFDFA" TextWrapping="Wrap" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,5,0,0" TextAlignment="Center" Grid.Column="3" Tag="tag4393"/> <TextBlock Grid.Row="0" x:Name="qzfw" Text="Q区变量值" Height="{Binding Height, ElementName=csh}" Width="100" Margin="{Binding Margin, ElementName=csh}" FontFamily="Arial" FontSize="16" Foreground="#FFFDFDFC" TextWrapping="Wrap" HorizontalAlignment="Center" VerticalAlignment="Center" TextAlignment="Center" Grid.Column="4" Tag="tag4395"/> <TextBlock Grid.Row="0" x:Name="dw" Text="记录时间" Height="{Binding Height, ElementName=csh}" Width="120" Margin="{Binding Margin, ElementName=csh}" FontFamily="Arial" FontSize="16" Foreground="#FFFAFAF9" TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Center" TextAlignment="Center" Grid.Column="5" Tag="tag4394"/> <TextBlock Grid.Row="0" x:Name="ms" Text="参数功能描述" Height="{Binding Height, ElementName=csh}" FontFamily="Arial" FontSize="16" Foreground="#FFFDFDF9" TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Center" Grid.Column="6" Margin="10,5,0,0" Tag="tag4396"/> </Grid> </Border> <ListBox x:Name="listbox" Grid.Row="2" Background="{x:Null}" BorderBrush="{x:Null}" ItemsSource="{Binding DataTable3Property.DataTablePropertyList}" ItemContainerStyle="{StaticResource Catalogue2}" > <ListBox.ItemTemplate> <DataTemplate > <StackPanel Orientation="Horizontal" Height="45" Margin="0"> <Grid > <Grid.ColumnDefinitions> <ColumnDefinition Width="80"/> <ColumnDefinition Width="100"/> <ColumnDefinition Width="80"/> <ColumnDefinition Width="80"/> <ColumnDefinition Width="120"/> <ColumnDefinition Width="50"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <TextBlock Name="aaa" Text="{Binding ID}" FontSize="16" TextWrapping="Wrap" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" TextAlignment="Center" /> <Grid Grid.Column="1"> <!--<Popup PopupAnimation="Scroll" PlacementTarget="{Binding ElementName=AA}" IsOpen="{Binding IsChecked,ElementName=AA}" AllowsTransparency="True" Margin="0" Opened="pop_Opened" Closed="pop_Closed"> <Border Background="White" BorderBrush="Black" BorderThickness="1" Padding="4" Width="300" Height="360" CornerRadius="3"> <cs:NumKeyBoard IsChecked="{Binding IsChecked, ElementName=AA, Mode=TwoWay}" IsDynamicGiveTag="True"/> </Border> </Popup> <cs:A_Write_1 x:Name="AA" TagWriteText="{Binding TagWriteText}" TagReadText="{Binding TagReadText}" Style="{StaticResource InputBox}" Checked="Write_Checked" IsDisplayPostfix="False"/>--> </Grid> <TextBlock Text="{Binding dbValue}" FontSize="16" TextWrapping="Wrap" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" TextAlignment="Center" Grid.Column="2" /> <TextBlock Text="{Binding mValue}" FontSize="16" TextWrapping="Wrap" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" TextAlignment="Center" Grid.Column="3" /> <TextBlock Text="{Binding iValue}" FontSize="16" TextWrapping="Wrap" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" TextAlignment="Center" Grid.Column="4"/> <TextBlock Text="{Binding qValue}" FontSize="16" TextWrapping="Wrap" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" TextAlignment="Center" Grid.Column="5"/> <TextBlock Text="{Binding Times}" FontSize="16" TextWrapping="Wrap" Foreground="#FFFCFCF9" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10,0,0,0" Grid.Column="6"/> </Grid> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> </Border> <Border Grid.Column="1" Margin="3,0" CornerRadius="10" BorderBrush="#FF3BAABC" BorderThickness="1"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <ListBox x:Name="day" Background="{x:Null}" BorderBrush="{x:Null}" ItemsSource="{Binding DataTable2Property.DataTablePropertyList}" SelectedItem="{Binding DataTable3Property}" ItemContainerStyle="{StaticResource day}"> <ListBox.ItemTemplate> <DataTemplate > <RadioButton Style="{DynamicResource RadioButtonStyle}" Command="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type local:DataTableView}}, Path=DataContext.Select2Command,Mode=TwoWay}" CommandParameter="{Binding}"> <RadioButton.IsChecked> <Binding Path="IsSelected" RelativeSource="{RelativeSource AncestorType=ListBoxItem}" Mode="TwoWay"/> </RadioButton.IsChecked> <StackPanel Width="130"> <TextBlock Text="{Binding day}" Tag="{Binding Tag}" Foreground="White" FontSize="16" Padding="5,0" HorizontalAlignment="Left" VerticalAlignment="Center" TextAlignment="Center" TextWrapping="Wrap"/> </StackPanel> </RadioButton> </DataTemplate> </ListBox.ItemTemplate> </ListBox> <ListBox x:Name="month" Grid.Column="1" BorderBrush="{x:Null}" Background="{x:Null}" ItemsSource="{Binding DataTable1Property.DataTablePropertyList}" SelectedItem="{Binding DataTable2Property}" ItemContainerStyle="{StaticResource month}"> <ListBox.ItemTemplate> <DataTemplate > <RadioButton Style="{DynamicResource RadioButtonStyle}" Command="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type local:DataTableView}}, Path=DataContext.Select1Command,Mode=TwoWay}" CommandParameter="{Binding}"> <RadioButton.IsChecked> <Binding Path="IsSelected" RelativeSource="{RelativeSource AncestorType=ListBoxItem}" Mode="TwoWay"/> </RadioButton.IsChecked> <StackPanel Width="130"> <TextBlock Text ="{Binding month}" Tag="{Binding Tag}" Foreground="White" FontSize="16" Padding="5,0" TextWrapping="Wrap"/> </StackPanel> </RadioButton> </DataTemplate> </ListBox.ItemTemplate> </ListBox> <ListBox x:Name="year" Grid.Column="2" BorderBrush="{x:Null}" Background="{x:Null}" ItemsSource="{Binding DataTablePropertyList}" SelectedItem="{Binding DataTable1Property}" ItemContainerStyle="{StaticResource year}"> <ListBox.ItemTemplate> <DataTemplate > <RadioButton Style="{DynamicResource RadioButtonStyle}" Command="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type local:DataTableView}}, Path=DataContext.SelectCommand,Mode=TwoWay}" CommandParameter="{Binding}"> <RadioButton.IsChecked> <Binding Path="IsSelected" RelativeSource="{RelativeSource AncestorType=ListBoxItem}" Mode="TwoWay"/> </RadioButton.IsChecked> <StackPanel Width="130"> <TextBlock Text ="{Binding year}" Foreground="White" FontSize="16" Padding="5,0" TextWrapping="Wrap"/> </StackPanel> </RadioButton> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> </Border> </Grid> </UserControl>
别忘了在view后台添加viewModel绑定
public DataTableView() { InitializeComponent(); this.DataContext = new DataTableViewModel(); }
3、编写viewModel控制代码
class DataTableViewModel : ViewModelBase { [ImportingConstructor] public DataTableViewModel() { SelectCommand = new RelayCommand<DataTableModel>(t => Select(t)); Select1Command = new RelayCommand<DataTableModel>(t => Select1(t)); Select2Command = new RelayCommand<DataTableModel>(t => Select2(t)); Init(); } #region Command public RelayCommand<DataTableModel> SelectCommand { get; set; } public RelayCommand<DataTableModel> Select1Command { get; set; } public RelayCommand<DataTableModel> Select2Command { get; set; } #endregion #region 属性集 /// <summary> /// 参数表数据集 /// 用于前台控件绑定 /// </summary> public DataTableModel DataTableProperty { get { return _DataTableProperty; } set { _DataTableProperty = value; RaisePropertyChanged(); } } private DataTableModel _DataTableProperty = new DataTableModel(); /// <summary> /// 年级目录 /// </summary> public DataTableModel DataTable1Property { get { return _DataTable1Property; } set { _DataTable1Property = value; RaisePropertyChanged(); } } private DataTableModel _DataTable1Property = new DataTableModel(); /// <summary> /// 月级目录 /// </summary> public DataTableModel DataTable2Property { get { return _DataTable2Property; } set { _DataTable2Property = value; RaisePropertyChanged(); } } private DataTableModel _DataTable2Property = new DataTableModel(); /// <summary> /// 日目录 /// </summary> public DataTableModel DataTable3Property { get { return _DataTable3Property; } set { _DataTable3Property = value; RaisePropertyChanged(); } } private DataTableModel _DataTable3Property = new DataTableModel(); /// <summary> /// Model类节点数据集 /// </summary> public ObservableCollection<DataTableModel> DataTablePropertyList { get { return _DataTablePropertyList; } set { _DataTablePropertyList = value; RaisePropertyChanged(); } } private ObservableCollection<DataTableModel> _DataTablePropertyList; #endregion /// <summary> /// 年目录点击事件 /// </summary> /// <param name="ColumnsName">选中年份</param> private void Select(DataTableModel ColumnsName) { DataTable1Property = ColumnsName;//将事件调用获取到的参数传给年目录依赖属性,触发前台页面刷新 DataTable2Property = ColumnsName.DataTablePropertyList[0]; } /// <summary> /// 表单数据生成 /// </summary> /// <param name="ColumnsName">显示列名</param> private void Select1(DataTableModel ColumnsName) { DataTable2Property = ColumnsName; DataTable3Property = ColumnsName.DataTablePropertyList[0]; } private void Select2(DataTableModel ColumnsName) { DataTable3Property = ColumnsName; } /// <summary> /// 数据初始化 /// </summary> private void Init() { try { List<string> namey = new List<string>(); List<string> namem = new List<string>(); //获取年目录 if (Directory.Exists(Environment.CurrentDirectory + @"\DATA") == true)//检查年份文件夹 { string[] Y = Directory.GetDirectories(Environment.CurrentDirectory + @"\DATA");//获取文件夹名称带路径 for (int i = 0; i < Y.Length; i++) { //string YYY = YY[YY.Length - 1];//获取文件夹名 //namey.Add(YYY); DataTableModel a = new DataTableModel(); string[] YY = Y[i].Split('\\');//分割路径 a.year = YY[YY.Length - 1];//获取文件夹名 //获取月数据库名 string[] M = Directory.GetFileSystemEntries(Y[i]);//获取文件名称带路径 DataTablePropertyList = new ObservableCollection<DataTableModel>(); //生成月目录 for (int ii = 0; ii < M.Length; ii++) { DataTableModel aa = new DataTableModel(); string[] MM = M[ii].Split('\\');//分割路径 string MMM = MM[MM.Length - 1];//获取文件夹名 aa.month = MMM.TrimEnd('.', 'd', 'b'); DataBase DataBasea = new DataBase(@"Data Source=" + M[ii]);//实例化DataBase类,使数据库功能静态类连接指向当月数据库 //查询当月数据库所有表名 DataTable dta = DataBase.Sqlcmd_Datareader("select name from sqlite_master where type='table' order by name"); for (int iii = 0; iii < dta.Rows.Count; iii++) { DataTableModel aaa = new DataTableModel(); aaa.day = dta.Rows[iii][0].ToString(); DataTable DT = DataBase.Sqlcmd_Datareader("select* from " + aaa.day); //生成数据表单 for (int iiii = 0; iiii < DT.Rows.Count; iiii++) //for (int iiii = 0; iiii < 10; iiii++) { DataTableModel aaaa = new DataTableModel();//表单对象 //aaaa.ID = iiii.ToString(); aaaa.ID = DT.Rows[iiii][0].ToString(); aaaa.dbValue = DT.Rows[iiii][1].ToString(); aaaa.mValue = DT.Rows[iiii][1].ToString(); aaaa.iValue = DT.Rows[iiii][2].ToString(); aaaa.qValue = DT.Rows[iiii][2].ToString(); aaaa.Times = DT.Rows[iiii][3].ToString(); aaa.DataTablePropertyList.Add(aaaa);//将表单对象添加到日对象集中,月和年通过嵌套循环逐层添加 } aa.DataTablePropertyList.Add(aaa); } a.DataTablePropertyList.Add(aa); } DataTablePropertyList.Add(a); } DataTable1Property = DataTablePropertyList[0]; Select(DataTable1Property); } } catch { MessageBox.Show("数据表单目录获取失败!", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } }