Word Interop: How to obtain valid selected cells collection from Word table when selection area is not a rectangle?

StackOverflow https://stackoverflow.com/questions/22666219

Question

I'm trying to get selected cells from Word table. I need collection of selected table cells to create custom bookmark object with information what cell range has been selected by the user. Everything works fine when user selects rectangle area of cells. This's how I'm getting selected table cells in my Word Addin:

Globals.ThisAddIn.Application.Selection.Range.Application.ActiveWindow.Selection.Cells

If users selects area which is not rectangle or selects more than one area (while holding CTRL down), this property returns invalid collections of cells. I've created a simple vba code for testing what cells are selected in table using two properties:

Globals.ThisAddIn.Application.Selection.Range.Application.ActiveWindow.Selection.Cells
Globals.ThisAddIn.Application.Selection.Range.Cells

Both of those properties returns different and invalid selected cells collection when user selects non rectangle table cells area. This is vba code I'm using for testing purpose, when there's cells range selected in Word table:

Sub test()

Dim listaKomorek1 As New Collection
Dim listaKomorek2 As New Collection
Dim indekser1 As Integer
Dim indekser2 As Integer
Dim tekst1 As String
Dim tekst2 As String
Dim tabela As Table

Set tabela = Selection.Range.Tables(1)

indekser1 = 1
indekser2 = 1
liczbaKomorek1 = Selection.Range.Cells.count()
liczbaKomorek2 = Selection.Range.Application.ActiveWindow.Selection.Cells.count()
tekst1 = "Lista komorek 1 (Selection.Range.Cells): " & vbCrLf & vbCrLf & vbCrLf
tekst2 = "Lista komorek 2 (ActiveWindow.Selection.Cells): " & vbCrLf & vbCrLf & vbCrLf

For Each komorka In Selection.Range.Cells
    tekst1 = tekst1 & "#Cell: " & CStr(indekser1) & " Value: " & CStr(komorka.Range.Text) & " Column: " & CStr(komorka.ColumnIndex) & " Row: " & CStr(komorka.RowIndex) & vbCrLf
    indekser1 = indekser1 + 1
Next komorka

For Each komorka In Selection.Range.Application.ActiveWindow.Selection.Cells
    tekst2 = tekst2 & "#Cell: " & CStr(indekser2) & " Value: " & CStr(komorka.Range.Text) & " Column: " & CStr(komorka.ColumnIndex) & " Row: " & CStr(komorka.RowIndex) & vbCrLf
    indekser2 = indekser2 + 1
Next komorka

tekst1 = tekst1 & vbCrLf & "Cells count: " & CStr(liczbaKomorek1)
tekst2 = tekst2 & vbCrLf & "Cells count: " & CStr(liczbaKomorek2)
MsgBox tekst1
MsgBox tekst2
End Sub

My question's, how I can obtain valid selected cells collection when user selects area in Word table which is not rectangle?

Was it helpful?

Solution

I managed to find a solution thanks to article posted by Tim Williams. This solution is by no means perfect but it delivers and I didn't managed to think of something better. In order to get a valid collection of selected cells, when there's contiguous selection in table. we can change one of the properties that are supported with contigous selection. After that we can easily find cells with changed properties which will be all selected cells in table. Here's method I created, I'm using Font.Size to find selected cells:

public static List<System.Drawing.Point> GetSelectedCells()
    {
        //We will use this to hold column and row coordinates for cells
        List<System.Drawing.Point> value = new List<System.Drawing.Point>();
        try
        {
            Microsoft.Office.Interop.Word.Range range = Globals.ThisAddIn.Application.Selection.Range; 
            if (range.Tables.Count > 0)
            {
                Table tempTable = range.Tables[1];
                float[,] backupTable = new float[range.Tables[1].Rows.Count + 1, range.Tables[1].Columns.Count + 1];

                for (int i = 1; i <= tempTable.Rows.Count; i++)
                {
                    for (int j = 1; j <= tempTable.Rows[i].Cells.Count; j++)
                        backupTable[i, j] = tempTable.Rows[i].Cells[j].Range.Font.Size;
                }

                Globals.ThisAddIn.Application.Selection.Font.Size = 1;

                foreach (Row row in range.Tables[1].Rows)
                {
                    foreach (Cell cell in row.Cells)
                    {
                        if (cell.Range.Font.Size == 1)
                        {
                            System.Drawing.Point point = new System.Drawing.Point();
                            point.X = cell.RowIndex;
                            point.Y = cell.ColumnIndex;
                            value.Add(point);
                        }
                    }
                }

                for (int i = 1; i <= tempTable.Rows.Count; i++)
                {
                    for (int j = 1; j <= tempTable.Rows[i].Cells.Count; j++)
                        tempTable.Rows[i].Cells[j].Range.Font.Size = backupTable[i, j];
                }
                Marshal.ReleaseComObject(tempTable);
            }
        }
        catch(Exception)
        {
           //This exception can be used to handle Exception that occurs when there are merged cells in the     table.
        }
        return value;
    }

This won't work with merged cells.

OTHER TIPS

Just use the RowIndex and ColumnIndex-Properties of the selected Range:

int selectedRowIndex = Globals.ThisAddIn.Application.Selection.Range.Cells[1].RowIndex;
int selectedColumnIndex = Globals.ThisAddIn.Application.Selection.Range.Cells[1].ColumnIndex;

//to get the index of the selected Table, you could use thie titel-property, if you set it before
string selectedTableTitle = Globals.ThisAddIn.Application.Selection.Tables[1].Title;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top