دمج خلايا الجدول
في بعض الأحيان، تتطلب صفوف معينة في الجدول عنوانا أو كتلا كبيرة من النص تشغل العرض الكامل للجدول. للتصميم المناسب للجدول، يمكن للمستخدم دمج عدة خلايا جدول في خلية واحدة. 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يتكون من خليتين مختلفتين – 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(); |