WPF DataGrid动态列

#程序员##万众创业##IT教育##.net##IT#

动态列在动态表显示时非常有用,常见的DataGrid显示时,xaml里静态指定了列名和列属性,而动态列是通过代码加列,在列名是动态时,比较有用。本例子来自于codeproject,经过锐英源的详细翻译,供大家学习,有些细节因为有闲暇巴盗用不能公布,有兴趣的请关注联系锐英源,锐英源有专业的开源企业服务。

介绍

Datagrid控件非常适合显示存储在表格中的数据。数据库表中的一行等于数据 格中的一行。当数据存储在多个表中时,比如表 A 和 B,并且行 A 与表 B 中的行具有一对多(也称为 1:N、父子或主从)关系,则行 A 可以引用表 B 中的多行。这种类型的数据可以在主从数据视图类型中显示。另一种类型的数据关系是多对多类型(也称为 N:M 关系)。表A的行可以对表B的行有多个引用。但是除了前面的情况,表B的一行可以被表A的多行引用。

使用代码

应用程序

示例代码实现了一个用户管理表单,可以在其中管理用户、角色和用户角色分配。角色和用户显示在两个数据 格中。用户角色分配在用户数据 格中完成。因此,此 格具有动态内容,将每个角色显示为单独的复选框列。通过选中相应的复选框来完成用户角色分配。

数据模型

这个样本的数据模型由一个User和一个Role表组成,一个UserRole表是其他两个表之间的关联表。表中的条目UserRole意味着用户(由其用户 id 引用)具有分配的角色(由角色的 id 引用)。如果某个用户-角色组合没有条目,则意味着相关用户没有分配相应的角色。

数据模型是使用 .NET 实现的DataSet。它是一个具有引用完整性的良好内存数据库,它包含发布数据行的插入、删除和修改的内置通知委托。它的内容可以存储到一个 XML 文件中,在本例中用作持久性机制。

组件和类图

