1. 程式人生 > >NPOI導出WPF DataGrid控件顯示數據

NPOI導出WPF DataGrid控件顯示數據

項目 his convert sent points png pda 基本 target

最近做個項目,需要導出DataGrid顯示的數據,中間遇到了不少的坑,在此紀錄一下,方便以後查看,也希望能給用到的人,一點幫助。

導出DataGrid顯示的數據,並不是導出DataGrid的ItemsSource,這兩者是有區別的,這裏紀錄的是導出DataGrid的顯示數據,也就是所見即所得的東西。

舉個例子:

我這裏有個一個People的實體類,它包含了6個字段,如下

public class People
{
    public string Name { get; set; }
    public int Age { get; set; }
    public int
Sex { get; set; } public string ClassName { get; set; } public string GradeName { get; set; } public string SchoolName { get; set; } }

然而,將List<People>的集合給DataGrid的ItemsSource進行賦值,但是我頁面,顯示的是5個字段,甚至還有一個轉換器的使用

<DataGrid x:Name="dg_x" CanUserAddRows="False" AutoGenerateColumns
="False"> <DataGrid.Columns> <DataGridTextColumn Header="姓名" Binding="{Binding Name}" Width="*" /> <DataGridTextColumn Header="年齡" Binding="{Binding Age}" Width="*" /> <DataGridTextColumn Header="性別" Binding="{Binding Sex,Converter={StaticResource sexcov}}" Width
="*" /> <DataGridTextColumn Header="年級" Binding="{Binding GradeName}" Width="*" /> <DataGridTextColumn Header="學校" Binding="{Binding SchoolName}" Width="*" /> </DataGrid.Columns> </DataGrid>

技術分享

導出的效果就是DataGrid上展示的數據,加了少許的樣式

技術分享

接下來,我們介紹下我在寫代碼過程中遇到的兩個坑

一、代碼順序問題

做到上面的內容大致需要做兩個工作,第一個就是要取得到DataGrid的Cell的值,基本上百度一下,就會看到一個方法,好多的博客都有,如下

http://www.cnblogs.com/qq247039968/p/4066058.html

這個方法本身是沒啥問題,但是,當DataGrid有滾動條的時候,也就是DataGrid的Row沒有完全渲染完的情況就有問題了。

假設,我現在有30條數據,但是,我沒有拖動過滾動條,直接點導出的話,會拋出如下的異常:

技術分享

看了下代碼,是因為presenter這是Null,繼續打斷點跟蹤,發現此處numVisuals得到的值是0,而不是1,也就是說沒有獲取到內容,所以返回的Null。

技術分享

出現以上問題的原因,在於,以下代碼的一個順序問題

技術分享

大家都知道UpdateLayout函數是更新布局的意思,這兩段代碼,是先更新了布局,然後,滾動到rowIndex的位置,所以,依然沒有渲染成功。

正確的寫法是將兩句的順序換一下位置,先滾動,再更新,就不會出現問題了。

技術分享
public static class DataGridPlus
{
    /// <summary>
    /// 獲取DataGrid的行
    /// </summary>
    /// <param name="dataGrid">DataGrid控件</param>
    /// <param name="rowIndex">DataGrid行號</param>
    /// <returns>指定的行號</returns>
    public static DataGridRow GetRow(this DataGrid dataGrid, int rowIndex)
    {

        DataGridRow rowContainer = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(rowIndex);
        if (rowContainer == null)
        {
            dataGrid.ScrollIntoView(dataGrid.Items[rowIndex]);
            dataGrid.UpdateLayout();
            rowContainer = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(rowIndex);
        }
        return rowContainer;
    }
    /// <summary>
    /// 獲取父可視對象中第一個指定類型的子可視對象
    /// </summary>
    /// <typeparam name="T">可視對象類型</typeparam>
    /// <param name="parent">父可視對象</param>
    /// <returns>第一個指定類型的子可視對象</returns>
    public static T GetVisualChild<T>(Visual parent) where T : Visual
    {
        T child = default(T);

        int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
        for (int i = 0; i < numVisuals; i++)
        {
            Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
            child = v as T;
            if (child == null)
            {
                child = GetVisualChild<T>(v);
            }
            if (child != null)
            {
                break;
            }
        }
        return child;
    }
    /// <summary>
    /// 獲取DataGrid控件單元格
    /// </summary>
    /// <param name="dataGrid">DataGrid控件</param>
    /// <param name="rowIndex">單元格所在的行號</param>
    /// <param name="columnIndex">單元格所在的列號</param>
    /// <returns>指定的單元格</returns>
    public static DataGridCell GetCell(this DataGrid dataGrid, int rowIndex, int columnIndex)
    {
        DataGridRow rowContainer = dataGrid.GetRow(rowIndex);
        if (rowContainer != null)
        {
            DataGridCellsPresenter presenter = GetVisualChild<DataGridCellsPresenter>(rowContainer);
            DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(columnIndex);
            if (cell == null)
            {
                dataGrid.ScrollIntoView(rowContainer, dataGrid.Columns[columnIndex]);
                cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(columnIndex);
            }
            return cell;
        }
        return null;
    }
}
DataGridPlus

點擊導出之後,滾動條會自動滾到DataGrid的結尾處

技術分享

二、過多的創建了樣式和字體

技術分享

NPOI的Style是針對單元格的,所以,如果導出上面一個最普通的表格的話,就要至少9個樣式,這個不在於Font的字體或者怎麽樣,而是在於Border

粗和細的問題,基本上一個表格,最外面的框的粗線,內部是細線。

由於,第一次用NPOI,以前沒用過,所以,在設置樣式是,給放在了for裏面,當時的想法,就是判斷下處於列的什麽位置,或者是處於行的什麽位置,來設置Cell的樣式

但是,數量少的時候是沒問題的,當數量多了以後,由於不斷的New,導致,會提示Style的數量抄了,當時好像大概測試了2W條數據吧。

因此,需要將所有的樣式,都提出來,在最前面定義好,需要的時候,直接賦值就好了。

位置的話,通過枚舉來定義,方便使用

public enum CellPosition
{
    LeftTop,
    Top,
    RightTop,
    Left,
    Center,
    Right,
    LeftBottom,
    Bottom,
    RightBottom,
    None
}

還要註意的是NPOI可以通過DefaultColumnWidth來設置默認的寬度,但是DefaultRowHeight和DefaultRowHeightInPoints設置默認高度卻不好使,我看作者在13年的時候已經修復這個問題了,但是不知道為什麽還是不起左右,有知道的,希望可以留言告知,謝謝。

DEMO源碼

NPOI導出WPF DataGrid控件顯示數據