Có ba nhóm thiết lập của worksheet chẳng liên quan gì tới giá trị ô cả, mà liên quan hoàn toàn tới cách tệp hành xử một khi nó rời khỏi code của bạn. Bảo vệ sheet quyết định ô nào người dùng có thể sửa sau khi bạn bàn giao workbook. Page setup cố định hướng trang, khổ giấy, và lề. Các thiết lập in ấn (hàng tiêu đề lặp lại, tỉ lệ co giãn, và ngắt trang thủ công) kiểm soát cách một lưới dữ liệu dài tùy ý xuất hiện trên giấy. Không cái nào trong ba nhóm đó lộ ra khi bạn liếc mắt xem dữ liệu trong một trình xem, và cả ba đều âm thầm gãy ngoài thực địa khi bị sai. HotXLS, một thư viện bảng tính thuần cho Delphi và C++Builder, phơi bày toàn bộ bề mặt đó cho cả .xls lẫn .xlsx, nghĩa là nó cũng tái hiện lại mọi quy tắc phản trực giác của Excel đã ăn sâu vào bề mặt đó
Quy tắc đầu tiên trong số đó khiến gần như ai cũng vấp phải ngay lần đầu bảo vệ một sheet do mình tạo ra. Gọi Protect và đột nhiên chẳng ai gõ được vào bất kỳ ô nào nữa, kể cả những cột nhập liệu mà bạn đã xây cả workbook xoay quanh chúng. Không có dòng code nào của bạn từng đụng tới những cột đó cả, và đó chính xác là lý do vì sao nó xảy ra
Mọi ô sinh ra đã bị khóa sẵn
ECMA-376 định nghĩa locked là một phần của bản ghi định dạng ô, chứ không phải là một thuộc tính của bản thân việc bảo vệ, và giá trị mặc định của nó là true. Bảo vệ sheet chỉ đơn thuần là công tắc khiến cờ đó có hiệu lực. Vì vậy toàn bộ lưới mang theo một cờ khóa ngay từ khoảnh khắc nó tồn tại, ở trạng thái ngủ yên, và lệnh gọi Protect kích hoạt tất cả chúng cùng một lúc. Cách sửa là thiết lập thứ tự một cách có chủ đích: dựng layout trước, mở khóa rõ ràng những vùng người dùng phải sửa, rồi mới bảo vệ sau cùng
Book := TXLSXWorkbook.Create;
try
Sheet := Book.Sheets.Add('Timesheet');
// ... hàng tiêu đề, cột tên, và công thức đơn giá được ghi ở đây ...
Sheet.Range['B2:B50'].SetLocked(False); // giờ làm của nhân viên ở đây
Sheet.Range['F2:F50'].SetFormulaHidden(True); // giữ kín phép tính đơn giá
Sheet.Protect('review-2026'); // giờ các cờ khóa mới phát huy tác dụng
Book.SaveAs('timesheet.xlsx');
finally
Book.Free;
end;
SetFormulaHidden làm một việc tách biệt và dễ bị bỏ sót: trong khi bảo vệ đang bật, ô vẫn hiển thị giá trị đã tính ra, nhưng thanh công thức thì không hiện gì cả. Điều đó quan trọng khi một công thức nhúng đơn giá tính tiền, biên lợi nhuận, hay trọng số chấm điểm mà bạn không muốn trao cho bất kỳ ai click vào một tổng số. Ở facade XLS, cùng ý định đó được thể hiện theo từng range qua IXLSRange.Locked và FormulaHidden. Worksheet ở đó còn mang theo mười lăm cờ Allow* (AllowSort, AllowAutoFilter, AllowFormatCells, và những cờ còn lại), nên một sheet đã bảo vệ vẫn có thể được sắp xếp và lọc chứ không bị đóng băng thành một hiện vật niêm phong
Mật khẩu bảo vệ thực sự bảo vệ điều gì
Cả hai định dạng đều lưu mật khẩu bảo vệ sheet và workbook dưới dạng một hash cũ 4 chữ số hex. Mười sáu bit nghĩa là vô số chuỗi khác nhau sẽ trùng với bất kỳ mật khẩu nào cho trước, và công cụ gỡ bỏ chỉ cách một lần tìm kiếm. Hãy xem việc bảo vệ như một dây an toàn chống sửa nhầm, chứ không phải như kiểm soát truy cập. Đó là công cụ đúng để ngăn reviewer gõ đè lên cột công thức, và là công cụ sai cho bất cứ thứ gì dính tới từ bảo mật
Lên một cấp nữa, ProtectWorkbook trên facade XLSX khóa cấu trúc workbook, ngăn việc thêm, đổi tên, xóa, hay sắp xếp lại thứ tự sheet. Hãy bật nó bất cứ khi nào danh sách sheet tự nó là một hợp đồng với một parser ở phía sau vốn đánh chỉ mục sheet theo tên hoặc theo vị trí. Một sheet bị đổi tên phá hỏng khâu import ở phía bên kia chắc chắn không kém gì một cột bị xóa. Facade XLS phản chiếu lại các tầng bảo vệ đó bằng TXLSWorkbook.Protect ở cấp workbook và các lệnh gọi Protect theo từng sheet, cộng thêm một thuộc tính isProtected cho đoạn code cần kiểm tra một tệp thừa kế trước khi sửa bất cứ thứ gì trong đó
Khi yêu cầu thực sự là bảo mật, cơ chế thay đổi hoàn toàn. SaveAsEncrypted tạo ra một package mã hóa AES theo lược đồ ECMA-376 Standard Encryption, được trình bày chi tiết trong hướng dẫn xuất XLSX được bảo vệ bằng AES, còn facade XLS cũ thì ghi và đọc tệp .xls mã hóa RC4 thông qua EncryptionPassword và overload có mật khẩu của Open. Sự khác biệt này không hề mang tính hàn lâm. Một sheet được bảo vệ vẫn di chuyển ở dạng cleartext, nên bất kỳ công cụ zip nào cũng đọc được giá trị ô của nó, còn một package đã mã hóa thì không thể đọc được nếu thiếu mật khẩu. Một dòng yêu cầu trong audit ghi rằng "tệp bảng lương phải được bảo vệ" gần như lúc nào cũng có nghĩa là mã hóa, bất kể nó dùng từ ngữ gì
Page setup là một phần của hợp đồng tài liệu
Hành vi in ấn vô hình trên màn hình, đó là lý do vì sao nó thường được đưa ra thị trường trong tình trạng hỏng. Ngay khoảnh khắc một khách hàng in workbook, hoặc xuất nó ra PDF cho một kiểm toán viên, lề, tỉ lệ co giãn, và tiêu đề lặp lại biến thành các yêu cầu chức năng mà chẳng ai từng kiểm thử. Trên facade XLSX, các thiết lập này gắn trực tiếp vào worksheet:
Sheet.PageLandscape := True;
Sheet.PaperSize := xlsxPaperA4;
Sheet.SetPageMargins(0.5, 0.5, 0.75, 0.75, 0.3, 0.3);
Sheet.CenterHeader := 'Monthly Timesheet';
Sheet.RightFooter := 'Page &P of &N';
Sheet.PrintArea := '$A$1:$F$60'; // tham chiếu trần: không có tên sheet ở đây
Sheet.PrintTitleRows := '$1:$1'; // hàng tiêu đề lặp lại trên mọi trang
Sheet.FitToWidth := 1;
Sheet.FitToHeight := 0; // tự kéo dài xuống khi dữ liệu tăng
Sheet.PrintGridlines := False;
Hai trong số các dòng đó giấu bẫy. Chuỗi header và footer dùng các mã định dạng của Excel: &P cho trang hiện tại, &N cho tổng số trang, cùng với &L, &C, và &R để chỉ định rõ ba khu vực. Cái bẫy còn lại là PrintArea, vốn cố tình nhận một tham chiếu ô trần trụi. HotXLS lưu nó ở dạng không đủ điều kiện rồi tự thêm tiền tố tên sheet khi ghi tệp, nên nếu bạn tự truyền vào 'Timesheet!$A$1:$F$60' thì kết quả sẽ là một tham chiếu bị gắn tiền tố hai lần, sai định dạng. Sự thận trọng tương tự cũng áp dụng ở một tầng thấp hơn: vùng in và tiêu đề in được lưu trữ dưới dạng các defined name tích hợp sẵn _xlnm.Print_Area và _xlnm.Print_Titles, nên đừng bao giờ tự tay thêm các mục _xlnm.* qua DefinedNames, nếu không hai cơ chế đó sẽ giành nhau cùng một chỗ
Co giãn tỉ lệ chịu được khối lượng dữ liệu thực tế
Cách kết hợp FitToWidth := 1 với FitToHeight := 0 đọc ra nghĩa là "luôn ép các cột vừa trên một trang, rồi cứ kéo dài xuống bao nhiêu trang tùy dữ liệu cần", và đó là giá trị mặc định đúng đắn cho bất kỳ báo cáo nào có số dòng thay đổi. Cái bẫy là việc tinh chỉnh một tỉ lệ phần trăm cố định hay một cặp fit-to-page dựa theo một tệp thử ba mươi dòng: đưa cùng thiết lập đó vào sáu trăm dòng dữ liệu thực tế thì kết quả hoặc là nổ tung thành hàng chục trang bị cắt cụt, hoặc co lại nhỏ tới mức không đọc nổi. Hãy co giãn theo chiều rộng, để chiều dài tự phát triển, và lặp lại hàng tiêu đề qua PrintTitleRows để trang mười bảy vẫn tự nó đọc hiểu được
Ngắt trang thủ công tuân theo cùng kỷ luật tái sinh như mọi thứ khác trong một workbook được tạo tự động. AddRowBreak(BeforeRow) bắt đầu một trang mới trước một ranh giới phần, nhưng khi bộ sinh chạy lại và các dòng dịch chuyển, một ngắt trang cũ sẽ rơi giữa bảng. Hãy gọi ClearAllPageBreaks trước, rồi thêm lại các ngắt trang được tính từ chính bộ đếm dòng của bộ sinh thay vì vá lại các vị trí cũ. Trên facade XLS, các điều khiển tương đương nằm trên Sheet.PageSetup (hướng trang, khổ giấy, lề, chuỗi header và footer, fit-to-pages), với RepeatRows và RepeatColumns phụ trách phần tiêu đề in
Kiểm tra kết quả trước khi khách hàng làm điều đó
Lỗi bảo vệ và lỗi in ấn có chung một đặc điểm: chúng cực kỳ đơn giản để kiểm tra bằng tay, và gần như chẳng bao giờ được kiểm tra. Hãy mở tệp đã tạo ra trong Excel và bỏ ra chín mươi giây cho nó. Gõ vào một ô nhập liệu và xác nhận nó chấp nhận phím gõ; gõ vào một ô đã khóa và xác nhận hộp thoại bảo vệ hiện ra; kiểm tra rằng một công thức bị ẩn khiến thanh công thức trống trơn. Sau đó chạy Print Preview trên một tập dữ liệu có kích thước như thực tế, chứ không phải một mẫu ba mươi dòng, rồi đọc lại số trang, hàng tiêu đề lặp lại, và cách đánh số ở footer. Bước xem trước này là bước tự nó bù đắp xứng đáng, vì hình học in ấn phụ thuộc vào những thiết lập không hề có bất kỳ hiển thị nào trên màn hình, và ngoại trừ một chiếc máy in vật lý thì đây là chỗ duy nhất mà một lỗi co giãn tỉ lệ từng hiện hình
Một thiết lập cuối cùng hoàn thiện phần rà soát. FreezePane(ACol, ARow) giữ khối tiêu đề luôn hiện ra trong khi reviewer cuộn trang. Đó là hành vi trên màn hình chứ không phải hành vi khi in, nhưng một reviewer đánh giá toàn bộ sản phẩm bàn giao cùng một lúc. Và một workbook bắt đầu vòng đời như một layout do designer duy trì thì đã có sẵn phần lớn những điều này miễn phí: quy trình sinh báo cáo từ template giữ page setup nằm ngay trong template, nơi một con người đã tinh chỉnh nó trên một máy in thật, còn code chỉ cần điền dữ liệu và áp lại bảo vệ khi layout đã ổn định
HotXLS là một thư viện bảng tính Object Pascal thuần cho Delphi và C++Builder; tài liệu tham chiếu API đầy đủ về bảo vệ và page setup có trên trang sản phẩm HotXLS Delphi Component