最近做個項目,需要導出DataGrid顯示的數據,中間遇到了不少的坑,在此紀錄一下,方便以後查看,也希望能給用到的人,一點幫助。 導出DataGrid顯示的數據,並不是導出DataGrid的ItemsSource,這兩者是有區別的,這裡紀錄的是導出DataGrid的顯示數據,也就是所見即所得的東西。 ...
最近做個項目,需要導出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年的時候已經修複這個問題了,但是不知道為什麼還是不起左右,有知道的,希望可以留言告知,謝謝。