Bất cứ thứ gì nổi trên lưới trang tính (một biểu đồ, một logo, một con dấu, một hộp chú thích) là một đối tượng vẽ (drawing object), và một đối tượng vẽ được định nghĩa bởi hai thứ: nó là gì, và nó được neo ở đâu. Điểm neo là phần mà mọi người hay làm sai. Một biểu đồ không sống trong một ô; nó nằm trong một hình chữ nhật được ghim vào một khoảng hàng và cột, và dữ liệu nó vẽ là một tập tham chiếu A1 riêng biệt mà điểm neo không hề biết gì về nó. Di chuyển khung và biểu đồ vẫn giữ nguyên. Chèn hàng bên dưới nó và khung trượt xuống theo. Giữ hai hệ tọa độ đó tách biệt rõ ràng chính là phần lớn những gì khiến mã vẽ hoạt động đúng
HotXLS là một thư viện Object Pascal gốc đọc và ghi XLS và XLSX mà không cần tự động hóa Excel, và nó mang theo hai mô hình vẽ riêng biệt vì hai định dạng tệp lưu trữ các bản vẽ khác nhau. Định dạng BIFF8 .xls giữ các biểu đồ trên các trang tính riêng của chúng và các hình dạng nổi trong một luồng OfficeArt gắn vào trang tính. Định dạng OOXML .xlsx có thể nhúng một biểu đồ bên trong lưới, được neo vào một hình chữ nhật ô, cùng với cùng loại hình ảnh và hình dạng nổi. Mô hình đối tượng phản chiếu sự phân chia đó, và các lỗi đáng viết ra đều đến từ việc áp dụng quy tắc của định dạng này cho định dạng kia
Container nào có thể chứa gì
Việc chọn container phải đến trước bất kỳ mã biểu đồ nào, vì các kiểu đối tượng khả dụng khác nhau giữa hai định dạng:
- XLS (BIFF8): các biểu đồ sống trên các trang tính biểu đồ riêng được tạo thông qua
AddChartSheettrên tập hợpSheets. Hình ảnh, hộp văn bản, hình chữ nhật, hình bầu dục, và đường thẳng là các hình dạng OfficeArt được quản lý thông qua tập hợpShapescủa trang tính. Không có API để nhúng một biểu đồ bên trong lưới trang tính thông thường - XLSX (OOXML): các biểu đồ có thể được nhúng trực tiếp vào một trang tính bằng
TXLSXWorksheet.AddChart, được neo vào một hình chữ nhật ô, hoặc đặt trên một trang tính biểu đồ riêng bằngTXLSXWorkbook.AddChartSheet. Hình ảnh được thêm vào bằngAddImagehoặcAddImageFromFile, và nhãn nổi bằngAddTextBox
Vì vậy một yêu cầu được diễn đạt là "một trang tổng quan (dashboard) với biểu đồ cạnh các con số" thực ra là một yêu cầu cho .xlsx. Bạn chỉ có thể xấp xỉ nó trong .xls bằng cách đẩy biểu đồ sang trang riêng của nó, điều này thay đổi cách người dùng điều hướng tệp và thay đổi cách mã của bạn phải hành xử. Trang tính được trả về bởi AddChartSheet phía XLS là một luồng con biểu đồ, không phải một lưới: ghi vào nó bằng Cells.Item tạo ra một luồng vẽ không nhất quán mà tạo ra không có lỗi và Excel sau đó loại bỏ khi mở. Biểu đồ đơn giản là biến mất, và không có gì trong nhật ký build nói tại sao. Coi trang tính được trả về là chỉ-dành-cho-biểu-đồ và toàn bộ nhóm báo cáo "biểu đồ bị mất" sẽ biến mất
Nhúng một biểu đồ vào một trang tính XLSX
Đường XLSX là đường có không gian để xoay xở, và đây là nơi hai hệ tọa độ từ phần mở đầu trở nên cụ thể. Hình chữ nhật neo được truyền cho AddChart được diễn đạt theo hàng và cột trang tính và cố định vị trí khung biểu đồ nằm. Dữ liệu chuỗi được diễn đạt như các tham chiếu A1 tuyệt đối bao gồm tên trang tính. Chúng độc lập với nhau: bạn có thể di chuyển khung sang phía bên kia của trang tính và nó vẫn vẽ cùng các ô đó
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
Chart: TXLSXChart;
begin
Book := TXLSXWorkbook.Create;
try
Sheet := Book.Sheets.Add('Sales');
Sheet.Cells[1, 1].Value := 'Region';
Sheet.Cells[1, 2].Value := 'Revenue';
Sheet.Cells[2, 1].Value := 'East';
Sheet.Cells[2, 2].Value := 1184350;
Sheet.Cells[3, 1].Value := 'Central';
Sheet.Cells[3, 2].Value := 902210;
Sheet.Cells[4, 1].Value := 'West';
Sheet.Cells[4, 2].Value := 1010675;
// Khung được neo vào hàng 6..22, cột 1..8
Chart := Sheet.AddChart(xlsxChartColumn, 'Revenue by Region', 6, 1, 22, 8);
Chart.AddSeries('Revenue', 'Sales!$A$2:$A$4', 'Sales!$B$2:$B$4');
Chart.ValueAxisTitle := 'USD';
Sheet.AddImageFromFile(1, 5, 'logo.png');
Book.SaveAs('dashboard.xlsx');
finally
Book.Free;
end;
end;
Tham số gây khó chịu là chuỗi phạm vi được đưa cho AddSeries. Nó là một chuỗi ký tự, được chụp lại tại thời điểm gọi, và nó không biết gì về việc bạn có thể nối thêm hai mươi hàng dữ liệu nữa sau đó. Hãy xây dựng nó từ một số lượng hàng bạn đã tính toán sau khi dữ liệu được ghi ra, không bao giờ trước đó. Biểu đồ phân tán (scatter) và bong bóng (bubble) nạp chồng cùng hai tham số với các ý nghĩa khác nhau: phạm vi danh mục giờ cung cấp các giá trị X và phạm vi giá trị cung cấp Y, và bán kính bong bóng đến từ một tham chiếu thứ ba được đặt thông qua BubbleSizeRange trên đối tượng TXLSXChartSeries được trả về. Hãy đọc lệnh gọi như "X, Y, kích thước" thay vì "danh mục, giá trị" một khi bạn rời khỏi họ biểu đồ cột-và-thanh
TXLSXChartType bao gồm các biểu đồ cột, thanh, đường, tròn, vùng, doughnut, phân tán, bong bóng, và radar, bao phủ hết kho vũ khí báo cáo hàng ngày. Đối với một biểu đồ toàn trang không có lưới xung quanh, Book.AddChartSheet trả về một trang tính có thuộc tính IsChartSheet là true. Đây là đối tác .xlsx của trang tính biểu đồ kế thừa và mang cùng kỳ vọng: đừng ghi nội dung ô vào nó
Hình ảnh được thêm vào dưới dạng byte, và chúng được đo kích thước theo EMU
Có hai overload để chèn một hình ảnh, và nhầm lẫn giữa chúng là lỗi hình ảnh xuất hiện nhiều nhất trong việc xem xét mã. AddImage(ARow, ACol, AData, AFormat) muốn các byte hình ảnh đã được mã hóa sẵn trong AData: nội dung thô của một PNG, JPEG, GIF, hoặc BMP. Truyền cho nó một đường dẫn tệp và bạn đã lưu một chuỗi bốn mươi byte mà không trình xem nào có thể giải mã, đây chính xác là báo cáo biểu-tượng-ảnh-hỏng mà bạn không muốn phải gỡ lỗi sau khi triển khai. Khi nguồn là một tệp trên đĩa, hãy gọi AddImageFromFile thay vào đó và để thư viện đọc các byte và phân loại định dạng giúp bạn
Rồi đến việc đo kích thước. DrawingML không đo theo pixel; nó đo theo English Metric Units (EMU), trong đó 914400 EMU tạo thành một inch và, ở 96 DPI, 9525 EMU tạo thành một pixel. Đối tượng TXLSXImage phơi bày WidthEMU và HeightEMU, nên một logo dự định hiển thị 180 x 60 pixel cần 1714500 x 571500 EMU. Hãy đặt phép chuyển đổi đó trong một hằng số được đặt tên và tính toán dựa trên nó. Các con số ma thuật như 1714500 rải rác khắp mã lệnh vừa khó đọc vừa âm thầm sai lần đầu tiên ai đó thay đổi DPI mục tiêu. Nhân tiện, hàng và cột neo được đánh số từ 1, khớp với phần còn lại của API ô hơn là phép toán EMU đánh số từ 0
Trang tính biểu đồ và hình dạng trong tệp XLS kế thừa
Ở phía BIFF8, overload AddChartSheet phong phú hơn nhận kiểu biểu đồ, tiêu đề trục, và một mảng mở các bản ghi TXLSChartSeriesInfo, trong đó mỗi bản ghi chứa một tên và một phạm vi danh mục và giá trị dưới dạng chuỗi. Hình dạng nổi là một vấn đề riêng biệt: chúng nằm trên chính trang tính dữ liệu, thông qua tập hợp Shapes của nó, không phải trên trang tính biểu đồ
var
Book: IXLSWorkbook;
Data, Trend: IXLSWorksheet;
Series: array[0..0] of TXLSChartSeriesInfo;
begin
Book := TXLSWorkbook.Create; // đếm tham chiếu qua interface: không được gọi Free
Data := Book.Sheets.Add;
Data.Name := 'Data';
Data.Cells.Item[1, 1].Value := 'Month';
Data.Cells.Item[1, 2].Value := 'Units';
Data.Cells.Item[2, 1].Value := 'Apr';
Data.Cells.Item[2, 2].Value := 1530;
Data.Cells.Item[3, 1].Value := 'May';
Data.Cells.Item[3, 2].Value := 1721;
Series[0].Name := 'Units';
Series[0].Categories := 'Data!$A$2:$A$3';
Series[0].Values := 'Data!$B$2:$B$3';
Trend := Book.Sheets.AddChartSheet('Trend', xlsChartTypeLine,
'Units sold', 'Month', 'Units', Series);
// Trend là một luồng con biểu đồ: không bao giờ gọi phương thức ô trên nó
Data.Shapes.AddTextBox('Source: ERP nightly export', 6, 1, 8, 4);
Data.Shapes.AddPicture('approved-stamp.bmp');
Book.SaveAs('trend.xls');
end;
Hai chi tiết về vòng đời quan trọng ở đây, và chúng kéo theo hai hướng ngược nhau. TXLSWorkbook được giữ thông qua interface IXLSWorkbook và được đếm tham chiếu, nên tự gọi Free trên nó sẽ kích hoạt một lần giải phóng kép. TXLSXWorkbook từ các phần trước là một đối tượng thuần túy và phải được giải phóng trong một khối try..finally. Cùng một người xem xét mã đánh dấu một Free bị thiếu ở phía XLSX phải đánh dấu một Free có mặt ở phía XLS, đây là một cái bẫy thực sự khi bạn làm việc với cả hai định dạng trong cùng một unit. Bản thân các hàm trợ giúp hình dạng thì đồng nhất: AddRectangle, AddOval, và AddLine, với DeleteInRange để xóa một vùng các bản vẽ, tất cả đều neo theo cặp hàng và cột, nên một mẫu chèn hàng phía trên chúng sẽ dịch chuyển chúng cùng với lưới
Một thuộc tính khác xứng đáng với vị trí của nó trên các tệp kế thừa. TXLSPicture.TransparentColor che đi một màu nền được chọn khỏi một bitmap, đây là cách bạn thả một con dấu không phải hình chữ nhật (một dấu "Approved", một watermark) lên trên lưới trong một định dạng mà việc kết xuất BIFF của nó chưa bao giờ học được alpha PNG. Đặt màu mà con dấu được tạo ra dựa trên và hình chữ nhật xung quanh sẽ biến mất
Màu theme không sống sót qua một vòng lặp qua-lại BIFF8
Các phần tô của bản vẽ OOXML có thể trỏ vào một khe màu theme, đây là lý do tại sao việc tô lại màu cho toàn bộ một .xlsx bằng cách hoán đổi theme của nó lại rẻ đến vậy. Các bản ghi vẽ BIFF8 không có khe như vậy. Khi HotXLS áp dụng một màu theme vào một bản vẽ XLS, nó phân giải màu đó thành một giá trị RGB thực và lưu trữ nó; chỉ mục theme mà nó đến từ đó biến mất ngay khi tệp được ghi ra, và việc mở lại không thể khôi phục nó. Điều này đặc biệt bắt được các công cụ báo cáo white-label, loại tái thương hiệu cùng một tài liệu được tạo ra cho nhiều khách hàng. Hãy giữ ánh xạ theme-sang-RGB trong cấu hình riêng của bạn và áp dụng lại nó mỗi lần bạn tạo ra, thay vì mong đợi đọc lại nó từ một tệp .xls đã lưu
Một quyết định liên quan xuất hiện ở khía cạnh hiệu năng. Lớp giao diện XLS có thể được yêu cầu bỏ qua hoàn toàn việc phân tích cú pháp lớp vẽ khi tất cả những gì bạn muốn từ một tệp kế thừa lớn là dữ liệu ô của nó, bằng cách đặt _DisableGraphics thành true, và điều đó cắt giảm thời gian thực đáng kể cho các lần đọc hàng loạt. Cái bẫy là vĩnh viễn: một workbook được mở theo cách đó không có luồng OfficeArt nào trong bộ nhớ, nên việc lưu nó sẽ ghi các bản vẽ ra khỏi sự tồn tại. Hãy dành cờ này cho các tác vụ phân tích chỉ-đọc. Bức tranh hiệu năng rộng hơn nằm trong ghi chú của chúng tôi về hiệu năng workbook lớn trong HotXLS
Giữ các điểm neo ổn định trong khi lưới thay đổi
Các báo cáo hiếm khi giữ nguyên kích thước lúc chúng được tạo ra, và đây là nơi mô hình neo từ phần mở đầu mang lại lợi ích. Các thao tác cấu trúc của lớp giao diện XLSX (InsertRows, DeleteRows, và các phép tương đương cho cột) di chuyển các lớp phụ thuộc cùng với các ô. Các vùng đã hợp nhất, siêu liên kết, chú thích, khung cố định (frozen pane), phạm vi lọc, định dạng có điều kiện, xác thực, bảng, tên đã định nghĩa, và, cho chủ đề này, các điểm neo hình ảnh và biểu đồ, tất cả đều di chuyển cùng nhau. Một logo được neo ở hàng 1 vẫn ở trên cùng khi mười hàng được chèn vào bên dưới nó. Một khung biểu đồ được neo dưới khối dữ liệu trượt xuống khi khối đó lớn lên. Điều duy nhất không được viết lại là bất kỳ chuỗi phạm vi nào bạn đã chụp lại như một chuỗi ký tự trước khi việc chèn xảy ra, vì nó chỉ là văn bản mà thư viện không có lý do gì để xem lại. Điều đó cố định thứ tự an toàn cho việc điền mẫu: ghi và định hình lại dữ liệu trước, rồi tạo biểu đồ và đặt hình ảnh như lượt cuối cùng, với mọi chuỗi phạm vi được suy ra từ số lượng hàng bạn có sau khi chèn, không phải trước đó
Hai công cụ nhỏ hơn hoàn thiện bộ công cụ đặt vị trí. TXLSTextBox.SetArea ở phía XLS neo lại một hộp văn bản hoặc auto shape hiện có vào một hình chữ nhật ô mới, điều này tốt hơn việc xóa và tạo lại nó khi một khối chân trang dịch chuyển. Và overload bitmap của AddPicture nhận một TBitmap sống với một cờ trong suốt tùy chọn, nên bất cứ thứ gì mã VCL của riêng bạn có thể vẽ (một đồng hồ đo, một dải sparkline, một kiểu biểu đồ mà danh sách gốc không cung cấp) đều có thể được đóng dấu thẳng vào trang tính mà không cần ghi một tệp tạm thời trước
Biểu đồ và hình ảnh gần như luôn là lớp hoàn thiện trên một báo cáo đã có cấu trúc sẵn, đây là lý do tại sao công việc nền tảng quyết định liệu chúng có vào đúng vị trí gọn gàng hay không. Việc điền dữ liệu mà một biểu đồ sẽ tham chiếu đến được trình bày trong tạo báo cáo theo hướng mẫu, và giữ lưới ổn định bên dưới các điểm neo của bạn là chủ đề của các ô đã hợp nhất và kiểm soát bố cục. Tài liệu đầy đủ về lớp và phương thức nằm trên trang sản phẩm HotXLS Delphi Component