Mesclar Células Da Tabela

Às vezes, certas linhas de uma tabela requerem um título ou grandes blocos de texto que ocupam toda a largura da tabela. Para o design adequado da tabela, o usuário pode mesclar várias células da tabela em uma. Aspose.Words suporta células mescladas ao trabalhar com todos os formatos de entrada, incluindo a importação de conteúdo HTML.

Como mesclar células da tabela

Em Aspose.Words, as células mescladas são representadas pelas seguintes propriedades da classe CellFormat:

  • HorizontalMerge que descreve se a célula faz parte de uma fusão horizontal de células
  • VerticalMerge que descreve se a célula faz parte de uma fusão vertical de células

Os valores dessas propriedades determinam o comportamento de mesclagem das células:

Verifique se a célula foi mesclada

Para verificar se uma célula faz parte de uma sequência de células mescladas, basta verificar as propriedades HorizontalMerge e VerticalMerge.

O exemplo de código a seguir mostra como imprimir o tipo de mesclagem de células horizontal e vertical:

// 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;
}
}

Células mescladas em uma tabela

Para mesclar células em uma tabela criada com o DocumentBuilder, você precisa definir o tipo de mesclagem apropriado para cada célula em que a mesclagem é esperada – primeiro CellMerge.First e depois CellMerge.Previous.

Além disso, você deve se lembrar de limpar a configuração de mesclagem para as células em que nenhuma mesclagem é necessária – isso pode ser feito definindo a primeira célula sem mesclagem como CellMerge.None. Se isso não for feito, todas as células da tabela serão mescladas.

O exemplo de código a seguir mostra como criar uma tabela com duas linhas onde as células da primeira linha são mescladas horizontalmente:

// 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");

O exemplo de código a seguir mostra como criar uma tabela de duas colunas onde as células da primeira coluna são mescladas verticalmente:

// 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");

Mesclar células da tabela em outros casos

Em outras situações em que o DocumentBuilder não é usado, como em uma tabela existente, mesclar células da maneira anterior pode não ser tão fácil. Em vez disso, podemos envolver as operações básicas envolvidas na aplicação de propriedades de mesclagem às células em um método que torna a tarefa muito mais fácil. Este método é semelhante ao método de automação de mesclagem, que é chamado para mesclar um intervalo de células em uma tabela.

O código abaixo irá mesclar as células da tabela no intervalo especificado, começando na célula dada e terminando na célula final. Nesse caso, o intervalo pode abranger várias linhas ou colunas:

// 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);
}
}
}
}
view raw merge-cells.h hosted with ❤ by GitHub

O exemplo de código a seguir mostra como mesclar um intervalo de células entre duas células especificadas:

// 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");

Dependendo da versão da estrutura que você está usando, convém refinar esse método transformando-o em um método de extensão. Nesse caso, você pode chamar esse método diretamente em uma célula para mesclar um intervalo de células, como cell1.Merge(cell2).

Células mescladas verticais e horizontais em uma tabela

Como dissemos em artigos anteriores, uma tabela em Microsoft Word é um conjunto de linhas independentes. Cada linha tem um conjunto de células que são independentes das células de outras linhas. Assim, na tabela Microsoft Word não existe um objeto como uma “coluna”, e “1st coluna” é algo como “o conjunto das 1st células de cada linha na tabela”. Isso permite que os usuários tenham uma tabela na qual, por exemplo, a linha 1st consiste em duas células – 2cm e 1cm, e a linha 2nd consiste em duas células diferentes – 1cm e 2cm de largura. E Aspose.Words apoia este conceito de tabelas.

Uma tabela em HTML tem uma estrutura essencialmente diferente: cada linha tem o mesmo número de células e (é importante para a tarefa) cada célula tem a largura da coluna correspondente, a mesma para todas as células de uma coluna. Portanto, se HorizontalMerge e VerticalMerge retornarem um valor incorreto, use o seguinte exemplo de código:

// 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;
};

Converter em células mescladas horizontalmente

Às vezes, não é possível detectar quais células são mescladas porque algumas versões mais recentes de Microsoft Word não usam mais os sinalizadores de mesclagem quando as células são mescladas horizontalmente. Mas para situações em que as células são mescladas em uma célula horizontalmente por sua largura usando sinalizadores de mesclagem, Aspose.Words fornece o método ConvertToHorizontallyMergedCells para converter células. Este método simplesmente transforma a tabela e adiciona novas células conforme necessário.

O exemplo de código a seguir mostra o método acima em operação:

// 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();