エクセルの名前管理には同じ名前を登録できる

エクセルには複数個のセルに名前を付ける便利な機能がある。参照式を書くときによく使うのだが、困ったことに同じ名前が登録できてしまう。

エクセルのリボンの「数式」→「名前の管理」で登録された名前の一覧を見ることができる。
下図のように「test」という名前を2つ登録してみた。上の名前はシート1内で、下の名前はブック全体で使う?ということらしい。ともかく同じ名前で登録できてしまう。


ここではわざと登録しているけど、エクセルを使っていると、いつの間にか同じ名前が登録されていることが、時々起きる(ような気がする)。

そうすると、VSTOで名前を参照しているときに困ったことになる。VSTOの名前を得るメッソドにはワークブック用の名前を明示的にゲットするオプションが無いので、名前が2重定義されていると、どちらの名前が得られるかわからないのですね。

コードを書くと以下の通り。

まずはusing。

using Excel = Microsoft.Office.Interop.Excel;


次にエクセルを起動して、サンプルのエクセルファイルを開いて、名前を参照する。名前を参照するには、WorkbookオブジェクトのNamesプロパティを使う。

    xlsApp = new Excel.Application();

    Excel.Workbook wb = xlsApp.Workbooks.Open(Application.StartupPath + "\\" + "名前サンプル.xlsm");

    Excel.Name nm = wb.Names.Item("test");

    System.Diagnostics.Debug.WriteLine(string.Format("name = {0}, index={1}", nm.Name, nm.Index));


ここでゲットできた名前は、ワークグループ内の名前を期待してしまうが、マイクロソフトのヘルプを読むと「最初に定義されている方」ということで、ここではシート1用の名前が得られてしまう。デバッグ出力は以下の通り。

name = Sheet1!test , index=1

Namesプロパティの名前の一覧を表示してみると、たしかにヘルプの通りに並んでいる。

name = Sheet1!test, index= 1
name = test, index= 2

つまり、ワークブック用に登録した名前をゲットしたつもりなのに、返ってきたのはワークシート用の名前だったという事態がおきることになる(名前の定義順によっては起きないこともある)。

解決策

仕方ないので、ワークブックの名前を得るメソッドを作ってみる。シート内の名前は「Sheet1!」のような接頭辞がついているので、これで区別できそう。

Linqで書くとこんな感じ。

        /// <summary>
        /// ワークブック内の名前を得る(シート内の名前は無視する)
        /// </summary>
        /// <param name="wb"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        Excel.Name getNameInWorkbook(Excel.Workbook wb, string name)
        {
            var lst= wb.Names.Cast<Excel.Name>().Where(nm => nm.Name == name).ToList();

            if (lst.Count == 1)
            {
                return lst[0];
            }
            return null;
        }

#実際に使うときは、名前が見つからないならメソッド内で例外を起こす方が使いやすいかもしれない。

ということで、VSTOでエクセルの名前を使うときは注意という話でした。

コメント

このブログの人気の投稿

varchar をデータ型 numeric に変換中に、算術オーバーフロー エラーが発生しました。