WPF_DataGrid

ListView

ListView专门针对显示相同数据的不同视图而设计的,常用于显示每个数据项几部分信息的多列视图。ListView继承自ListBox类,并使用View属性进行扩展。

从技术角度看,View指向继承自ViewBase类的任意实例。ViewBase是一个将两个样式捆绑在一起的包,其中一个样式应用到ListView控件(通过DefaultStyleKey属性),而另一个应用到ListView控件中的项(通过ItemContainerDefaultStyleKey属性)。

事实上,为创建能够自定义的具有多列的列表,不需要使用具有View属性的ListView类。通过ListBox控件使用模板和样式也可获得相同的效果。但View属性是一个很有用的抽象概念,其优点有:

  • 可重用的视图
  • 多视图
  • 更好的组织

使用GridView创建列

<ListView Name="lstProducts">
    <ListView.View>
        <GridView>
            <GridView.Columns>
                <GridViewColumn Header="Name" 
                    DisplayMemberBinding="{Binding Path=ModelName}"/>
                <GridViewColumn Header="Model"
                    DisplayMemberBinding="{Binding Path=ModelNumber}"/>
                <GridViewColumn Header="Price"
                    DisplayMemberBinding="{Binding Path=UnitCost, StringFormat={}{0:C}}"/>
            </GridView.Columns>
        <GridView>
    </ListView.View>
</ListView>

为了在单元格中显示数据,DisplayMemberBinding属性不是唯一的方式,通过设置 CellTemplate 属性也可以。

<GridViewColumn Header="Price" Width="100">
    <GridViewColumn.CellTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Path=Description}" TextWrapping="Wrap"/>
            <!--<Image Source="{Binding Path=ProductImagePath, Converter={StaticResource ImagePathConverter}}>-->
        </DataTemplate>
    <GridViewColumn.CellTemplate>
</GridViewColumn>

因为要绑定到具体的字段,所以不同列没办法重用模板。
如果同时设置了 DisplayMember 和 CellTemplate 属性,会使用前者为单元格设置内容并忽略模板。

TreeView

TreeView控件在本质上是驻留 TreeViewItem 对象的特殊ItemsControl控件。每个TreeViewItem对象都是单独的ItemsControl 控件,可以包含更多的 TreeViewItem 对象。

从技术角度看,TreeViewItem类继承自HeaderedItemsControl类,该类又继承自ItemsControl类。WPF还提供了另外两个HeaderedItems类:MenuItem和ToolBar.

创建数据绑定的TreeView控件

public class Category : INotifyPropertyChanged
{
    private string categoryName;
    public string CategoryName
    {
        get { reutrn categoryName; }
        set {
            categoryName = value;
            OnPropertyChanged(new PropertyChangedEventArgs("CategoryName"));
        }
    }

    private ObservableCollection<Product> products;
    public ObservableCollection<Product> Products
    {
        get { reutrn products; }
        set {
            products = value;
            OnPropertyChanged(new PropertyChangedEventArgs("Products"));
        }
    }
}

为了显示CategoryName属性,需要提供能处理绑定对象的 TreeView.ItemTemplate.

<TreeView>
    <TreeView.ItemTemplate>
        <!--HierarchicalDataTemplate能够封装第二个模板-->
        <!--两个模板分别用于两个层次,第二个模板使用从第一个模板中选择的项作为数据源-->
        <HierarchicalDataTemplate ItemsSource="{Bindign Path=Products}">
            <TextBlock Text="{Binding Path=CategoryName}"/>
            <HierarchicalDataTemplate.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Path=ModelName}"/>
                </DataTemplate>
            <HierarchicalDataTemplate.ItemTemplate>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

上面的方式是同时设置了两层模板,但分解每个数据模板并通过数据类型(而不是位置)将之应用到数据对象的情况更加普遍。

<Window.Resources>
    <HierarchicalDataTemplate DataType="{x:Type local:Category}" 
        ItemsSource="{Binding Path=Products}">
        <TextBlock Text="{Binding Path=ModelName}"/>
    </HierarchicalDataTemplate>
    <HierarchicalDataTemplate DataType="{x:Type local:Product}">
        <TextBlock Text="{Binding Path=ModelName}"/>
    </HierarchicalDataTemplate>
</Widnow.Resources>
<!--TreeView没有显示设置ItemTemplate,而是根据绑定对象的数据类型匹配-->
<TreeView />

DataGrid

DataGrid控件的基本显示属性如下:

属性名 说明
RowBackground 绘制每行背景的画刷
AlternatingRowBackground 绘制交替行,默认奇数白色背景,偶数灰色背景
ColumnHeaderHight 顶部的列标题行的高度
RowHeaderWidth 行头的宽度
ColumnWidth 每列默认宽度
RowHeight 每行的高度
GridLinesVisibility 是否显示网格线的 DataGridGridlines枚举值
VerticalGridLinesBrush 绘制列之间网格线的画刷
HorizontalGridLinesBrush 绘制行之间网格线的画刷
HeadersVisibility 是否显示行头和列头
HorizontalScrollBarVisibility 是否显示水平滚动条
VerticalScrollBarVisibility 是否显示垂直滚动条
CanUserReorderColumns 是否允许拖动列来改变显示顺序
CanUserResizeColumns 是否允许调整列的宽度
AutoGenerateColumns 是否自动根据公共属性生成列

