交叉報表
# 交叉報表
這種報表具有表格結構,這意味著它由行和列組成。在設計時,不知道輸出表將具有多少行和列。這就是為什麼報表不僅會向下發展(如前面所述的報表型別),還會橫向發展。交叉表報表的典型示例如下所示。我們來看看錶中的元素:
在圖示中,我們看到一個包含兩行(行)和四列的表,其中「a」和「b」是行標題,「1」,「2」,「3」和「4」是列標題,「a1「......」a4「和」b1「......」b4「是表格中的單元。要構建這樣的報表,我們只需要一組數據(來自查詢或表),它有三個欄位幷包含以下值:
a 1 a1
a 2 a2
a 3 a3
a 4 a4
b 1 b1
b 2 b2
b 3 b3
b 4 b4
2
3
4
5
6
7
8
您可以看到第一個欄位包含一個行號,第二個欄位包含一個列號,第三個欄位包含指定行和列交叉處的單元格內容。輸出報表時,FastReport在記憶體中建立一個表並用數據填充它。因此,表格會動態擴充套件,建立尚不存在的行和列。標題可以出現在多個級別,如下所示:
在此示例中,列的數字或索引是複合的,即它由兩個值組成。此報表由以下數據產生:
a 10 1 a10.1
a 10 2 a10.2
a 20 1 a20.1
a 20 2 a20.2
b 10 1 b10.1
b 10 2 b10.2
b 20 1 b20.1
b 20 2 b20.2
2
3
4
5
6
7
8
這裡第一個欄位包含行索引,如前所述,第二個和第三個欄位包含列索引,最後一個欄位包含單元格值。在處理具有複雜標題的交叉表數據時,檢視FastReport如何構建記憶體表:
從此記憶體表輸出報表時,FastReport將連線具有相同值且位於同一級別的標題單元格。這是一個更復雜的交叉表報表,包含中間和總計:
此報表源自與以前相同的數據。以新顏色突出顯示的單元格中的值將自動計算,並且不會出現在原始數據集中。
# 1. 構建交叉報表
現在讓我們從理論轉向實踐。我們將構建一份簡單的交叉表報表。
使用SQL構造以下內容的數據集,將數據集匯入至設計器中。
SELECT '李明' AS FName,2018 AS FYear,89300 AS FSalary
UNION
SELECT '李明' AS FName,2019 AS FYear,98430 AS FSalary
UNION
SELECT '李明' AS FName,2020 AS FYear,118300 AS FSalary
UNION
SELECT '劉東' AS FName,2018 AS FYear,79580 AS FSalary
UNION
SELECT '劉東' AS FName,2019 AS FYear,91300 AS FSalary
UNION
SELECT '劉東' AS FName,2020 AS FYear,12300 AS FSalary
2
3
4
5
6
7
8
9
10
11
打開報表設計器界面,使用報表-數據...
功能表項連線至數據源,然後從設計器的對象工具欄中選擇DB交叉表對像
,並單擊設計頁面將對像放置於此,此時會自動打開交叉報表編輯器:
上圖中顯示專案的相關說明:
- 可用數據源的下拉選單。
- 所選數據源中的欄位列表; 此列表中的欄位可以拖動到編號為3,4或5的列表中。
- 產生行(行)標題的欄位列表。
- 產生列標題的欄位列表。
- 產生表格單元格的欄位列表。
- 表結構預覽。
- 結構選項:顯示標題,總數等。
您只能在此編輯器中使用滑鼠進行更改。對於我們的示例,只需要將列表2中的欄位拖到列表3,4和5(在上圖中)。之後,單擊[確定]
按鈕關閉編輯器。交叉表對像現在顯示其結構:
預覽報表時,您將看到類似於此的表格:
# 2. 更改外觀
讓我們修改交叉表對象的外觀。我們要做的第一件事是更改標題顏色並顯示「總計」。
將標題顏色更改為灰色,依次單擊FYear
,FName
和Grand Total
單元格,然後使用工具欄上的背景顏色
按鈕/選擇灰色。
我們還可以使用一組預定義的樣式。這些在交叉表編輯器中可用。在對像列表中選擇交叉報表對像使用滑鼠右鍵,點選編輯
按鈕打開交叉報表編輯器,在其中單擊選擇樣式
並選擇其中的一個樣式應用。
要更改兩個Grand Total
文字,請雙擊每個單元格,這將打開熟悉的文字編輯器,我們可以在其中鍵入合計
:
要格式化貨幣值,請選擇第一個單元格(在我們的示例中為[FName]
和[FYear]
的交集),右鍵單擊以顯示上下文功能表,然後選擇顯示格式...
。
選擇所需的格式並關閉格式編輯器。所有這些都會產生報表:
# 3. 使用函式
在我們的示例中,我們在合計
行中看到每個員工三年以上工資的總和。可以使用以下任何聚合函式:
SUM(合計)
:數值的和。MIN(最小值)
:數值的最小值。MAX(最大值)
:數值的最大值。AVG(平均值)
:數值的平均值。COUNT(計數)
:記錄的數量。
我們在示例中使用MIN
函式。打開交叉報表編輯器,在列表5(FSalary
欄位項)中單擊向下箭頭。
從下拉選單中選擇最小值
功能。現在我們可以將總單元格中的文字從合計
更改為最小值
。完成的報表如下所示:
# 4. 排序
預設情況下,行和列按數字或字母順序按升序排列,具體取決於數據型別。可以為行和列單獨設定排序模式。排序模式是:「按升序排列」,「按降序排列」和「不執行排序」。如果沒有排序,則行/列將以預設資料庫順序顯示。
讓我們在示例中更改列排序。讓年份按遞減順序排列。要執行此操作,請打開交叉表編輯器,選擇[FYear]
列元素並通過單擊向下箭頭並選擇降序來更改排序模式:
關閉編輯器並預覽報表。它看起來像這樣:
# 5. 帶有複合標題的表
我們之前的示例包含每行和單列標題的單個值。讓我們看一下具有多個值的複合頭的交叉表設計。我們將以上數據進行修改,顯示的SQL內容如下:
SELECT '李明' AS FName,2018 AS FYear,1 AS FMonth,19300 AS FSalary
UNION
SELECT '李明' AS FName,2018 AS FYear,2 AS FMonth,23300 AS FSalary
UNION
SELECT '李明' AS FName,2018 AS FYear,3 AS FMonth,13800 AS FSalary
UNION
SELECT '李明' AS FName,2019 AS FYear,2 AS FMonth,18430 AS FSalary
UNION
SELECT '李明' AS FName,2019 AS FYear,3 AS FMonth,12330 AS FSalary
UNION
SELECT '李明' AS FName,2020 AS FYear,1 AS FMonth,18300 AS FSalary
UNION
SELECT '劉東' AS FName,2018 AS FYear,1 AS FMonth,19580 AS FSalary
UNION
SELECT '劉東' AS FName,2018 AS FYear,3 AS FMonth,18580 AS FSalary
UNION
SELECT '劉東' AS FName,2019 AS FYear,2 AS FMonth,12300 AS FSalary
UNION
SELECT '劉東' AS FName,2020 AS FYear,3 AS FMonth,9800 AS FSalary
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
我們分別新增了包含月份編號和工作日數的「月」欄位。可以從這些數據中構建幾個不同的報表,例如:每年的員工工資,按月分解。我們會得到什麼樣的報表?它必須類似於前一個示例中的報表,但將年度數據按月細分。必須以與以前相同的方式設定交叉表對象。這次我們還將FMonth
欄位拖到列標題列表中,如下所示:
在預覽中,我們看到以下報表:
請注意,FastReport已自動新增一列中間總計,每年後顯示一列。通過取消選擇[FYear]
列元素的小計
標誌,可以在交叉表編輯器中關閉它:
另請注意,列標題列表中的最後一個列元素從不具有小計
標記(包括單個元素的大小寫)。在我們的示例中,我們不需要每個月的中間總計,因此可以關閉小計
標記。如果使用中間總計還有另一個特徵:最好將中間總數作為「年+年總數」而不是「總計」。在報表頁面上的交叉表對像中,雙擊中間的總單元格,然後在文字編輯器中鍵入:Total for [Value]
。
在報表構造中,[Value]
表達式將替換為上面單元格中列標題的實際值:
# 6. 調整單元格寬度
檢視前面的插圖,很明顯FastReport會自動調整單元格寬度,以便更大的單元格值確實適合單元格。然而,在某些情況下,這可能並不理想,因為超寬的列可能看起來很難看。
讓我們看看控制單元寬度的三種方法。控制單元格寬度的最簡單方法是在中間總計的文字中新增換行符,即:
Total
for [Value]
2
這樣可以讓表格看起來更緊湊。
但是,有些情況下很難或不可能手動地斷線。因此,交叉表對像具有MinWidth
和MaxWidth
屬性(指單元格寬度)。這兩個屬性只能通過對像檢查器來訪問。預設情況下,MinWidth
為0,MaxWidth
為200,這在大多數情況下是足夠的。
控制單元格寬度的第二種方法是根據特殊要求改變這些值。因此在我們的示例中,我們可以將MinWidth
和MaxWidth
都設定為50,這意味著數據單元必須至少為50畫素寬,即使單元格值適合較少的畫素。對於大單元格值,單元格寬度限制為MaxWidth
值,單元格中的文字將根據需要中斷。所以我們的例子現在看起來像這樣:
控制單元寬度的第三種方法是手動更改單元格寬度。為此,必須將AutoSize
屬性設定為False
。然後可以使用滑鼠更改交叉表單元格寬度。在報表頁面交叉表對像中,滑鼠游標在單元格邊框上更改形狀,因此允許拖動邊框。以下是可以實現的示例:
請記住,如果關閉自動調整大小,則交叉表不會自動調整單元格的寬度和高度,您可能會在報表預覽中看到類似的內容:
如果是這樣,那麼只需稍微增加單元格寬度。
# 7. 字型顏色和突出顯示
有時需要突出顯示值和/或更改字型顏色。我們在組報表示例中檢視了這一點,其中我們對文字對像使用了條件突出顯示。條件突出顯示對我們來說也很有用。要在示例報表中新增突出顯示,假設我們需要更改大於16000的值的字型顏色 - 選擇表示表格單元格的對象,並通過單擊工具欄上的突出顯示按鈕/來設定突出顯示參數。熟悉的突出顯示編輯器視窗將打開可以設定此條件的位置:
Value > 16000
並將字型設定為紅色:
這就是所需要的。單擊[確定]
按鈕關閉編輯器並預覽報表:
同樣,如果需要,可以突出顯示總值,還可以使用背景和框架顏色按鈕突出顯示單元格和線條。
# 8. 在指令碼中管理交叉表
如果上面顯示的任何方法無法實現所需的報表,則可以使用報表指令碼。交叉報表對像具有以下事件:
事件 | 描述 |
---|---|
OnAfterPrint | 在列印表格后呼叫事件 |
OnBeforePrint | 在列印表之前呼叫事件 |
OnCalcHeight | 在計算表中行的高度之前呼叫事件,事件處理程式可以設定為所需的高度,如果需要隱藏行,則為「0」 |
OnCalcWidth | 在計算表中的列寬之前呼叫事件,事件處理程式可以設定為所需的寬度,如果需要隱藏列,則為「0」 |
OnPrintCell | 在顯示錶格單元格之前呼叫事件,事件處理程式可以修改單元格設計或其內容 |
OnPrintColumnHeader | 在顯示列標題之前呼叫事件,事件處理程式可以修改標題單元格的設計或內容 |
OnPrintRowHeader | 在顯示行標題之前呼叫事件,事件處理程式可以修改標題單元格的設計或內容 |
我們可以在這些事件中使用「交叉表」對象的以下方法:
//返回表中的列數
function ColCount: Integer;
//返回表中的行數
function RowCount: Integer;
//如果'Index'列是總計,則返回True
function IsGrandTotalColumn(Index: Integer): Boolean;
//如果'Index'行是總數,則返回True
function IsGrandTotalRow(Index: Integer): Boolean;
//如果索引列是小計,則返回True
function IsTotalColumn(Index: Integer): Boolean;
//如果'Index'行是小計,則返回True
function IsTotalRow(Index: Integer): Boolean;
//為表新增一個值
procedure AddValue(const Rows, Columns, Cells: array of Variant);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
讓我們展示如何突出顯示第三列的內容。在報表設計頁面上選擇交叉表對象,在對像檢查器中單擊事件選項卡,找到OnPrintCell
事件並通過雙擊事件右側的
空列表在內碼表上建立處理程式名稱。指令碼編輯器將顯示為您建立的基本聲明,然後在聲明的空begin ... end
塊中新增所需的程式碼:
procedure Cross1OnPrintCell(Memo: TfrxMemoView;
RowIndex, ColumnIndex, CellIndex: Integer;
RowValues, ColumnValues, Value: Variant);
begin
if ColumnIndex = 2 then
Memo.Color := clRed;
end;
2
3
4
5
6
7
預覽報表時,我們會看到以下內容:
要突出顯示列標題,請以類似方式建立OnPrintColumnHeader
事件處理程式:
procedure DBCross1OnPrintColumnHeader(Memo: TfrxMemoView;
HeaderIndexes, HeaderValues, Value: Variant);
begin
if (VarToStr(HeaderValues[0]) = '2019') and
(VarToStr(HeaderValues[1]) = '3') then
Memo.Color := clRed;
end;
begin
end.
2
3
4
5
6
7
8
9
10
11
報表預覽效果:
這是指令碼的工作原理:在表的數據區域中列印單元格之前呼叫OnPrintCell
事件處理程式(請注意,表標題中的單元格呼叫OnPrintColumnHeader
或OnPrintRowHeader
處理程式)。OnPrintCell
處理程式參數包括:指向「文字」的鏈接表示單元格的對象(「備註」參數)和單元格的「地址」作為行,列和單元格的位置(如果交叉表包含多級單元格,則單元格相關)作為RowIndex
,分別為ColumnIndex
和CellIndex
參數。參數列表還具有指定為Variants的標題值(RowValues
和ColumnValues
參數)和儲存單元格內容的Value
Variant參數。
在我們的示例中,使用RowIndex
和ColumnIndex
指定「地址」更容易。列和行的編號從「0」開始,因此ColumnIndex = 2
指的是第三列。我們還可以通過檢視其數據內容來指定正確的列。
procedureCross1OnPrintCell(Memo: TfrxMemoView;
RowIndex, ColumnIndex, CellIndex: Integer;
RowValues, ColumnValues, Value: Variant);
begin
if (VarToStr(ColumnValues[0]) = '2019') and
(VarToStr(ColumnValues[1]) = '3') then
Memo.Color := clRed;
end;
2
3
4
5
6
7
8
RowValues
和ColumnValues
參數是Variant型別的陣列,具有零基數。「0」元素位於表格標題的最高級別,「1」元素位於下一級別,等等。在我們的示例中,ColumnValues [0]
包含年份,ColumnValues [1]
包含月份。
為什麼需要VarToStr
功能?這可以防止型別轉換期間出錯。使用Variant型別時,FastReport嘗試自動將字串轉換為數字格式,這反過來可能會在轉換Total
和Grand Total
列值時導致錯誤。
在輸出列標題單元格期間呼叫OnPrintColumnHeader
事件處理程式 。參數列表類似於OnPrintCell
處理程式的參數列表,但在這種情況下,單元格的「地址」(HeaderIndexes
和HeaderValues
參數)採用不同的形式。HeaderValues
參數與ColumnValues
和RowValues
中的值保持相同。OnPrintCell
處理程式。HeaderIndexes
參數也是Variant型別的值陣列,並且包含不同形式的標題單元格的地址:「0」元素是表標題中最高級別的索引,「1」是爲了闡明單元格編號的原理,請參
考下圖:
在我們的示例中,使用HeaderValues
參數更容易,但可以使用以下處理程式:
procedure Cross1OnPrintColumnHeader(Memo: TfrxMemoView;
HeaderIndexes, HeaderValues, Value: Variant);
begin
if (HeaderIndexes[0] = 0) and (HeaderIndexes[1] = 2) then
Memo.Color := clRed;
end;
2
3
4
5
6
# 9. 調整行、列大小
可以使用OnCalcWidth
和OnCalcHeight
事件處理程式調整表列和行的寬度和高度。讓我們看看如何通過以下示例增加某一年年月份的列的寬度。 建立一個OnCalcWidth
事件處理程式:
procedure DBCross1OnCalcWidth(ColumnIndex: Integer;
ColumnValues: Variant; var Width: Extended);
begin
if (VarToStr(ColumnValues[0]) = '2019') and
(VarToStr(ColumnValues[1]) = '3') then
Width := 100;
end;
2
3
4
5
6
7
要在我們的示例中隱藏列,只需將「寬度」設定為零。請注意,不會重新計算總計,因為此時表已填充了值。
# 10. 手動填寫表格
交叉報表有兩中對像型別,DB交叉報表以及交叉報表,到目前為止,我們已經處理了第一個對象,它附加到資料庫表中的數據。報表運行后,對像會自動填充。讓我們看看第二個對像交叉表。
「交叉表」對像未附加到資料庫表。因此必須手動填充數據。「交叉表」對像具有與「DB交叉表」對像類似的編輯器,但不同之處在於必須指定表的標題和單元格的維度,而不是由資料庫欄位設定:
讓我們通過一個例子展示「交叉表」對象的用法。在報表設計頁面上放置一個「交叉表」對象,並設定其屬性如上所示:行標題中的級別數為1,列標題中為2,單元格中為1.讓我們填寫表格使用OnBeforePrint
事件處理程式的數據:
procedure Cross1OnBeforePrint(Sender: TfrxComponent);
begin
with Cross1 do
begin
AddValue(['Ann'], [2001, 2], [1500]);
AddValue(['Ann'], [2001, 3], [1600]);
AddValue(['Ann'], [2002, 1], [1700]);
AddValue(['Ben'], [2002, 1], [2000]);
AddValue(['Den'], [2001, 1], [4000]);
AddValue(['Den'], [2001, 2], [4100]);
end;
end;
2
3
4
5
6
7
8
9
10
11
12
在處理程式中,數據通過TfrxCrossView.AddValue
方法新增到表中。此方法有三個參數,每個參數都是Variant型別的陣列。第一個參數是行值,第二個參數是列值,第三個參數是單元格值。請注意,每個陣列中的值的數量必須與對象的設定匹配!在我們的示例中,對像在行標題中有一個級別,列標題中有兩個級別,一個單元格級別,因此AddValue
方法的Variant陣列參數需要一個值用於行,兩個值用於列,一個值用於單元格。
在預覽時,報表輸出為:
AddValue
方法也可用於「DB交叉表」對象。這允許插入不是從附加到對象的數據源導出的數據。如果以這種方式新增任何數據,則還會使用數據源中的數據進行彙總。
# 11. 將外部對像新增至表中
外部對像(如線條,形狀,圖片)可以放在交叉表中。例如,您可能需要以圖形形式顯示某些數據。讓我們看一個使用形狀來顯示基本進度條的示例:
如果單元格值小於13000,則顯示深紅色條,如果小於30000,則顯示黃色;如果大於30000,則顯示綠色。
讓我們從我們的報表開始。將以下SQL產生的數據集匯入報表設計器:
SELECT '李明' AS FName,2018 AS FYear,1 AS FMonth,19300 AS FSalary
UNION
SELECT '李明' AS FName,2018 AS FYear,2 AS FMonth,23300 AS FSalary
UNION
SELECT '李明' AS FName,2018 AS FYear,3 AS FMonth,13800 AS FSalary
UNION
SELECT '李明' AS FName,2019 AS FYear,2 AS FMonth,18430 AS FSalary
UNION
SELECT '李明' AS FName,2019 AS FYear,3 AS FMonth,12330 AS FSalary
UNION
SELECT '李明' AS FName,2020 AS FYear,1 AS FMonth,18300 AS FSalary
UNION
SELECT '劉東' AS FName,2018 AS FYear,1 AS FMonth,19580 AS FSalary
UNION
SELECT '劉東' AS FName,2018 AS FYear,3 AS FMonth,18580 AS FSalary
UNION
SELECT '劉東' AS FName,2019 AS FYear,2 AS FMonth,12300 AS FSalary
UNION
SELECT '劉東' AS FName,2020 AS FYear,3 AS FMonth,9800 AS FSalary
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
在報表頁面上放置一個DB交叉報表對象,並設定如下所示的內容:
關閉此對象的Autosize
屬性並通過滑鼠拖動單元格的豎線以設定列寬:
現在,通過在對像工具欄上選擇矩形對象並將其放在單元格中,將形狀對像新增到表中:
將其Height
和Width
屬性均更改為0.2
並移動使其處於合適的位置。再新增兩個相似的矩形如上圖所示進行排列。
現在建立一個指令碼,顯示正確數量的彩色形狀(取決於單元格的值)。為此,請選擇單元格並雙擊OnBeforePrint
建立事件處理程式:
在事件處理程式中編寫以下程式碼(注意形狀名稱,以便它們與您的對象匹配):
procedure DBCross1Cell0OnBeforePrint(Sender: TfrxComponent);
begin
//去掉邊框的顏色
DBCross1Object1.Frame.Color := clNone;
DBCross1Object2.Frame.Color := clNone;;
DBCross1Object3.Frame.Color := clNone;;
if Value < 13000 then
begin
// 第一個形狀對像
DBCross1Object1.Color := clMaroon; // 深紅色
// 第二個形狀對像
DBCross1Object2.Color := clWhite;
// 第三個形狀對像
DBCross1Object3.Color := clWhite;
end
else if Value < 30000 then
begin
DBCross1Object1.Color := $00CCFF; // 黃色
DBCross1Object2.Color := $00CCFF;
DBCross1Object3.Color := clWhite;
end
else
begin
DBCross1Object1.Color := $00CC98; // 綠色
DBCross1Object2.Color := $00CC98;
DBCross1Object3.Color := $00CC98;
end;
end;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
預覽報表,這與本節頂部顯示的類似。
# 12. 交叉報表設定
讓我們看一下交叉表編輯器中提供的其他一些設定。
前六個選項允許您顯示或隱藏各種表格元素。
自動大小
選項已衆所周知,取消此項的勾選後,允許手動設定表格寬度和高度。
圍繞單元格的邊框
選項允許在單元格周圍繪製框架。
往下列印然後交叉
選項確定如何在多個頁面上列印表格。以下兩個示例顯示了此選項的工作原理(請注意頁面順序):
選項關閉時的列印順序:
選項開啟時的列印順序:
在新頁面上列印頭
選項確定是否在每個新預覽界面上列印表格標題。
如果表格單元格中有兩個或更多值,則使用並排單元格
選項。它確定這些單元格值是並排列印還是一個堆疊在另一個之上(預設值)。
如果它們包含相同的值,則連線相等單元格
選項會水平合併單元格:
在對像檢視器中還有以下屬性可使用:
AddWidth
,AddHeight
:向單元格寬度或高度新增指定的空間量。 FastReport引擎計算單元格大小時會考慮它(必須啟用自動大小
選項)。NextCross
:指向另一個交叉表對象的指針,該對像將列印到此一側。NextCrossGap
:設定兩個相鄰交叉報表對像之間的間隙。