vsto Range。查找空的单个单元格选择将返回单元格之外的范围

时间:2018-07-25 21:30:17

标签: c# excel com range vsto

在我的VSTO外接程序项目中,如果我选择一个空单元格(例如F11),并在该范围内运行find以获取任何值,则似乎会搜索整个工作表并在期望范围内返回范围A1:E10返回null。下面是我选择的单元格的图像。

enter image description here

我正在通过选择来访问此范围,当我在调试时进行检查时,选择的列为6列,行为11列。当我尝试在此范围内找到任何东西时,我希望得到一个空值,但我却得到了E10的值,这没有任何意义。

这是我的C#代码,第一部分得到选择并遍历 范围区域,如果选择一个区域,则只有1个区域。

即使F11是唯一选择的单元格,结尾处val1和val2的值也都是3197077000。

<add namespace="HtmlHelpers.BeginCollectionItem" />

当我在VBA中执行相同的操作时,按预期在F11上运行find时会得到一个空值

using Microsoft.Office.Interop.Excel;
using System;
using System.Windows.Forms;
using Excel = Microsoft.Office.Interop.Excel;

namespace ExcelAddIn1
{
    public partial class UserControl1 : UserControl
    {
        public UserControl1()
        {
            InitializeComponent();
        }

        private void button1_Click_1(object sender, EventArgs e)
        { 
            Excel.Range selection = Globals.ThisAddIn.Application.Selection;

            if (null != selection)
            {
                var sheet = selection.Worksheet;
                var book = (Excel.Workbook)sheet.Parent;

                var areasCount = selection.Areas.Count;

                for (int i = 1; i <= areasCount; i++)
                {
                    var area = selection.Areas[i];

                    var test = area.Value;
                    var testCount = area.Count;
                    var testRow = area.Row;
                    var testCol = area.Column;

                    Excel.Range lastCellInLastRow = area.Find(
                    "*",
                    Type.Missing,
                    XlFindLookIn.xlFormulas,
                    XlLookAt.xlPart,
                    XlSearchOrder.xlByRows,
                    XlSearchDirection.xlPrevious,
                    false,
                    Type.Missing,
                    Type.Missing);

                    Excel.Range lastCellInLastCol = area.Find(
                        "*",
                        Type.Missing,
                        XlFindLookIn.xlFormulas,
                        XlLookAt.xlPart,
                        XlSearchOrder.xlByColumns,
                        XlSearchDirection.xlPrevious,
                        false,
                        Type.Missing,
                        Type.Missing);

                    var val1 = lastCellInLastCol.Value;
                    var val2 = lastCellInLastRow.Value;

                }
            }
        }
    }
}

1 个答案:

答案 0 :(得分:0)

简短答案:互操作/范围。查找可能不可靠。尽可能少使用Interop。

更长的答案:我在实践中注意到的关于Range.Find的不可靠的事情之一是,它似乎记忆了以前搜索过的内容。这可能是问题,也可能不是问题,但是如果我是您,我不会费心尝试解决问题,因为您可以按以下方式解决您的问题:

  • 将您的整个范围作为二维数组读入内存
  • 将数组元素转换为字符串
  • 使用健壮的C#正则表达式库并嵌套循环以搜索数组

需要做更多的工作,但是值得摆脱不可靠的Interop基础。以下是一些入门方面的小代码:

public string[,] ReadValues(Range range)
{
    return ConvertToStringArray2D(GetArray(range, () => range.Value2));
}

private string[,] ConvertToStringArray2D(Array values)
{
    string[,] retArray = new string[values.GetLength(0), values.GetLength(1)];

    // loop through the 2-D System.Array and populate the 1-D String Array
    for (int row = 1; row <= values.GetLength(0); row++)
    {
        for (int col = 1; col <= values.GetLength(1); col++)
        {
            if (values.GetValue(row, col) == null)
            {
                retArray[row - 1, col - 1] = "";
            }
            else
            {
                retArray[row - 1, col - 1] = values.GetValue(row, col).ToString();
            }
        }
    }

    return retArray;
}

private Array OneBasedSingletonArray2D()
{
    return Array.CreateInstance(typeof(Object), new int[] { 1, 1 }, new int[] { 1, 1 });
}       

private Array GetArray(Range range, Func<object> dataProducer)
{
    if (range.Cells.Count == 0)
    {
        return Array.CreateInstance(typeof(Object), 0, 0);
    }
    if (range.Cells.Count == 1)
    {
        //Creating a 1-based array to fit with Excel's 1-based indexing
        Array retArray = OneBasedSingletonArray2D();
        retArray.SetValue(dataProducer.Invoke(), 1, 1);
        return retArray;
    }
    else
    {
        return (Array)dataProducer.Invoke();
    }
}