HotXLS предоставя TXLSWorkbookViewer, нативен VCL контрол, който визуализира книги във формати XLS, XLSX, XLSM и ODS като интерактивна мрежа за електронни таблици във форма на Delphi или C++Builder, без да инсталира Excel или да го управлява чрез OLE automation. Добрата реализация на такъв контрол изисква решаването на три конкретни задачи: свързване на щракване с мишката върху обединена клетка с правилната логическа клетка, поддържане на съгласувани позиция на превъртане, заглавни ленти и избор на клетка, когато потребителят преглежда лист, много по-голям от видимия прозорец, както и определяне на действието при щракване върху маркер за коментар или клетка с хипервръзка
Повечето екипи за Delphi използват преглед на електронни таблици по причини, които нямат общо с редактирането: одитна станция, която преглежда качени книги, преди да влязат в конвейер, киоск или преглед на отчети, при който Microsoft Office не е част от инсталационния образ, или QA инструмент, който трябва да показва съдържанието на книга без непредсказуемостта на управлението на реален процес на Excel през COM. Обикновена мрежа от текстови полета бързо показва текста в клетките, но файлът на електронна таблица не е обикновена мрежа: клетките се обединяват в блокове, които съществуват само в основния модел, листовете имат фиксирани заглавни ленти и независими хоризонтални и вертикални позиции на превъртане, а отделните клетки съдържат коментари и хипервръзки със собствен модел на взаимодействие. TXLSWorkbookViewer е отговорът на HotXLS на тази празнина, а вътрешният му дизайн е разумен модел за всеки, който създава подобен контрол от нулата
Как прегледът на книга работи без зависимост от Excel
TXLSWorkbookViewer изобщо не използва Excel, а чете собствения анализиран обектен модел на HotXLS, вместо да отваря документа чрез Excel и да го управлява автоматизирано. Свойството Workbook свързва съществуващ TXLSWorkbook за класически XLS файлове, а XlsxWorkbook свързва TXLSXWorkbook за XLSX, XLSM и вариантите на шаблони; всеки от тях може вече да е отворен на друго място в приложението, а прегледът само чете от него. Когато контролът трябва сам да притежава файла, LoadFromFile проверява разширението, насочва XLSX, XLSM, XLTX, XLTM и ODS към модерния механизъм, а всичко останало към класическия, след което освобождава създадената от него книга при изчистване или унищожаване на контрола
var
Viewer: TXLSWorkbookViewer;
Book: TXLSXWorkbook;
begin
Book := TXLSXWorkbook.Create;
if Book.Open('quarterly-report.xlsx') <> 1 then
raise Exception.Create('Could not open workbook');
Viewer := TXLSWorkbookViewer.Create(Self);
Viewer.Parent := Self;
Viewer.Align := alClient;
Viewer.XlsxWorkbook := Book; // the viewer does not take ownership
Viewer.GoToCell(1, 1);
Caption := Viewer.WorksheetName + ': ' + Viewer.SelectedCellText;
end;
Намиране на правилната клетка в обединен диапазон
Определянето на правилната клетка за щракване в TXLSWorkbookViewer е двустепенно търсене и разделянето е важно, защото геометрията на пикселите и семантиката на електронната таблица са наистина различни задачи. Първият етап е чисто геометричен: частният метод CellAtPoint обхожда ширините на колоните и височините на редовете от текущата позиция на превъртане, докато намери лентата, която съдържа щракнатите координати X и Y, без изобщо да знае за обединените клетки. Вторият етап е семантичен: всеки път, който променя избора, независимо дали става дума за щракване с мишката, клавиш със стрелка, Tab или директно извикване на GoToCell, минава през една вътрешна процедура ChangeSelection, която нормализира необработените ред и колона спрямо всяко обединяване и ги насочва към опорната клетка на обединението, преди изборът действително да се промени
Опорната клетка е горната лява клетка на обединения диапазон и е единствената клетка в този блок, която действително съдържа стойност, формат, коментар или хипервръзка в основния модел на книгата; всички останали клетки, покрити визуално от обединението, са празни в самите данни. При класическите XLS книги опорната клетка идва от Cell.MergeArea, IXLSRange, чиито Row и Column сочат към притежаващата клетка; при XLSX и ODS книгите MergedCells.FindAt връща TXLSXMergedRange, който предоставя същата опора чрез Row1 и Col1. Рисуването решава независимо еквивалентен проблем, като разширява правоъгълника на обединената клетка до целия обхват от редове и колони и пропуска клетките вътре в този обхват, така че контурът на избора обхваща целия обединен блок, а не само ъгъла на опорната клетка; записването на обединени оформления, вместо само четенето им, е свързана, но отделна задача, разгледана в придружаващата статия за оформление с обединени клетки за шаблони на отчети
var
Sheet: TXLSXWorksheet;
begin
Sheet := Book.Sheets.Add('Summary');
Sheet.MergeCells(2, 2, 3, 4); // B2:D3
Sheet.Cells[2, 2].Value := 'Region totals';
Viewer.XlsxWorkbook := Book;
Viewer.GoToCell(3, 4); // targets the bottom-right corner of the merge
// SelectedRow is now 2 and SelectedCol is now 2: normalized to the anchor cell
end;
Как се синхронизират превъртането, заглавията и изборът
TXLSWorkbookViewer поддържа съгласувани три отделни състояния: логическата позиция на превъртане в TopRow и LeftCol, нативните ленти за превъртане на Windows, заявени от контрола чрез WS_HSCROLL и WS_VSCROLL в CreateParams, и текущия избор в SelectedRow и SelectedCol. Плъзгането на лента за превъртане или завъртането на колелцето на мишката задейства WM_HSCROLL, WM_VSCROLL или WM_MOUSEWHEEL, които обновяват TopRow или LeftCol и прерисуват контрола; изборът не се премества, което съответства на начина, по който самият Excel разделя преглеждането от избора. След всяка такава промяна UpdateScrollBars записва новата позиция обратно в нативната лента чрез SetScrollInfo, така че плъзгачът никога да не се разминава с това, което мрежата действително показва
Навигацията с клавиатура използва същата синхронизация в обратната посока: когато изборът премине отвъд ръба на видимата мрежа, EnsureSelectionVisible измества TopRow или LeftCol, като натрупва действителните ширини на колоните и височини на редовете, вместо просто да увеличава стойността с едно, тъй като редовете и колоните могат да имат персонализирани размери, след което извиква UpdateScrollBars, за да отрази плъзгачът мястото, до което клавиатурата е преместила изгледа. Лентите със заглавия на редове и букви на колони, оразмерени чрез RowHeaderWidth и ColumnHeaderHeight, са частта от този контрол, която остава фиксирана на екрана, докато TopRow и LeftCol превъртат данните под нея, и това е целият механизъм за фиксиране, който контролът предлага сам: той не е функцията Freeze Panes на Excel и няма вграден начин да се фиксира произволен ред или колона с данни, докато останалата част от листа се превърта. Една граница, която си струва да се тества, преди да пуснете преглед върху файлове, които не контролирате напълно, е че TopRow и LeftCol не се ограничават спрямо реално използвания диапазон на листа, така че плъзгач, издърпан до структурния си предел, може да попадне на ред 1,048,576 или колона 16,384 и да покаже празна мрежа вместо последния ред или колона, съдържащи данни; книгите, достатъчно големи, за да направят това забележимо, обикновено са достатъчно големи и за да изискват внимание към зареждането, разгледано в статията за производителността при големи книги
Свързване на коментари и хипервръзки със събитията на мишката и избора
TXLSWorkbookViewer третира коментарите и хипервръзките като атрибути на текущо избраната клетка, а не като цели при посочване с мишката, затова SelectedCellCommentText, SelectedCellCommentAuthor и SelectedCellHyperlink се обновяват всеки път, когато се задейства OnSelectionChange, независимо дали изборът е преместен с щракване, клавиш със стрелка или извикване на GoToCell. В горния десен ъгъл на клетка с коментар се рисува малък червен триъгълник като визуална подсказка, подобно на флага за коментар в Excel, но маркерът е само визуален; контролът няма вградено изскачащо пояснение при посочване с мишката, така че приложение, което иска изскачащ прозорец при задържане вместо при избор, трябва да изгради този слой самостоятелно. Активирането на хипервръзка работи по същия начин, първо чрез избор: двойното щракване върху клетка извиква ActivateSelectedCell, което прочита SelectedCellHyperlink и, ако тя не е празна, задейства OnHyperlinkClick с адреса на целта и параметър var Handled: Boolean, който обработчикът трябва да зададе
Също толкова важно е какво OnHyperlinkClick не прави: TXLSWorkbookViewer никога сам не извиква ShellExecute и не отваря браузър, независимо дали обработчикът задава Handled на true или го оставя false. Навигацията и всяко решение какво се счита за безопасна цел са изцяло отговорност на хост приложението, което е правилната настройка по подразбиране за компонент, който не знае дали е вграден в доверен вътрешен инструмент, или е преглед на файлове, току-що качени от клиент
procedure TMainForm.ViewerSelectionChange(Sender: TObject; Row, Col: Integer);
begin
if Viewer.SelectedCellCommentText <> '' then
StatusBar.SimpleText := Viewer.SelectedCellCommentAuthor + ': ' +
Viewer.SelectedCellCommentText
else
StatusBar.SimpleText := Viewer.SelectedCellHyperlink;
end;
procedure TMainForm.ViewerHyperlinkClick(Sender: TObject;
const Target: WideString; var Handled: Boolean);
begin
ShellExecute(0, 'open', PWideChar(Target), nil, nil, SW_SHOWNORMAL);
Handled := True;
end;
Обхват на избора и ограничения на навигацията с клавиатура
Изборът в TXLSWorkbookViewer винаги е една логическа клетка, проследявана чрез SelectedRow и SelectedCol; базовият контрол няма избор на правоъгълен диапазон от няколко клетки, така че всяка функция, която трябва да работи върху блок от клетки, трябва да бъде изградена над него, вместо да чете обект на избора. Поддръжката на клавиатурата нарочно е базова: клавишите със стрелки преместват с една клетка, Home връща в началото на реда или, с Ctrl, към клетка A1, Page Up и Page Down прескачат с десет реда, а Tab и Shift+Tab преминават през колоните; няма прескачане с Ctrl+Arrow до края на област с данни и няма избор на разширен диапазон чрез Shift, така че потребителите, идващи директно от Excel, ще забележат разликата при плътен лист
Ограниченията на колоните се налагат в същата точка ChangeSelection, която обработва нормализирането на обединенията, и нарочно се различават според механизма: преглед, свързан с класически TXLSWorkbook, спира на колона 256, структурния предел на формата BIFF8, докато преглед, свързан с TXLSXWorkbook, спазва съвременния предел от 16 384 колони, наследен от Excel 2007 нататък. Редовете са ограничени до 1 048 576 и в двата случая, така че практическата разлика между отварянето на стар XLS файл и XLSX файл в същия преглед е изцяло в това колко надясно мрежата ще ви позволи да стигнете
Нищо от това не е екзотично, когато бъде разделено на търсене по пиксели, нормализиране към опорна клетка и няколко обработчика на съобщения, но съгласуването на трите при реални файлове с реални обединения, коментари и хипервръзки е по-голямата част от работата по подобен компонент. TXLSWorkbookViewer се предоставя като част от стандартния компонент HotXLS Excel за Delphi и C++Builder, заедно с класическите и XLSX обектните модели, от които той визуализира данните