当前位置:   article > 正文

WPF控件之DataGrid扩展应用(一)_wpf treedatagrid

wpf treedatagrid

一、分析

        这次的需求是:有一所学校,为了方便了解学校里所有班级的情况(这里就看各位想定义哪些情况了),故需要设计出一个在界面上既能查看每个班级中每个学生的信息,又能展示每个班级的总体情况。

        我分析的是:结合前面所讲述的知识点,查看每个班级用TreeView,展示总体情况用DataGrid。那就动手看看吧

二、实现

        先看项目结构(注意个模块得统一:要么用core,要么用Framework)

 接下来逐一讲述

(1)common模块

        枚举(Enums):就一个枚举(节点类型)

  1. public enum NodeType
  2. {
  3. [Description("班级")]
  4. Grade,
  5. [Description("学生")]
  6. Student,
  7. }

        转换器(Converts):从枚举的值判断后返回是否显示(上代码)

  1. public class EnumToVisibilityConvert : IValueConverter
  2. {
  3. public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  4. {
  5. if (value is Enum enumValue)
  6. {
  7. switch (enumValue)
  8. {
  9. case NodeType.Grade:
  10. return Visibility.Collapsed;
  11. case NodeType.Student:
  12. return Visibility.Visible;
  13. default:
  14. return Visibility.Collapsed;
  15. }
  16. }
  17. return Visibility.Collapsed;
  18. }
  19. public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  20. {
  21. return value;
  22. }
  23. }

         命令(DelegateCommand),这个就不做概述了,之前用的都是它,不知道的可以看我前面的文章

        对象(Models):就一个主体对象,树结构加附属对象——学生结构(看代码,BindableObject不讲述了,跟命令一样的,前面文章有)

  1. public abstract class BaseModel : BindableObject
  2. {
  3. public virtual string Name { get; set; }
  4. public virtual Guid Guid { get; set; }
  5. public virtual NodeType Node { get; set; } = NodeType.Grade;
  6. }

  1. public class TreeModel : BaseModel
  2. {
  3. private ObservableCollection<StudentInfo> student = new ObservableCollection<StudentInfo>();
  4. public ObservableCollection<StudentInfo> Student
  5. {
  6. get => student;
  7. set
  8. {
  9. student = value;
  10. OnPropertyChanged();
  11. }
  12. }
  13. }

  1. public class StudentInfo : BaseModel
  2. {
  3. private string gender;
  4. public string Gender
  5. {
  6. get => gender;
  7. set
  8. {
  9. gender = value;
  10. OnPropertyChanged();
  11. }
  12. }
  13. private int age;
  14. public int Age
  15. {
  16. get => age;
  17. set
  18. {
  19. age = value;
  20. OnPropertyChanged();
  21. }
  22. }
  23. private string address;
  24. public string Address
  25. {
  26. get => address;
  27. set
  28. {
  29. address = value;
  30. OnPropertyChanged();
  31. }
  32. }
  33. private string number;
  34. public string Number
  35. {
  36. get => number;
  37. set
  38. {
  39. number = value;
  40. OnPropertyChanged();
  41. }
  42. }
  43. }

        管理者(TreeViewManager):管理整个TreeView的逻辑交互(这次的封装不太好,大部分的逻辑,ViewModel那里就实现了,都没到这管理库来——哈哈哈,应该是我想偷懒,不想提取了)

 

  1. public class TreeViewManager : TreeViewInterface
  2. {
  3. private static ObservableCollection<TreeModel> treeModels = new ObservableCollection<TreeModel>();
  4. public static ObservableCollection<TreeModel> TreeModels
  5. {
  6. get => treeModels;
  7. set
  8. {
  9. treeModels = value;
  10. OnTreeModelsChanged();
  11. }
  12. }
  13. public static event EventHandler TreeModelsChanged;
  14. public static void OnTreeModelsChanged()
  15. {
  16. TreeModelsChanged?.Invoke(TreeModels, new EventArgs());
  17. }
  18. public void InitTree()
  19. {
  20. TreeModel treeModel = new TreeModel()
  21. {
  22. Guid = Guid.NewGuid(),
  23. Name = "三年级二班",
  24. Node = Enums.NodeType.Grade
  25. };
  26. StudentInfo student = new StudentInfo()
  27. {
  28. Name = "小红",
  29. Guid = treeModel.Guid,
  30. Age = 8,
  31. Gender = "女",
  32. Address = "天河路",
  33. Node = Enums.NodeType.Student,
  34. Number = "111111"
  35. };
  36. treeModel.Student.Add(student);
  37. student = new StudentInfo()
  38. {
  39. Name = "小明",
  40. Guid = treeModel.Guid,
  41. Age = 8,
  42. Gender = "男",
  43. Address = "尧新路",
  44. Node = Enums.NodeType.Student,
  45. Number = "222222"
  46. };
  47. treeModel.Student.Add(student);
  48. student = new StudentInfo()
  49. {
  50. Name = "小黑",
  51. Guid = treeModel.Guid,
  52. Age = 7,
  53. Gender = "男",
  54. Address = "水西路",
  55. Node = Enums.NodeType.Student,
  56. Number = "333333"
  57. };
  58. treeModel.Student.Add(student);
  59. TreeModels.Add(treeModel);
  60. }
  61. }