自定义列

DataGrid控件支持集中类型的列,通过继承自DataGridColumn的不同类来表示:

  • DataGridTextColumn - 值被转换为文本,并在TextBlock中显示;当编辑行时,TextBlcok元素被替换为标准的文本框
  • DataGridCheckBoxColumn - 列显示复选框,为Boolean值自动使用这种类型。通常,复选框是只读的;当编辑行时,会变成普通的复选框
  • DataGridHyperlinkColumn - 列显示可单击的链接
  • DataGridComboBox - 编辑模式下会变成下拉的 ComboBox控件
  • DataGridTemplateColumn - 允许为显示列值定义数据模板

设置列的格式和样式

可使用与设置TextBlock元素格式相同的方式设置 DataGridTextureColumn 元素的格式,但并没有提供TextBlock的所有属性,这种情况可使用 ElementStyle 属性。ElementStyle属性用于创建应用于 DataGrid 单元格内部的元素的样式。

<DataGridTextColumn>
    <DataGridTextColumn.ElementStyle>
        <Style TargetType="TextBlock">
            <Setter Property="TextWrapping" Value="Wrap"/>
        </Style>
    </DataGridTextColumn.ElementStyle>
</DataGridTextColumn>

DataGrid提供了少部分用于设置网格其他部分格式的额外属性:

属性 样式的使用范围
ColumnHeaderStyle 位于网格顶部的列题头的 TextBlock
RowHeaderStyle 行题头的 TextBlock
DragIndicatorStyle 当用户正在将列题头拖动到新位置时用于列题头的 textBlock
RowStyle 用于普通行(没设置ElementStyle属性)的 textBlock
ElementStyle 用于创建应用于DataGrid单元格内部元素的样式

设置行的格式

对于设置行格式,LoadingRow事件是个强大的工具,允许开发者对当前行数据进行简单的范围检查、比较以及更复杂的操作。

当每一行出现在屏幕上时,会立即引发 LoadingRow 事件,这样不会格式化整个网格。为降低内存开销,在数据中滚动时DataGird控件为显示新数据而重用相同的DataGridRow对象(这也是为什么叫LoadingRow而不是CreatingRow的原因)。如果不慎,DataGrid控件能够将数据加载到已经格式化了的DataGridRow对象中。

// 为价格较高的项提供明亮的橙色背景
private void gridProducts_LoadingRow(object sender, DataGridRowEventArgs e)
{
    // 获取行数据对象
    Product product = (Product)e.Row.DataContext;

    // 条件
    if (product.UnitCost > 100)
    {
        e.Row.Background = highlightBrush;
    }
    else
    {
        e.Row.Background = normalBrush;
    }
}

显示行细节

行细节是一块可选的独立显示区域,在行的列值的下面显示:

  • 能够跨越DataGrid控件的整个宽度,并且不会切入到独立的列中,从而提供了更多可供使用的空间
  • 可配置行细节区域,从而只为选择的行显示该区域,当不需要时允许用户折叠额外的细节
<DataGrid.RowDetailsTemplate>
    <DataTemplate>
        <Border BorderBrush="SteelBlue" CornerRadius="5">
            <TextBlock Text="{Binding Path=Description}" TextWrapping="Wrap" />
        </Border>
    </DataTemplate>
</DataGrid.RowDetailsTemplate>

冻结列

冻结列必须位于网格的左侧,通过设置 ForzenColumnCount 属性来决定冻结的列数量。

<DataGrid ForzenColumnCount="1"/>

排序

DataGrid 控件内置了排序功能,只要绑定到实现了 IList 接口的集合(List和ObservableCollection),DataGrid控件就可以自动获得排序功能。

用户单击列头,可以根据此列进行排序;按住Shift键可以根据多列进行排序。

编辑

DataGrid控件以几种方式限制编辑功能:

  • DataGrid.IsReadOnly - 值为true时不能编辑
  • DataGridColumn.IsReadOnly - 值为true时,不能编辑该列
  • 只读属性 - 如果数据对象的属性没有 set 方法,不能编辑该列

当单元格切换到编辑模式时发生的变化取决于列的类型。DataGridTextColumn显示文本框,DataGridCheckBox 列显示复选框,下面显示 DataPicker控件:

<DataGridTemplateColumn Header="Date Added">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Path=DateAdded, Converter={StaticResource DateOnlyConverter}}"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
    <DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <DatePicker SelectedDate="{Binding Path=DateAdded, Mode=TwoWay}"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>

DataGrid支持基本验证系统,当数据绑定中出问题和属性设置器抛出异常会显示红色外边框。可以通过 ValidationRules 对数据的合法性进行验证。

除此之外,也可以通过其他几种方法进行验证:

方法名 说明
BeginningEdit 当单元格进入编辑模式时发生
PreparingCellForEdit 用于模板列
CellEditEnding 当单元格正退出编辑模式时发生
RowEnditEnding 当用户在编辑完当前行之后导航到新行时发生

我的公众号

WPF_DataGrid

上一篇:easyui部分组件使用经验


下一篇:WPF DataGrid控件隐藏栏显示