Об'єднати комірки таблиці
Іноді для певних рядків у таблиці потрібен заголовок або великі блоки тексту, що займають всю ширину таблиці. Для правильного оформлення таблиці користувач може об’єднати кілька осередків таблиці в одну. Aspose.Words підтримує об’єднані комірки при роботі з усіма вхідними форматами, включаючи імпорт вмісту HTML.
Як об’єднати комірки таблиці
В Aspose.Words об’єднані комірки представлені наступними властивостями класу CellFormat:
- HorizontalMerge, який описує, чи є клітина частиною горизонтального злиття комірок
- VerticalMerge, який описує, чи є комірка частиною вертикального об’єднання комірок
Значення цих властивостей визначають поведінку клітин при злитті:
- Перша комірка в послідовності об’єднаних комірок матиме CellMerge.First
- Усі наступні об’єднані комірки матимуть CellMerge.Previous
- Клітинка, яка не була об’єднана, матиме CellMerge.None
Перевірте, чи об’єднана клітина
Щоб перевірити, чи є комірка частиною послідовності об’єднаних комірок, ми просто перевіряємо властивості HorizontalMerge та VerticalMerge.
Наступний приклад коду показує, як надрукувати тип злиття горизонтальних і вертикальних комірок:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C.git. | |
auto doc = MakeObject<Document>(MyDir + u"Table with merged cells.docx"); | |
auto table = System::ExplicitCast<Table>(doc->GetChild(NodeType::Table, 0, true)); | |
for (const auto& row : System::IterateOver<Row>(table->get_Rows())) | |
{ | |
for (const auto& cell : System::IterateOver<Cell>(row->get_Cells())) | |
{ | |
std::cout << PrintCellMergeType(cell) << std::endl; | |
} | |
} |
Об’єднані комірки в таблиці
Щоб об’єднати комірки в таблиці, створеній за допомогою DocumentBuilder, вам потрібно встановити відповідний тип злиття для кожної комірки, в якій очікується злиття – спочатку CellMerge.First, а потім CellMerge.Previous.
Крім того, ви повинні пам’ятати, що для тих комірок, де злиття не потрібно, необхідно скасувати Налаштування злиття – це можна зробити, встановивши для першої комірки, що не підлягає злиттю, значення CellMerge.None. Якщо цього не зробити, всі осередки в таблиці будуть об’єднані.
Наступний приклад коду показує, як створити таблицю з двома рядками, де комірки в першому рядку об’єднуються горизонтально:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C.git. | |
auto doc = MakeObject<Document>(); | |
auto builder = MakeObject<DocumentBuilder>(doc); | |
builder->InsertCell(); | |
builder->get_CellFormat()->set_HorizontalMerge(CellMerge::First); | |
builder->Write(u"Text in merged cells."); | |
builder->InsertCell(); | |
// This cell is merged to the previous and should be empty. | |
builder->get_CellFormat()->set_HorizontalMerge(CellMerge::Previous); | |
builder->EndRow(); | |
builder->InsertCell(); | |
builder->get_CellFormat()->set_HorizontalMerge(CellMerge::None); | |
builder->Write(u"Text in one cell."); | |
builder->InsertCell(); | |
builder->Write(u"Text in another cell."); | |
builder->EndRow(); | |
builder->EndTable(); | |
doc->Save(ArtifactsDir + u"WorkingWithTables.HorizontalMerge.docx"); |
Наступний приклад коду показує, як створити таблицю з двома стовпцями, де комірки в першому стовпці об’єднуються вертикально:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C.git. | |
auto doc = MakeObject<Document>(); | |
auto builder = MakeObject<DocumentBuilder>(doc); | |
builder->InsertCell(); | |
builder->get_CellFormat()->set_VerticalMerge(CellMerge::First); | |
builder->Write(u"Text in merged cells."); | |
builder->InsertCell(); | |
builder->get_CellFormat()->set_VerticalMerge(CellMerge::None); | |
builder->Write(u"Text in one cell"); | |
builder->EndRow(); | |
builder->InsertCell(); | |
// This cell is vertically merged to the cell above and should be empty. | |
builder->get_CellFormat()->set_VerticalMerge(CellMerge::Previous); | |
builder->InsertCell(); | |
builder->get_CellFormat()->set_VerticalMerge(CellMerge::None); | |
builder->Write(u"Text in another cell"); | |
builder->EndRow(); | |
builder->EndTable(); | |
doc->Save(ArtifactsDir + u"WorkingWithTables.VerticalMerge.docx"); |
Об’єднуйте комірки таблиці в інших випадках
В інших ситуаціях, коли DocumentBuilder не використовується, наприклад, в існуючій таблиці, об’єднання комірок попереднім способом може виявитися не таким простим. Натомість ми можемо обернути основні операції, пов’язані із застосуванням властивостей об’єднання до комірок, у метод, який значно спрощує завдання. Цей метод подібний до методу автоматизації злиття, який викликається для об’єднання діапазону комірок у таблиці.
Наведений нижче код об’єднає комірки таблиці у вказаному діапазоні, починаючи з вказаної комірки і закінчуючи кінцевою коміркою. У цьому випадку діапазон може охоплювати кілька рядків або стовпців:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C.git. | |
void MergeCells(SharedPtr<Cell> startCell, SharedPtr<Cell> endCell) | |
{ | |
SharedPtr<Table> parentTable = startCell->get_ParentRow()->get_ParentTable(); | |
// Find the row and cell indices for the start and end cell. | |
System::Drawing::Point startCellPos(startCell->get_ParentRow()->IndexOf(startCell), parentTable->IndexOf(startCell->get_ParentRow())); | |
System::Drawing::Point endCellPos(endCell->get_ParentRow()->IndexOf(endCell), parentTable->IndexOf(endCell->get_ParentRow())); | |
// Create a range of cells to be merged based on these indices. | |
// Inverse each index if the end cell is before the start cell. | |
System::Drawing::Rectangle mergeRange( | |
System::Math::Min(startCellPos.get_X(), endCellPos.get_X()), System::Math::Min(startCellPos.get_Y(), endCellPos.get_Y()), | |
System::Math::Abs(endCellPos.get_X() - startCellPos.get_X()) + 1, System::Math::Abs(endCellPos.get_Y() - startCellPos.get_Y()) + 1); | |
for (const auto& row : System::IterateOver<Row>(parentTable->get_Rows())) | |
{ | |
for (const auto& cell : System::IterateOver<Cell>(row->get_Cells())) | |
{ | |
System::Drawing::Point currentPos(row->IndexOf(cell), parentTable->IndexOf(row)); | |
// Check if the current cell is inside our merge range, then merge it. | |
if (mergeRange.Contains(currentPos)) | |
{ | |
cell->get_CellFormat()->set_HorizontalMerge(currentPos.get_X() == mergeRange.get_X() ? CellMerge::First : CellMerge::Previous); | |
cell->get_CellFormat()->set_VerticalMerge(currentPos.get_Y() == mergeRange.get_Y() ? CellMerge::First : CellMerge::Previous); | |
} | |
} | |
} | |
} |
Наступний приклад коду показує, як об’єднати діапазон комірок між двома вказаними комірками:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C.git. | |
auto doc = MakeObject<Document>(MyDir + u"Table with merged cells.docx"); | |
SharedPtr<Table> table = doc->get_FirstSection()->get_Body()->get_Tables()->idx_get(0); | |
// We want to merge the range of cells found inbetween these two cells. | |
SharedPtr<Cell> cellStartRange = table->get_Rows()->idx_get(0)->get_Cells()->idx_get(0); | |
SharedPtr<Cell> cellEndRange = table->get_Rows()->idx_get(1)->get_Cells()->idx_get(1); | |
// Merge all the cells between the two specified cells into one. | |
MergeCells(cellStartRange, cellEndRange); | |
doc->Save(ArtifactsDir + u"WorkingWithTables.MergeCellRange.docx"); |
Залежно від використовуваної версії платформи, можливо, ви захочете вдосконалити цей метод, перетворивши його на метод розширення. У цьому випадку ви можете викликати цей метод безпосередньо для комірки, щоб об’єднати діапазон комірок, наприклад cell1.Merge(cell2)
.
Об’єднані по вертикалі і горизонталі осередки в таблиці
Як ми вже говорили в попередніх статтях, таблиця в Microsoft Word являє собою набір незалежних рядків. Кожен рядок містить набір комірок, які не залежать від комірок інших рядків. Отже, у таблиці Microsoft Word немає такого об’єкта, як “стовпець”, а “1-й стовпець” - це щось на зразок “набору 1-х комірок кожного рядка таблиці”. Це дозволяє користувачам створювати таблицю, в якій, наприклад, 1–й рядок складається з двох комірок – шириною 2 см і 1 см2, а 2-й рядок складається з двох різних комірок - шириною 1 см і 2 см.і Aspose.Words підтримують цю концепцію таблиць.
Таблиця в HTML має принципово іншу структуру: кожен рядок має однакову кількість комірок і (це важливо для завдання) кожна комірка має ширину відповідного стовпця, однакову для всіх комірок в одному стовпці. Отже, якщо HorizontalMerge та VerticalMerge повертають неправильне значення, використовуйте наступний приклад коду:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C.git. | |
auto doc = MakeObject<Document>(MyDir + u"Table with merged cells.docx"); | |
auto visitor = MakeObject<WorkingWithTables::SpanVisitor>(doc); | |
doc->Accept(visitor); |
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C.git. | |
/// <summary> | |
/// Helper class that contains collection of rowinfo for each row. | |
/// </summary> | |
class TableInfo : public System::Object | |
{ | |
public: | |
SharedPtr<System::Collections::Generic::List<SharedPtr<WorkingWithTables::RowInfo>>> get_Rows() | |
{ | |
return mRows; | |
} | |
TableInfo() : mRows(MakeObject<System::Collections::Generic::List<SharedPtr<WorkingWithTables::RowInfo>>>()) | |
{ | |
} | |
private: | |
SharedPtr<System::Collections::Generic::List<SharedPtr<WorkingWithTables::RowInfo>>> mRows; | |
}; | |
/// <summary> | |
/// Helper class that contains collection of cellinfo for each cell. | |
/// </summary> | |
class RowInfo : public System::Object | |
{ | |
public: | |
SharedPtr<System::Collections::Generic::List<SharedPtr<WorkingWithTables::CellInfo>>> get_Cells() | |
{ | |
return mCells; | |
} | |
RowInfo() : mCells(MakeObject<System::Collections::Generic::List<SharedPtr<WorkingWithTables::CellInfo>>>()) | |
{ | |
} | |
private: | |
SharedPtr<System::Collections::Generic::List<SharedPtr<WorkingWithTables::CellInfo>>> mCells; | |
}; | |
/// <summary> | |
/// Helper class that contains info about cell. currently here is only colspan and rowspan. | |
/// </summary> | |
class CellInfo : public System::Object | |
{ | |
public: | |
int get_ColSpan() | |
{ | |
return pr_ColSpan; | |
} | |
int get_RowSpan() | |
{ | |
return pr_RowSpan; | |
} | |
CellInfo(int colSpan, int rowSpan) : pr_ColSpan(0), pr_RowSpan(0) | |
{ | |
pr_ColSpan = colSpan; | |
pr_RowSpan = rowSpan; | |
} | |
private: | |
int pr_ColSpan; | |
int pr_RowSpan; | |
}; | |
class SpanVisitor : public DocumentVisitor | |
{ | |
public: | |
/// <summary> | |
/// Creates new SpanVisitor instance. | |
/// </summary> | |
/// <param name="doc"> | |
/// Is document which we should parse. | |
/// </param> | |
SpanVisitor(SharedPtr<Document> doc) : mTables(MakeObject<System::Collections::Generic::List<SharedPtr<WorkingWithTables::TableInfo>>>()) | |
{ | |
mWordTables = doc->GetChildNodes(NodeType::Table, true); | |
// We will parse HTML to determine the rowspan and colspan of each cell. | |
auto htmlStream = MakeObject<System::IO::MemoryStream>(); | |
auto options = MakeObject<Aspose::Words::Saving::HtmlSaveOptions>(); | |
options->set_ImagesFolder(System::IO::Path::GetTempPath()); | |
doc->Save(htmlStream, options); | |
// Load HTML into the XML document. | |
auto xmlDoc = MakeObject<System::Xml::XmlDocument>(); | |
htmlStream->set_Position(0); | |
xmlDoc->Load(htmlStream); | |
// Get collection of tables in the HTML document. | |
SharedPtr<System::Xml::XmlNodeList> tables = xmlDoc->get_DocumentElement()->GetElementsByTagName(u"table"); | |
for (const auto& table : System::IterateOver(tables)) | |
{ | |
auto tableInf = MakeObject<WorkingWithTables::TableInfo>(); | |
// Get collection of rows in the table. | |
SharedPtr<System::Xml::XmlNodeList> rows = table->SelectNodes(u"tr"); | |
for (const auto& row : System::IterateOver(rows)) | |
{ | |
auto rowInf = MakeObject<WorkingWithTables::RowInfo>(); | |
// Get collection of cells. | |
SharedPtr<System::Xml::XmlNodeList> cells = row->SelectNodes(u"td"); | |
for (const auto& cell : System::IterateOver(cells)) | |
{ | |
// Determine row span and colspan of the current cell. | |
SharedPtr<System::Xml::XmlAttribute> colSpanAttr = cell->get_Attributes()->idx_get(u"colspan"); | |
SharedPtr<System::Xml::XmlAttribute> rowSpanAttr = cell->get_Attributes()->idx_get(u"rowspan"); | |
int colSpan = colSpanAttr == nullptr ? 0 : System::Int32::Parse(colSpanAttr->get_Value()); | |
int rowSpan = rowSpanAttr == nullptr ? 0 : System::Int32::Parse(rowSpanAttr->get_Value()); | |
auto cellInf = MakeObject<WorkingWithTables::CellInfo>(colSpan, rowSpan); | |
rowInf->get_Cells()->Add(cellInf); | |
} | |
tableInf->get_Rows()->Add(rowInf); | |
} | |
mTables->Add(tableInf); | |
} | |
} | |
VisitorAction VisitCellStart(SharedPtr<Cell> cell) override | |
{ | |
int tabIdx = mWordTables->IndexOf(cell->get_ParentRow()->get_ParentTable()); | |
int rowIdx = cell->get_ParentRow()->get_ParentTable()->IndexOf(cell->get_ParentRow()); | |
int cellIdx = cell->get_ParentRow()->IndexOf(cell); | |
int colSpan = 0; | |
int rowSpan = 0; | |
if (tabIdx < mTables->get_Count() && rowIdx < mTables->idx_get(tabIdx)->get_Rows()->get_Count() && | |
cellIdx < mTables->idx_get(tabIdx)->get_Rows()->idx_get(rowIdx)->get_Cells()->get_Count()) | |
{ | |
colSpan = mTables->idx_get(tabIdx)->get_Rows()->idx_get(rowIdx)->get_Cells()->idx_get(cellIdx)->get_ColSpan(); | |
rowSpan = mTables->idx_get(tabIdx)->get_Rows()->idx_get(rowIdx)->get_Cells()->idx_get(cellIdx)->get_RowSpan(); | |
} | |
std::cout << tabIdx << "." << rowIdx << "." << cellIdx << " colspan=" << colSpan << "\t rowspan=" << rowSpan << std::endl; | |
return VisitorAction::Continue; | |
} | |
private: | |
SharedPtr<System::Collections::Generic::List<SharedPtr<WorkingWithTables::TableInfo>>> mTables; | |
SharedPtr<NodeCollection> mWordTables; | |
}; |
Перетворити в горизонтально об’єднані комірки
Іноді неможливо визначити, які комірки об’єднані, оскільки деякі новіші версії Microsoft Word більше не використовують прапори об’єднання при горизонтальному об’єднанні комірок. Але для ситуацій, коли комірки об’єднуються в комірку по горизонталі по ширині за допомогою прапорів об’єднання, Aspose.Words надає метод перетворення комірок ConvertToHorizontallyMergedCells
. Цей метод просто перетворює таблицю та додає нові комірки за потребою.
Наступний приклад коду показує описаний вище метод у дії:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-C.git. | |
auto doc = MakeObject<Document>(MyDir + u"Table with merged cells.docx"); | |
SharedPtr<Table> table = doc->get_FirstSection()->get_Body()->get_Tables()->idx_get(0); | |
// Now merged cells have appropriate merge flags. | |
table->ConvertToHorizontallyMergedCells(); |