(2)interface模块

        就一个,目录树的接口,对应一些方法(我这就只有一个方法。。。哈哈哈)

  1. public interface TreeViewInterface
  2. {
  3. void InitTree();
  4. }

(2)VM-V模块

        VM模块:一个是表格的,一个是目录树的,直接看代码吧

  1. public class DataGridViewModel : BindableObject
  2. {
  3. private TreeModel treeModel;
  4. public TreeModel TreeModel
  5. {
  6. get => treeModel;
  7. set
  8. {
  9. treeModel = value;
  10. OnPropertyChanged();
  11. }
  12. }
  13. public DataGridViewModel(TreeModel model)
  14. {
  15. this.TreeModel = model;
  16. }
  17. }

  1. public class TreeViewModel : BindableObject
  2. {
  3. private readonly TreeViewInterface @interface;
  4. public ObservableCollection<TreeModel> TreeModels => TreeViewManager.TreeModels;
  5. private TreeModel treeModel;
  6. public TreeModel TreeModel
  7. {
  8. get => treeModel;
  9. set
  10. {
  11. treeModel = value;
  12. OnPropertyChanged();
  13. }
  14. }
  15. private StudentInfo student;
  16. public StudentInfo Student
  17. {
  18. get => student;
  19. set
  20. {
  21. student = value;
  22. OnPropertyChanged();
  23. }
  24. }
  25. private BaseModel baseModel;
  26. public BaseModel BaseModel
  27. {
  28. get => baseModel;
  29. set
  30. {
  31. baseModel = value;
  32. OnPropertyChanged();
  33. }
  34. }
  35. public DelegateCommand<TreeView> SelectChangeCommand { get; private set; }
  36. public DelegateCommand<TreeModel> OpenTableCommand { get; private set; }
  37. public TreeViewModel(TreeViewInterface @interface)
  38. {
  39. this.@interface = @interface;
  40. this.SelectChangeCommand = new DelegateCommand<TreeView>(SelectChange);
  41. OpenTableCommand = new DelegateCommand<TreeModel>(OpenTable);
  42. InitTree();
  43. }
  44. #region 命令
  45. public void OpenTable(TreeModel model)
  46. {
  47. var win = new DataGridView(model);
  48. win.Show();
  49. }
  50. #endregion
  51. #region 事件
  52. public void SelectChange(TreeView tree)
  53. {
  54. BaseModel = (BaseModel)tree.SelectedItem;
  55. JudgeNode();
  56. }
  57. #endregion
  58. #region 方法
  59. private void InitTree()
  60. {
  61. this.@interface.InitTree();
  62. BaseModel = TreeModels[0];
  63. }
  64. private void JudgeNode()
  65. {
  66. switch (BaseModel.Node)
  67. {
  68. case TreeViewChangeDataGrid.Common.Enums.NodeType.Grade:
  69. TreeModel = (TreeModel)BaseModel;
  70. break;
  71. case TreeViewChangeDataGrid.Common.Enums.NodeType.Student:
  72. Student = (StudentInfo)BaseModel;
  73. break;
  74. default:
  75. break;
  76. }
  77. }
  78. #endregion

        V模块:一个是表格的,一个是目录树的(记得安装之前讲过的behavior的包)

                a、树的界面,这次我用了之前没讲过的方法,这次树不是递归的结构,所有要么用我第一篇文章那样绑定(那方法有点老套了),要么就换一种方法(看图)

 这就是新的绑定方法,跟之前很像,只是多了一个DataType,用来指定绑定的结构类型的(看代码)

  1. <Window
  2. x:Class="TreeViewChangeDataGrid.Views.TreeView"
  3. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  4. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  5. xmlns:convert="clr-namespace:TreeViewChangeDataGrid.Common.Converts;assembly=TreeViewChangeDataGrid.Common"
  6. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  7. xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
  8. xmlns:local="clr-namespace:TreeViewChangeDataGrid.Views"
  9. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  10. xmlns:model="clr-namespace:TreeViewChangeDataGrid.Common.Models;assembly=TreeViewChangeDataGrid.Common"
  11. Title="MainView"
  12. Width="800"
  13. Height="450"
  14. mc:Ignorable="d">
  15. <Window.Resources>
  16. <convert:EnumToVisibilityConvert x:Key="enumToVisibilityConvert" />
  17. <!-- 右键菜单 -->
  18. <ContextMenu x:Key="Custom">
  19. <MenuItem Header="表格查看">
  20. <i:Interaction.Triggers>
  21. <i:EventTrigger EventName="Click">
  22. <i:InvokeCommandAction Command="{Binding DataContext.OpenTableCommand, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}" CommandParameter="{Binding}" />
  23. </i:EventTrigger>
  24. </i:Interaction.Triggers>
  25. </MenuItem>
  26. </ContextMenu>
  27. <!-- 右键菜单 -->
  28. <HierarchicalDataTemplate DataType="{x:Type model:TreeModel}" ItemsSource="{Binding Student}">
  29. <Grid>
  30. <TextBlock Text="{Binding Name}" />
  31. <Grid.Style>
  32. <Style TargetType="Grid">
  33. <Setter Property="ContextMenu" Value="{StaticResource Custom}" />
  34. </Style>
  35. </Grid.Style>
  36. </Grid>
  37. </HierarchicalDataTemplate>
  38. <HierarchicalDataTemplate DataType="{x:Type model:StudentInfo}">
  39. <Grid>
  40. <TextBlock Text="{Binding Name}" />
  41. </Grid>
  42. </HierarchicalDataTemplate>
  43. </Window.Resources>
  44. <Grid Margin="10">
  45. <Grid.ColumnDefinitions>
  46. <ColumnDefinition Width="200" />
  47. <ColumnDefinition />
  48. </Grid.ColumnDefinitions>
  49. <TreeView x:Name="tree" ItemsSource="{Binding TreeModels}">
  50. <i:Interaction.Triggers>
  51. <i:EventTrigger EventName="SelectedItemChanged">
  52. <i:InvokeCommandAction Command="{Binding DataContext.SelectChangeCommand, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}" CommandParameter="{Binding ElementName=tree}" />
  53. </i:EventTrigger>
  54. </i:Interaction.Triggers>
  55. </TreeView>
  56. <StackPanel
  57. Grid.Column="1"
  58. VerticalAlignment="Center"
  59. Visibility="{Binding BaseModel.Node, Converter={StaticResource enumToVisibilityConvert}}">
  60. <StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
  61. <Label Margin="0,0,20,0" Content="姓名:" />
  62. <TextBox
  63. Width="100"
  64. VerticalContentAlignment="Center"
  65. Text="{Binding Student.Name}" />
  66. </StackPanel>
  67. <StackPanel
  68. Margin="0,20"
  69. HorizontalAlignment="Center"
  70. Orientation="Horizontal">
  71. <Label Margin="0,0,20,0" Content="年龄:" />
  72. <TextBox
  73. Width="100"
  74. VerticalContentAlignment="Center"
  75. Text="{Binding Student.Age}" />
  76. </StackPanel>
  77. <StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
  78. <Label Margin="0,0,20,0" Content="性别:" />
  79. <TextBox
  80. Width="100"
  81. VerticalContentAlignment="Center"
  82. Text="{Binding Student.Gender}" />
  83. </StackPanel>
  84. <StackPanel
  85. Margin="0,20"
  86. HorizontalAlignment="Center"
  87. Orientation="Horizontal">
  88. <Label Margin="0,0,20,0" Content="地址:" />
  89. <TextBox
  90. Width="100"
  91. VerticalContentAlignment="Center"
  92. Text="{Binding Student.Address}" />
  93. </StackPanel>
  94. <StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
  95. <Label Margin="0,0,20,0" Content="电话:" />
  96. <TextBox
  97. Width="100"
  98. VerticalContentAlignment="Center"
  99. Text="{Binding Student.Number}" />
  100. </StackPanel>
  101. </StackPanel>
  102. </Grid>
  103. </Window>

                b、表格的界面,从外到里设置,一次是DataGrid、Column、Row、Cell的样式,而对于列数据的绑定,我用的是DataGridTemplateColumn,直接看代码

 

 

  1. <Grid>
  2. <ScrollViewer
  3. Margin="10"
  4. HorizontalScrollBarVisibility="Auto"
  5. PreviewMouseWheel="PreviewMouseWheel"
  6. VerticalScrollBarVisibility="Auto">
  7. <DataGrid
  8. HorizontalAlignment="Center"
  9. AutoGenerateColumns="False"
  10. Background="#fff"
  11. BorderBrush="Transparent"
  12. CanUserAddRows="False"
  13. GridLinesVisibility="None"
  14. HorizontalScrollBarVisibility="Hidden"
  15. IsReadOnly="True"
  16. ItemsSource="{Binding DataContext.TreeModel.Student, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}">
  17. <!--#region 设置列的个数-->
  18. <DataGrid.Style>
  19. <Style TargetType="DataGrid">
  20. <Setter Property="HeadersVisibility" Value="Column" />
  21. </Style>
  22. </DataGrid.Style>
  23. <!--#endregion-->
  24. <!--#region 数据网格的列标题样式属性-->
  25. <DataGrid.ColumnHeaderStyle>
  26. <!-- 样式类型:DataGridColumnHeader(数据网格列标题) -->
  27. <Style TargetType="{x:Type DataGridColumnHeader}">
  28. <Setter Property="Height" Value="74" />
  29. <Setter Property="BorderThickness">
  30. <Setter.Value>
  31. <Thickness
  32. Bottom="1"
  33. Left="1"
  34. Right="1"
  35. Top="1" />
  36. </Setter.Value>
  37. </Setter>
  38. <Setter Property="Background" Value="Orange" />
  39. <Setter Property="BorderBrush" Value="Green" />
  40. <Setter Property="HorizontalContentAlignment" Value="Center" />
  41. <Setter Property="FontFamily" Value="微软雅黑" />
  42. <Setter Property="FontWeight" Value="Black" />
  43. <Setter Property="FontSize" Value="14" />
  44. </Style>
  45. </DataGrid.ColumnHeaderStyle>
  46. <!--#endregion-->
  47. <!--#region DataGrid的RowStyle属性-->
  48. <DataGrid.RowStyle>
  49. <Style TargetType="DataGridRow">
  50. <Setter Property="Background" Value="LightBlue" />
  51. </Style>
  52. </DataGrid.RowStyle>
  53. <!--#endregion-->
  54. <!--#region DataGrid的CellStyle属性(数据表格的单元格样式)-->
  55. <DataGrid.CellStyle>
  56. <Style TargetType="DataGridCell">
  57. <Setter Property="BorderThickness">
  58. <Setter.Value>
  59. <Thickness
  60. Bottom="1"
  61. Left="1"
  62. Right="1" />
  63. </Setter.Value>
  64. </Setter>
  65. <Setter Property="BorderBrush" Value="Green" />
  66. <Style.Triggers>
  67. <Trigger Property="IsSelected" Value="True">
  68. <Setter Property="Background">
  69. <Setter.Value>
  70. <SolidColorBrush Opacity="0.9" Color="AntiqueWhite" />
  71. </Setter.Value>
  72. </Setter>
  73. <Setter Property="Foreground" Value="Red" />
  74. </Trigger>
  75. </Style.Triggers>
  76. </Style>
  77. </DataGrid.CellStyle>
  78. <!--#endregion-->
  79. <DataGrid.Columns>
  80. <!--#region 姓名-->
  81. <DataGridTemplateColumn Header="姓名">
  82. <DataGridTemplateColumn.CellTemplate>
  83. <DataTemplate>
  84. <StackPanel
  85. Margin="20"
  86. HorizontalAlignment="Center"
  87. VerticalAlignment="Center"
  88. Orientation="Horizontal">
  89. <TextBlock Text="{Binding Name}" />
  90. </StackPanel>
  91. </DataTemplate>
  92. </DataGridTemplateColumn.CellTemplate>
  93. </DataGridTemplateColumn>
  94. <!--#endregion-->
  95. <!--#region 年龄-->
  96. <DataGridTemplateColumn Header="年龄">
  97. <DataGridTemplateColumn.CellTemplate>
  98. <DataTemplate>
  99. <StackPanel
  100. Margin="20"
  101. HorizontalAlignment="Center"
  102. VerticalAlignment="Center"
  103. Orientation="Horizontal">
  104. <TextBlock Text="{Binding Age}" />
  105. </StackPanel>
  106. </DataTemplate>
  107. </DataGridTemplateColumn.CellTemplate>
  108. </DataGridTemplateColumn>
  109. <!--#endregion-->
  110. <!--#region 性别-->
  111. <DataGridTemplateColumn Header="性别">
  112. <DataGridTemplateColumn.CellTemplate>
  113. <DataTemplate>
  114. <StackPanel
  115. Margin="20"
  116. HorizontalAlignment="Center"
  117. VerticalAlignment="Center"
  118. Orientation="Horizontal">
  119. <TextBlock Text="{Binding Gender}" />
  120. </StackPanel>
  121. </DataTemplate>
  122. </DataGridTemplateColumn.CellTemplate>
  123. </DataGridTemplateColumn>
  124. <!--#endregion-->
  125. <!--#region 地址-->
  126. <DataGridTemplateColumn Header="地址">
  127. <DataGridTemplateColumn.CellTemplate>
  128. <DataTemplate>
  129. <StackPanel
  130. Margin="20"
  131. HorizontalAlignment="Center"
  132. VerticalAlignment="Center"
  133. Orientation="Horizontal">
  134. <TextBlock Text="{Binding Address}" />
  135. </StackPanel>
  136. </DataTemplate>
  137. </DataGridTemplateColumn.CellTemplate>
  138. </DataGridTemplateColumn>
  139. <!--#endregion-->
  140. <!--#region 地址-->
  141. <DataGridTemplateColumn Header="电话">
  142. <DataGridTemplateColumn.CellTemplate>
  143. <DataTemplate>
  144. <StackPanel
  145. Margin="20"
  146. HorizontalAlignment="Center"
  147. VerticalAlignment="Center"
  148. Orientation="Horizontal">
  149. <TextBlock Text="{Binding Number}" />
  150. </StackPanel>
  151. </DataTemplate>
  152. </DataGridTemplateColumn.CellTemplate>
  153. </DataGridTemplateColumn>
  154. <!--#endregion-->
  155. </DataGrid.Columns>
  156. </DataGrid>
  157. </ScrollViewer>
  158. </Grid>

三、总结

        总体思路是目录树显示个体信息,右键打开表格看总体信息,运行效果看图

四、补充

        (1)这次的案例并不算是典型案例,我自己随意想的,只是为了扩展下思路;

        (2)目录树我没有添加其他的功能,感兴趣的可以自行看我之前的文章扩展功能;

        (3)这次的案例需求简单,所以实现起来简单,如果是项目上,会有动态添加新的列,添加按钮,右键菜单等等;(后面感兴趣的可以跟我交流)

        (4)下次我想换个玩法了,从数据库中提取数据来渲染,不想每次的造数据,具体容我想想

        (5)这次案例的源码:GitHub - TQtong/TreeViewChangeDataGrid

五、结束

        这次的文章就到这了, 那么我继续闯荡江湖了,希望各位指出不足之处,

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/582872
推荐阅读