دمج خلايا الجدول
في بعض الأحيان، تتطلب صفوف معينة في الجدول عنوانًا أو كتلًا كبيرة من النص تشغل العرض الكامل للجدول. للحصول على تصميم مناسب للجدول، يمكن للمستخدم دمج عدة خلايا في جدول واحد. يدعم 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-.NET.git. | |
Document doc = new Document(MyDir + "Table with merged cells.docx"); | |
Table table = (Table) doc.GetChild(NodeType.Table, 0, true); | |
foreach (Row row in table.Rows) | |
{ | |
foreach (Cell cell in row.Cells) | |
{ | |
Console.WriteLine(PrintCellMergeType(cell)); | |
} | |
} |
دمج خلايا الجدول عند استخدام DocumentBuilder
لدمج الخلايا في جدول تم إنشاؤه باستخدام DocumentBuilder، تحتاج إلى تعيين نوع الدمج المناسب لكل خلية حيث يتوقع الدمج - أولاً CellMerge.First ثم CellMerge.Previous.
يجب عليك أيضًا أن تتذكر مسح إعداد الدمج لتلك الخلايا التي لا يلزم فيها الدمج - يمكن القيام بذلك عن طريق تعيين أول خلية غير مدمجة على CellMerge.None. إذا لم يتم ذلك، سيتم دمج كافة الخلايا في الجدول.
يوضح مثال التعليمات البرمجية التالي كيفية إنشاء جدول يحتوي على صفين حيث يتم دمج الخلايا الموجودة في الصف الأول أفقيًا:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-.NET.git. | |
Document doc = new Document(); | |
DocumentBuilder builder = new DocumentBuilder(doc); | |
builder.InsertCell(); | |
builder.CellFormat.HorizontalMerge = CellMerge.First; | |
builder.Write("Text in merged cells."); | |
builder.InsertCell(); | |
// This cell is merged to the previous and should be empty. | |
builder.CellFormat.HorizontalMerge = CellMerge.Previous; | |
builder.EndRow(); | |
builder.InsertCell(); | |
builder.CellFormat.HorizontalMerge = CellMerge.None; | |
builder.Write("Text in one cell."); | |
builder.InsertCell(); | |
builder.Write("Text in another cell."); | |
builder.EndRow(); | |
builder.EndTable(); | |
doc.Save(ArtifactsDir + "WorkingWithTables.HorizontalMerge.docx"); |
يوضح مثال التعليمات البرمجية التالي كيفية إنشاء جدول مكون من عمودين حيث يتم دمج الخلايا الموجودة في العمود الأول عموديًا:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-.NET.git. | |
Document doc = new Document(); | |
DocumentBuilder builder = new DocumentBuilder(doc); | |
builder.InsertCell(); | |
builder.CellFormat.VerticalMerge = CellMerge.First; | |
builder.Write("Text in merged cells."); | |
builder.InsertCell(); | |
builder.CellFormat.VerticalMerge = CellMerge.None; | |
builder.Write("Text in one cell"); | |
builder.EndRow(); | |
builder.InsertCell(); | |
// This cell is vertically merged to the cell above and should be empty. | |
builder.CellFormat.VerticalMerge = CellMerge.Previous; | |
builder.InsertCell(); | |
builder.CellFormat.VerticalMerge = CellMerge.None; | |
builder.Write("Text in another cell"); | |
builder.EndRow(); | |
builder.EndTable(); | |
doc.Save(ArtifactsDir + "WorkingWithTables.VerticalMerge.docx"); |
دمج خلايا الجدول في حالات أخرى
في المواقف الأخرى التي لا يتم فيها استخدام DocumentBuilder، كما هو الحال في جدول موجود، قد لا يكون دمج الخلايا بالطريقة السابقة سهلاً. بدلاً من ذلك، يمكننا تغليف العمليات الأساسية المتعلقة بتطبيق خصائص الدمج على الخلايا بطريقة تجعل المهمة أسهل بكثير. تشبه هذه الطريقة طريقة الدمج التلقائي، والتي يتم استدعاؤها لدمج نطاق من الخلايا في جدول.
سيقوم الكود أدناه بدمج خلايا الجدول في النطاق المحدد، بدءًا من الخلية المحددة وانتهاءً بالخلية النهائية. في هذه الحالة، يمكن أن يمتد النطاق لعدة صفوف أو أعمدة:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-.NET.git. | |
internal void MergeCells(Cell startCell, Cell endCell) | |
{ | |
Table parentTable = startCell.ParentRow.ParentTable; | |
// Find the row and cell indices for the start and end cell. | |
Point startCellPos = new Point(startCell.ParentRow.IndexOf(startCell), | |
parentTable.IndexOf(startCell.ParentRow)); | |
Point endCellPos = new Point(endCell.ParentRow.IndexOf(endCell), parentTable.IndexOf(endCell.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. | |
Rectangle mergeRange = new Rectangle(Math.Min(startCellPos.X, endCellPos.X), | |
Math.Min(startCellPos.Y, endCellPos.Y), | |
Math.Abs(endCellPos.X - startCellPos.X) + 1, Math.Abs(endCellPos.Y - startCellPos.Y) + 1); | |
foreach (Row row in parentTable.Rows) | |
{ | |
foreach (Cell cell in row.Cells) | |
{ | |
Point currentPos = new Point(row.IndexOf(cell), parentTable.IndexOf(row)); | |
// Check if the current cell is inside our merge range, then merge it. | |
if (mergeRange.Contains(currentPos)) | |
{ | |
cell.CellFormat.HorizontalMerge = currentPos.X == mergeRange.X ? CellMerge.First : CellMerge.Previous; | |
cell.CellFormat.VerticalMerge = currentPos.Y == mergeRange.Y ? CellMerge.First : CellMerge.Previous; | |
} | |
} | |
} | |
} |
يوضح مثال التعليمات البرمجية التالي كيفية دمج نطاق من الخلايا بين خليتين محددتين:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-.NET.git. | |
Document doc = new Document(MyDir + "Table with merged cells.docx"); | |
Table table = doc.FirstSection.Body.Tables[0]; | |
// We want to merge the range of cells found inbetween these two cells. | |
Cell cellStartRange = table.Rows[0].Cells[0]; | |
Cell cellEndRange = table.Rows[1].Cells[1]; | |
// Merge all the cells between the two specified cells into one. | |
MergeCells(cellStartRange, cellEndRange); | |
doc.Save(ArtifactsDir + "WorkingWithTables.MergeCellRange.docx"); |
اعتمادًا على إصدار .NET Framework الذي تستخدمه، قد ترغب في تحسين هذه الطريقة عن طريق تحويلها إلى طريقة ملحقة. في هذه الحالة، يمكنك استدعاء هذه الطريقة مباشرة على خلية لدمج نطاق من الخلايا، مثل cell1.Merge(cell2)
.
الخلايا المدمجة عموديًا وأفقيًا في جدول HTML
كما قلنا في المقالات السابقة، الجدول في Microsoft Word هو عبارة عن مجموعة من الصفوف المستقلة. يحتوي كل صف على مجموعة من الخلايا المستقلة عن خلايا الصفوف الأخرى. وبالتالي، في جدول Microsoft Word لا يوجد كائن مثل “العمود”، و"العمود الأول" هو شيء مثل “مجموعة الخلايا الأولى من كل صف في الجدول”. يتيح ذلك للمستخدمين الحصول على جدول، على سبيل المثال، يتكون الصف الأول من خليتين - 2 سم و1 سم، والصف الثاني يتكون من خليتين مختلفتين - عرض 1 سم و2 سم. ويدعم Aspose.Words مفهوم الجداول هذا.
يحتوي الجدول في HTML على بنية مختلفة بشكل أساسي: كل صف يحتوي على نفس عدد الخلايا و(هذا مهم للمهمة) كل خلية لها عرض العمود المقابل، وهو نفسه بالنسبة لجميع الخلايا في عمود واحد. لذا، إذا قام HorizontalMerge وVerticalMerge بإرجاع قيمة غير صحيحة، فاستخدم مثال التعليمات البرمجية التالي:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-.NET.git. | |
Document doc = new Document(MyDir + "Table with merged cells.docx"); | |
SpanVisitor visitor = new SpanVisitor(doc); | |
doc.Accept(visitor); |
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-.NET.git. | |
/// <summary> | |
/// Helper class that contains collection of rowinfo for each row. | |
/// </summary> | |
public class TableInfo | |
{ | |
public List<RowInfo> Rows { get; } = new List<RowInfo>(); | |
} | |
/// <summary> | |
/// Helper class that contains collection of cellinfo for each cell. | |
/// </summary> | |
public class RowInfo | |
{ | |
public List<CellInfo> Cells { get; } = new List<CellInfo>(); | |
} | |
/// <summary> | |
/// Helper class that contains info about cell. currently here is only colspan and rowspan. | |
/// </summary> | |
public class CellInfo | |
{ | |
public CellInfo(int colSpan, int rowSpan) | |
{ | |
ColSpan = colSpan; | |
RowSpan = rowSpan; | |
} | |
public int ColSpan { get; } | |
public int RowSpan { get; } | |
} | |
public class SpanVisitor : DocumentVisitor | |
{ | |
/// <summary> | |
/// Creates new SpanVisitor instance. | |
/// </summary> | |
/// <param name="doc"> | |
/// Is document which we should parse. | |
/// </param> | |
public SpanVisitor(Document doc) | |
{ | |
mWordTables = doc.GetChildNodes(NodeType.Table, true); | |
// We will parse HTML to determine the rowspan and colspan of each cell. | |
MemoryStream htmlStream = new MemoryStream(); | |
Aspose.Words.Saving.HtmlSaveOptions options = new Aspose.Words.Saving.HtmlSaveOptions | |
{ | |
ImagesFolder = Path.GetTempPath() | |
}; | |
doc.Save(htmlStream, options); | |
// Load HTML into the XML document. | |
XmlDocument xmlDoc = new XmlDocument(); | |
htmlStream.Position = 0; | |
xmlDoc.Load(htmlStream); | |
// Get collection of tables in the HTML document. | |
XmlNodeList tables = xmlDoc.DocumentElement.GetElementsByTagName("table"); | |
foreach (XmlNode table in tables) | |
{ | |
TableInfo tableInf = new TableInfo(); | |
// Get collection of rows in the table. | |
XmlNodeList rows = table.SelectNodes("tr"); | |
foreach (XmlNode row in rows) | |
{ | |
RowInfo rowInf = new RowInfo(); | |
// Get collection of cells. | |
XmlNodeList cells = row.SelectNodes("td"); | |
foreach (XmlNode cell in cells) | |
{ | |
// Determine row span and colspan of the current cell. | |
XmlAttribute colSpanAttr = cell.Attributes["colspan"]; | |
XmlAttribute rowSpanAttr = cell.Attributes["rowspan"]; | |
int colSpan = colSpanAttr == null ? 0 : int.Parse(colSpanAttr.Value); | |
int rowSpan = rowSpanAttr == null ? 0 : int.Parse(rowSpanAttr.Value); | |
CellInfo cellInf = new CellInfo(colSpan, rowSpan); | |
rowInf.Cells.Add(cellInf); | |
} | |
tableInf.Rows.Add(rowInf); | |
} | |
mTables.Add(tableInf); | |
} | |
} | |
public override VisitorAction VisitCellStart(Cell cell) | |
{ | |
int tabIdx = mWordTables.IndexOf(cell.ParentRow.ParentTable); | |
int rowIdx = cell.ParentRow.ParentTable.IndexOf(cell.ParentRow); | |
int cellIdx = cell.ParentRow.IndexOf(cell); | |
int colSpan = 0; | |
int rowSpan = 0; | |
if (tabIdx < mTables.Count && | |
rowIdx < mTables[tabIdx].Rows.Count && | |
cellIdx < mTables[tabIdx].Rows[rowIdx].Cells.Count) | |
{ | |
colSpan = mTables[tabIdx].Rows[rowIdx].Cells[cellIdx].ColSpan; | |
rowSpan = mTables[tabIdx].Rows[rowIdx].Cells[cellIdx].RowSpan; | |
} | |
Console.WriteLine("{0}.{1}.{2} colspan={3}\t rowspan={4}", tabIdx, rowIdx, cellIdx, colSpan, rowSpan); | |
return VisitorAction.Continue; | |
} | |
private readonly List<TableInfo> mTables = new List<TableInfo>(); | |
private readonly NodeCollection mWordTables; | |
} |
تحويل إلى خلايا مدمجة أفقيا
في بعض الأحيان لا يكون من الممكن اكتشاف الخلايا التي تم دمجها لأن بعض الإصدارات الأحدث من Microsoft Word لم تعد تستخدم علامات الدمج عند دمج الخلايا أفقيًا. ولكن بالنسبة للحالات التي يتم فيها دمج الخلايا في خلية أفقيًا حسب عرضها باستخدام إشارات الدمج، توفر Aspose.Words طريقة ConvertToHorizontallyMergedCells
لتحويل الخلايا. تقوم هذه الطريقة ببساطة بتحويل الجدول وإضافة خلايا جديدة حسب الحاجة.
يوضح مثال التعليمات البرمجية التالي الطريقة المذكورة أعلاه قيد التشغيل:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-.NET.git. | |
Document doc = new Document(MyDir + "Table with merged cells.docx"); | |
Table table = doc.FirstSection.Body.Tables[0]; | |
// Now merged cells have appropriate merge flags. | |
table.ConvertToHorizontallyMergedCells(); |