下一个组件图显示了应用程序的分层:

  • 应用程序:包含 GUI 元素
  • ViewModel:包含业务逻辑
  • DataModel:包含数据定义和持久性
  • 应用

  • MainWindow: GUI 定义,用 XAML 编写
  • UserRoleValueConverter: 定义当用户选中或取消选中复选框时会发生什么的值转换器实现
  • 视图模型

  • MainViewModel:包含视图的显示数据表属性和动态列处理的数据逻辑
  • ColumnTag: 用于将对象标记到派生自 的实例的附加属性DependencyObject,在这种情况下DataGridColumn
  • 数据模型

  • DatabaseContext: 单例实例,包含UserRoleDataSet
  • UserRoleDataSet:数据库实现,基于DataSet
  • 执行

    数据绑定

    该应用程序是使用 MVVM 设计模式编写的。这意味着主窗口绑定到主视图模型,视图控件绑定到主视图模型的属性。

    参考

    查看控件属性

    ViewModel 属性

    1

    MainWindow:DataGridRoles.ItemsSource

    MainViewModel.Roles

    2

    MainWindow:DataGridUsers.ItemsSource

    MainViewModel.Users

    3

    MainWindow:DataGridUsers.Column

    MainViewModel.UserRoleColumns

    要点1:将数据库角色表绑定到角色数据 格控件

    要点2:将数据库用户表绑定到用户数据 格控件

    要点 3:将列的可观察集合绑定到用户 格控件的列属性。动态列行为是通过此属性实现的,因为视图模型中的逻辑在此集合中添加和删除列。

    注:要点。

                  <Window x:Class="Application.MainWindow"                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"                xmlns:attachedBehaviors="clr-namespace:Application.AttachedBehaviors"                xmlns:viewModel="clr-namespace:ViewModel;assembly=ViewModel"                Title="User Administration" Height="350" Width="525">               <Window.DataContext>  <viewModel:MainViewModel/>  </Window.DataContext>               <DockPanel LastChildFill="True">  <ToolBar DockPanel.Dock="Top">  <Button Content="Save" Command="{Binding SaveCommand}"/>  </ToolBar>               <Grid>  <Grid.RowDefinitions>  <RowDefinition Height="146*"/>  <RowDefinition Height="147*"/>  </Grid.RowDefinitions>               <GroupBox x:Name="UsersGroupBox"                Grid.Column="0"                Header="User Role Assignment">  <DataGrid x:Name="DataGridUsers"                ItemsSource="{Binding Users}"                attachedBehaviors:DataGridColumnsBehavior.BindableColumns=  "{Binding UserRoleColumns}"                AutoGenerateColumns="False"                EnableRowVirtualization="False"/>  </GroupBox>               <GroupBox x:Name="RolesGroupBox"                Grid.Row="1" Grid.Column="0"                Header="Roles">  <DataGrid x:Name="DataGridRoles"                ItemsSource="{Binding Roles}"                AutoGenerateColumns="False">  <DataGrid.Columns>  <DataGridTextColumn Header="Name"                Binding="{Binding Name}"/>  </DataGrid.Columns>  </DataGrid>  </GroupBox>  </Grid>  </DockPanel>  </Window>              

    数据处理

    数据保存在UserRoleDataSet(Role、User和RoleUser)中的三个表中。Role和User表通过DataView 绑定到数据 格控件。允许修改、插入和删除行以及阻止这些操作。DataView上也可以过滤和排序。数据 格控件可以使用DataView控制数据. 可以在数据 格控件中插入、修改和删除行( 格底部有一个新的项目行,按下删除键时删除行),通过DataView可以直接更新数据。

    C#

    public class MainViewModel{    public MainViewModel()    {        --- Code omitted ---        this.UserRoleColumns = new ObservableCollection<DataGridColumn>();  
            --- Code omitted ---    }    public DataView Users    {        get        {            return this.dataContext.DataSet.User.DefaultView;        }    }    public DataView Roles    {        get        {            return this.dataContext.DataSet.Role.DefaultView;        }    }    public ObservableCollection UserRoleColumns { get; private set; }}

    DataSet可以与数据库连接一起使用,以从 SQL 服务器等存储和检索数据。在这个应用程序中,我使用持久性机制在 XML 文件中存储和检索数据。

    每个DataSet表都有一组事件,可用于在数据修改时获得通知。此机制用于在修改角色表时添加、删除和更新动态列。

    C#

    public class MainViewModel{    public MainViewModel()    {        --- Code omitted ---        this.dataContext = DatabaseContext.Instance;        this.dataContext.DataSet.Role.RoleRowChanged += this.RoleOnRowChanged;        this.dataContext.DataSet.Role.RoleRowDeleted += this.RoleOnRoleRowDeleted;        --- Code omitted ---    }    private void RoleOnRowChanged(object sender,                                  UserRoleDataSet.RoleRowChangeEvent roleRowChangeEvent)    {        switch (roleRowChangeEvent.Action)        {            case DataRowAction.Change:                this.UpdateRoleColumn(roleRowChangeEvent.Row);                break;            case DataRowAction.Add:                this.AddRoleColumn(roleRowChangeEvent.Row);                break;        }    }    private void RoleOnRoleRowDeleted(object sender,                                      UserRoleDataSet.RoleRowChangeEvent roleRowChangeEvent)    {        if (roleRowChangeEvent.Action == DataRowAction.Delete)        {            this.DeleteRoleColumn(roleRowChangeEvent.Row);        }    }}

    商业逻辑

    默认列定义

    用户数据 格列定义存储在UserRolesColumns集合中。这意味着默认列,用户的名字和姓氏,也必须在此集合中。DataGridTextColumns为名字和姓氏实例化了两个,单元格内容通过绑定到行的各自字段来绑定到数据行。

    C#

    public class MainViewModel{    public MainViewModel()    {        this.GenerateDefaultColumns();        --- Code omitted ---    }    private void GenerateDefaultColumns()    {        this.UserRoleColumns.Add(new DataGridTextColumn        {            Header = "First Name", Binding = new Binding("FirstName")        });        this.UserRoleColumns.Add(new DataGridTextColumn        {            Header = "Last Name", Binding = new Binding("LastName")        });    }}

    动态列定义

    动态列处理分为 3 种操作类型:

  • AddRoleColumnRole: 将角色添加到表时调用。它实例化一个新的DataGridCheckBoxColumn,分配CheckBoxColumnStyle和UserRoleValueConverter。后者实现了用户角色分配逻辑(见下文)。该列使用角色实例进行标记,以便分配逻辑可以工作。列的标题设置为角色名称。
  • UpdateRoleColumn: 当角色行的内容被修改时调用。该逻辑扫描动态列集合以查找标记有已修改角色实例的列。找到后,列的标题将更新为角色名称。绑定机制自动更新数据 格中的列标题。
  • DeleteRoleRole: 从表中删除角色时调用。该逻辑扫描动态列集合以查找标记有已删除角色实例的列并移除该列。
  • C#

    public class MainViewModel{    private void AddRoleColumn(UserRoleDataSet.RoleRow role)    {        var resourceDictionary = ResourceDictionaryResolver.GetResourceDictionary("Styles.xaml");        var userRoleValueConverter = resourceDictionary["UserRoleValueConverter"] as IValueConverter;        var checkBoxColumnStyle = resourceDictionary["CheckBoxColumnStyle"] as Style;        var binding = new Binding                          {                              Converter = userRoleValueConverter,                              RelativeSource =                                  new RelativeSource(RelativeSourceMode.FindAncestor,         typeof(DataGridCell), 1),                              Path = new PropertyPath("."),                              Mode = BindingMode.TwoWay                          };        var dataGridCheckBoxColumn = new DataGridCheckBoxColumn                                         { Header = role.Name, Binding = binding, IsThreeState = false, CanUserSort = false, ElementStyle = checkBoxColumnStyle,                                         };        ObjectTag.SetTag(dataGridCheckBoxColumn, role);        this.UserRoleColumns.Add(dataGridCheckBoxColumn);    }    private void UpdateRoleColumn(UserRoleDataSet.RoleRow role)    {        if (role != null)        {            foreach (var userRoleColumn in this.UserRoleColumns)            {                var roleScan = ColumnTag.GetTag(userRoleColumn) as UserRoleDataSet.RoleRow;                if (roleScan == role)                {                    userRoleColumn.Header = role.Name;                    break;                }            }        }    }    private void DeleteRoleColumn(UserRoleDataSet.RoleRow role)    {        if (role != null)        {            foreach (var userRoleColumn in this.UserRoleColumns)            {                var roleScan = ColumnTag.GetTag(userRoleColumn) as UserRoleDataSet.RoleRow;                if (roleScan == role)                {                    this.UserRoleColumns.Remove(userRoleColumn);                    break;                }            }        }    }}

    用户角色分配

    将DataGridCheckBoxColumn复选框控件绑定到它正在显示的行中数据的(可为空的)布尔属性。在这种情况下,它将是用户数据行中的布尔属性,表示用户到角色分配。由于定义中没有这样的属性,因此UserTable必须实施另一种解决方案。不是绑定到复选框控件,而是实例化一个值转换器并将其绑定到DataGridCell将包含该控件的CheckBox控件。上面显示的方法中的Binding定义AddRoleColumn包含对值转换器的赋值。绑定控件的相对源设置为DataGridCell,作为CheckBox控件的祖先找到(绑定在CheckBox级别上定义)。

    Convert每次DataGrid最初修改单元格或失去焦点时,都会调用值转换器的方法。在这两种情况下,都会检索用户和角色角色并返回转换结果(如果用户具有分配的角色)。用户行从DataGridCell’s中获取DataContext,其中包含DataRowView在其Row属性中具有用户行的实例。角色是从ColumnTag添加时分配给列的 中检索的。

    C#

    public class UserRoleValueConverter : IValueConverter{    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)    {        bool result = false;        var dataGridCell = value as DataGridCell;        if (dataGridCell != null)        {            var dataRowView = dataGridCell.DataContext as DataRowView;            if (dataRowView != null)            {                var user = dataRowView.Row as UserRoleDataSet.UserRow;                var role = ColumnTag.GetTag(dataGridCell.Column) as UserRoleDataSet.RoleRow;                if (user != null && role != null)                {                    var checkBox = dataGridCell.Content as CheckBox;                    if (checkBox != null)                    {                        if (dataGridCell.IsEditing)                        {                            checkBox.Checked += this.CheckBoxOnChecked;                        }                        else                        {                            checkBox.Checked -= this.CheckBoxOnChecked;                        }                    }                    result =                        DatabaseContext.Instance.DataSet.UserRole.Any(                            x => x.UserRow == user && x.RoleRow == role);                }            }        }        return result;    }    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)    {        throw new NotImplementedException();    }

    CheckedBoxOnChecked每当修改复选框状态时,都会调用该方法。逻辑搜索CheckBox’sDataGridCell并获取属于它的用户和角色实例。它将根据CheckBox.IsChecked状态以及 UserRoleRow是否已经存在来添加或删除用户角色条目。

    C#

    private void CheckBoxOnChecked(object sender, RoutedEventArgs routedEventArgs)    {        var checkBox = sender as CheckBox;        var dataGridCell = ControlHelper.FindVisualParent(checkBox);        if (dataGridCell != null)        {            var dataRowView = dataGridCell.DataContext as DataRowView;            if (checkBox != null && dataRowView != null)            {                var user = dataRowView.Row as UserRoleDataSet.UserRow;                var role = ObjectTag.GetTag(dataGridCell.Column) as UserRoleDataSet.RoleRow;                if (user != null && role != null)                {                    if (checkBox.IsChecked == true                        && DatabaseContext.Instance.DataSet.UserRole.Any(                            x => x.UserRow == user && x.RoleRow == role) == false)                    {                        DatabaseContext.Instance.DataSet.UserRole.AddUserRoleRow(user, role);                    }                    else                    {                        var userRole =                            DatabaseContext.Instance.DataSet.UserRole.FirstOrDefault(                                x => x.UserRow == user && x.RoleRow == role);                        if (userRole != null)                        {                            userRole.Delete();                        }                    }                }            }        }    }}

    兴趣点

    结论

    声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

    上一篇 2022年6月18日
    下一篇 2022年6月18日

    相关推荐