none
C#で、Excelファイル内のチェックボックスの値を取得したい RRS feed

  • 質問

  • この度はお世話になります。
    C#からExcelのチェックボックスの値を参照する方法をご教示頂けないでしょうか。

    オブジェクトは「ワークシート.CheckBoxes」や「ワークシート.Shapes」で取得する事が出来、
    ループすれば一つ一つを参照する事は分かったのですが、
    オブジェクトのValue値の取得方法がわかりません。

    コードは以下のように書きました。

    using Excel = Microsoft.Office.Interop.Excel;
    ===================中略======================

    protected  xlsReader( string inFilename )
    {

        Excel.Application oXls; // Excelオブジェクト
        Excel.Workbook oWBook;  // workbookオブジェクト        
        Excel.Worksheet oSheet;

        oXls = new Excel.Application();    //シートは1つしかない前提です

        oWBook = (Excel.Workbook)( oXls.Workbooks.Open( inFilename ) );
        oSheet = oWBook.ActiveSheet;


        for ( int i = 1 ; i <= oSheet.CheckBoxes().Count;i++ )
        {
         // ここでチェックボックスがONの時だけ処理をしたい

        }

    }

    ※ちなみに、「oSheet.CheckBoxes().Count」で正常にチェックボックスの数を取得出来ています。

    皆様よろしくお願い致します。

    2012年7月24日 11:59

回答

  • ちょっと古い資料ですが以下によると、「-4146」はCheckBoxのValueがxlOffを示すようですね。xlOnなら「1」が返ってくるようです。実際、私の方でテストコードを書いてみましたが、その通りになりました。VS2010(.NET Framework 4) + Excel 2010 の環境で、問題なく動いているようです。

    [XL95] 組込み定数一覧 (3/3)
    http://support.microsoft.com/kb/407883/ja

    #(追記)CheckBoxはActiveX版ではなくフォームコントロール版を使っています。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/


    2012年7月25日 2:21
    モデレータ
  • Fuda1です。 私も良く使うので、整理して見ました。

    using System;
    using System.Windows.Forms;
    using Excel = Microsoft.Office.Interop.Excel;
    namespace ExcelReadTest01
    {
        public partial class Form1 : Form
        {
            Excel.Workbook oWBook;  // workbookオブジェクト         
            Excel.Worksheet oSheet;
            public Form1()
            {
                InitializeComponent();
            }
            private void Form1_Load(object sender, EventArgs e)
            {
                Excel.Application oXls;         // Excelオブジェクト
                oXls = new Excel.Application(); //シートは1つしかない前提です
                oXls.Visible = true;
                oWBook = (Excel.Workbook)(oXls.Workbooks.Open(
                @"C:\work\Book1.xlsm",  // オープンするExcelファイル名
                Type.Missing, // (省略可能)UpdateLinks (0 / 1 / 2 / 3)
                Type.Missing, // (省略可能)ReadOnly (True / False )
                Type.Missing, // (省略可能)Format
                    // 1:タブ / 2:カンマ (,) / 3:スペース / 4:セミコロン (;)
                    // 5:なし / 6:引数 Delimiterで指定された文字
                Type.Missing, // (省略可能)Password
                Type.Missing, // (省略可能)WriteResPassword
                Type.Missing, // (省略可能)IgnoreReadOnlyRecommended
                Type.Missing, // (省略可能)Origin
                Type.Missing, // (省略可能)Delimiter
                Type.Missing, // (省略可能)Editable
                Type.Missing, // (省略可能)Notify
                Type.Missing, // (省略可能)Converter
                Type.Missing, // (省略可能)AddToMru
                Type.Missing, // (省略可能)Local
                Type.Missing  // (省略可能)CorruptLoad
              ));
            }
            private void button1_Click(object sender, EventArgs e)
            {
                oSheet = (Excel.Worksheet)oWBook.ActiveSheet;
                //現在まだ使えないようです。Microsoft.Office.Interop.Excel 12.0
                //foreach (Excel.CheckBox ChBox in oSheet.CheckBoxes )
                //{
                //    if (ChBox.LinkedCell == "")
                //    {
                //         Debug.WriteLine( ChBox.Name + " " + ChBox.Value );
                //    }
                //}
                // AxtiveXで張付けたチェックボックスは、0x800A03ECで読めないが
                // フォームコントロールで張付けたチェックボックスは正常に読める  
                Excel.CheckBox ChBox1 = (Excel.CheckBox)oSheet.CheckBoxes(1);
                Excel.CheckBox ChBox2 = (Excel.CheckBox)oSheet.CheckBoxes(2);
                Excel.CheckBox ChBox3 = (Excel.CheckBox)oSheet.CheckBoxes(3);
                MessageBox.Show(ChBox1.Name + "=" + ChBox1.Value.ToString() +
                          " " + ChBox2.Name + "=" + ChBox2.Value.ToString() +
                          " " + ChBox3.Name + "=" + ChBox3.Value.ToString(), "Test");
            }
        }
    }

    フォームコントロールでチェックボックスをシート張付ければC#から使える事が分かりました。勉強になりました。foreach文は、まだ使えないようです。

    Excelファイルにアクセスするには?[C#、VB]http://www.atmarkit.co.jp/fdotnet/dotnettips/717excelfile/excelfile.html

    • 回答としてマーク yurubon 2012年7月25日 9:23
    2012年7月25日 6:20

すべての返信

  • Fuda1です。

    そのまま、チェックボックスのオンオフを取るのは難しく、エクセルのセルの中に、オンはTrue、オフはFalseを出力するようにExcel側で設定します。フォームコントロールのチェックボックスをシートに貼り付け、コントロールの書式設定で、「リンクするセル」を指定します。これで、このセルにチェックボックスの選択状態が入ります。C#側は、セルの値を読むだけです。

    Excel(エクセル)基本講座:フォーム コントロール
    http://www.eurus.dti.ne.jp/yoneyama/Excel/form.html

    2012年7月24日 13:20
  • >Fuda1様

    ご回答有難うございます。

    なるほど。やはり直接は難しいですか・・・。

    チェックボックスをユーザがコピーペーストなどで追加出来るようにしたかったのですが

    リンクセルを使うとなると、コピーペーストしないようにしないといけないですね・・・。

    2012年7月25日 0:29
  • 試していませんが、以下を見る限りValue値は取得できそうなのですが、どのようにできないのでしょうか? さすがに取得できない仕様のはずはないと思うのですが・・・

    CheckBox.Value プロパティ
    http://msdn.microsoft.com/ja-jp/library/microsoft.office.interop.excel.checkbox.value(v=office.11).aspx


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2012年7月25日 0:38
    モデレータ
  • >trapemiya様

    ご回答有難うございます。
    そうなんですよね。「Value値を取れない」というのが
    とても不思議ではあったのですが、かなり検索してみても
    解決に至る情報を得られなかったもので諦めかけていました・・・。


    質問文ソース内
    「// ここでチェックボックスがONの時だけ処理をしたい」

    の部分で

    Excel.CheckBox chk = oSheet.CheckBoxes( i );

    とし、

    chk.Valueを取得しようとするのですが、

    chk.Valueの値が「-4146」となります。
    チェックがONでもOFFでも値は変わりません・・・。


    • 編集済み yurubon 2012年7月25日 1:18
    2012年7月25日 1:17
  • ちょっと古い資料ですが以下によると、「-4146」はCheckBoxのValueがxlOffを示すようですね。xlOnなら「1」が返ってくるようです。実際、私の方でテストコードを書いてみましたが、その通りになりました。VS2010(.NET Framework 4) + Excel 2010 の環境で、問題なく動いているようです。

    [XL95] 組込み定数一覧 (3/3)
    http://support.microsoft.com/kb/407883/ja

    #(追記)CheckBoxはActiveX版ではなくフォームコントロール版を使っています。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/


    2012年7月25日 2:21
    モデレータ
  • Fuda1です。

    AxtiveXコントロールのチェックボックスであれば、直接読めると思います。Excelで、「開発→挿入→AxtiveXコントロール→チェックボックス」シートに貼付けです。(Excel2007の場合)

    Private Sub CheckBox1_Click()
     MsgBox CheckBox1.Value
    End Sub

    VBAマクロで確認できますので、C#でも可能と思います。


    2012年7月25日 2:28
  • trapemiya様、Fuda1様、ご回答有難うございました。

    >trapemiya様
    なるほど「-4146」はOFFの値だったのですね。
    サポートページのご紹介も有難うございました。

    「ON/OFFのどちらでも「-4146」が返ってくる」と書きましたが、
    選択切替後、「上書き保存」ボタンをきちんと押せていなかったようです。
    (自分では保存したつもりだったのですが・・・)

    チェックONで保存したら、ちゃんとValue値に「1」が返ってきました。
    お騒がせして大変申し訳ありません。

    >Fuda1様
    フォームコントロールとActiveXコントロールで挙動に違いがあったのですね。
    確かに今回私が配置していたのはフォームコントロールでした。

    私はC#からの参照方法がわからず、断念してしまいましたが
    きちんと参照出来れば普通にValueで値が取れるのかもしれませんね。

     


    ・・・両方試してみてからどちらを使うか決めようと思ったのですが、
    ActiveXコントロールの方は「シート.CheckBoxes」で参照する事が出来ず、
    「シート.Shapes」でしか参照する事が出来ませんでした。
    色々試してみたのですがShapesからチェックボックスにキャストさせることが出来ず、
    Value値をとれなかったので、今回はフォームコントロールのまま実装しようと思います。

    お二人とも本当に有難うございました。

    2012年7月25日 4:51
  • Fuda1です。 私も良く使うので、整理して見ました。

    using System;
    using System.Windows.Forms;
    using Excel = Microsoft.Office.Interop.Excel;
    namespace ExcelReadTest01
    {
        public partial class Form1 : Form
        {
            Excel.Workbook oWBook;  // workbookオブジェクト         
            Excel.Worksheet oSheet;
            public Form1()
            {
                InitializeComponent();
            }
            private void Form1_Load(object sender, EventArgs e)
            {
                Excel.Application oXls;         // Excelオブジェクト
                oXls = new Excel.Application(); //シートは1つしかない前提です
                oXls.Visible = true;
                oWBook = (Excel.Workbook)(oXls.Workbooks.Open(
                @"C:\work\Book1.xlsm",  // オープンするExcelファイル名
                Type.Missing, // (省略可能)UpdateLinks (0 / 1 / 2 / 3)
                Type.Missing, // (省略可能)ReadOnly (True / False )
                Type.Missing, // (省略可能)Format
                    // 1:タブ / 2:カンマ (,) / 3:スペース / 4:セミコロン (;)
                    // 5:なし / 6:引数 Delimiterで指定された文字
                Type.Missing, // (省略可能)Password
                Type.Missing, // (省略可能)WriteResPassword
                Type.Missing, // (省略可能)IgnoreReadOnlyRecommended
                Type.Missing, // (省略可能)Origin
                Type.Missing, // (省略可能)Delimiter
                Type.Missing, // (省略可能)Editable
                Type.Missing, // (省略可能)Notify
                Type.Missing, // (省略可能)Converter
                Type.Missing, // (省略可能)AddToMru
                Type.Missing, // (省略可能)Local
                Type.Missing  // (省略可能)CorruptLoad
              ));
            }
            private void button1_Click(object sender, EventArgs e)
            {
                oSheet = (Excel.Worksheet)oWBook.ActiveSheet;
                //現在まだ使えないようです。Microsoft.Office.Interop.Excel 12.0
                //foreach (Excel.CheckBox ChBox in oSheet.CheckBoxes )
                //{
                //    if (ChBox.LinkedCell == "")
                //    {
                //         Debug.WriteLine( ChBox.Name + " " + ChBox.Value );
                //    }
                //}
                // AxtiveXで張付けたチェックボックスは、0x800A03ECで読めないが
                // フォームコントロールで張付けたチェックボックスは正常に読める  
                Excel.CheckBox ChBox1 = (Excel.CheckBox)oSheet.CheckBoxes(1);
                Excel.CheckBox ChBox2 = (Excel.CheckBox)oSheet.CheckBoxes(2);
                Excel.CheckBox ChBox3 = (Excel.CheckBox)oSheet.CheckBoxes(3);
                MessageBox.Show(ChBox1.Name + "=" + ChBox1.Value.ToString() +
                          " " + ChBox2.Name + "=" + ChBox2.Value.ToString() +
                          " " + ChBox3.Name + "=" + ChBox3.Value.ToString(), "Test");
            }
        }
    }

    フォームコントロールでチェックボックスをシート張付ければC#から使える事が分かりました。勉強になりました。foreach文は、まだ使えないようです。

    Excelファイルにアクセスするには?[C#、VB]http://www.atmarkit.co.jp/fdotnet/dotnettips/717excelfile/excelfile.html

    • 回答としてマーク yurubon 2012年7月25日 9:23
    2012年7月25日 6:20
  • >Fuda1様

    詳細にまとめて頂き有難うございました。
    あまり理解できていないままだったので漠然とした結論でしたが
    こうやってまとめて下さって、私の中でも整理できました。

    ※「CheckBoxes」ってメソッドだったんですね。
    プロパティかと思ってました^^;

    本当に有難うございました。

    2012年7月25日 9:33
  • 少し、補足しておきます。
    C# 4.0であれば、

    oWBook = (Excel.Workbook)( oXls.Workbooks.Open( inFilename ) );

    は、

    oWBook = oXls.Workbooks.Open( inFilename );

    で、かまいません。以下を読まれておくと良いでしょう。

    C# 4.0 の dynamic キーワードと COM
    http://msdn.microsoft.com/ja-jp/magazine/ff714583.aspx

    また、System.Runtime.InteropServices.Marshal.ReleaseComObjectを使用してCOMオブジェクトの参照カウントを減らさないと、Excelのプロセスが残ったままになります。

    (参考)
    vb.netで起動した、EXCELのプロセスが残る。
    http://social.msdn.microsoft.com/Forums/ja/vbgeneralja/thread/d2d47134-93d2-4b88-ba6d-18dfecb65563


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2012年7月26日 1:03
    モデレータ
  • System.Runtime.InteropServices.Marshal.ReleaseComObjectを使用してCOMオブジェクトの参照カウントを減らさないと、Excelのプロセスが残ったままになります

    今回作成したサンプルですと、Excelプロセスは、Excelを手動で終了し、自作プログラムを終了する事で、消滅します。『vb.netで起動した、Excelのプロセスが残る』では、ReleaseComObjectを使用しないと駄目なようです。私も、Excelプロセスが残るのは、大変困る事ですから無くしたいと思います。しかし、全て考慮してプログラムを作成すると、C#では負担が半端では無いと思われます。ガベージコレクションが動作しない条件が理解できれば、部分回避策でも安全と思います。また、Excelプロセスが残るプログラムパターンは明らかになっているのでしょうか?

    2012年7月26日 13:29
  • 「ガベージコレクションの不具合と考えてよい」、「負担が半端ではない」と主張されるのは結構ですが、現実問題として .NET で扱う上では必要なことになっています。
    今の時点でその苦労を負いたくないというのであれば、別のフレームワーク、言語で書くことが必要になります。
    (将来どうなるかはわかりませんが、直近で変わることはないと思います)

    以下に Office 開発系のサポートの方が書いている記事がありますのでリンクしておきます。

    Office オートメーションで割り当てたオブジェクトを解放する - Part1
    Office オートメーションで割り当てたオブジェクトを解放する – Part2

    2012年7月26日 13:41
    モデレータ
  • Fuda1です。 「Part1」「Part2」どうも有難うございます。率直に、驚きました。

                app.Quit();
                 // オブジェクトを破棄します。
                Marshal.ReleaseComObject(worksheet);
                worksheet = null;
                Marshal.ReleaseComObject(workbook);
                workbook = null;
                Marshal.ReleaseComObject(app);
                app = null;
                // ガベージコレクトを強制します。
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
    メモリ管理と思えば何とか出来そうです。イベントでの開放も忘れずに、パソコンが風邪になったのかなと思うときもありましたが、COMの開放不良ですとそんな風に見えるかも知れません。
    2012年7月26日 15:23
  • Fuda1です。 サンプルに、MarshalReleaseComObject処理を追加しました。

            //フォームクローズ前に呼ばれる
            private void Form1_FormClosing(object sender, FormClosingEventArgs e)
            {
                // Excelを閉じる
                oXls.Quit();
                // オブジェクトを破棄します。
                Marshal.ReleaseComObject(oSheet);
                oSheet = null;
                Marshal.ReleaseComObject(oWBook);
                oWBook = null;
                Marshal.ReleaseComObject(oXls);
                oXls = null;
                // ガベージコレクトを強制します。
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
            }

    チェックボックスの呼び出しテスト後に、フォームをクローズすると、自動的にExcelを終了します。Windowsタスクマネージャでは、Excelプロセスは終了している様子です。

    // Visual Studio 2008  C#3.0用 です。
    2012年7月26日 16:14
  • 皆様、とても有益な情報を有難うございます。

    正直、Quitして、
    各オブジェクトにnullを代入すればOKだと思っていました。
    ReleaseComObjectというのが必要なのですね。

    ご紹介頂いたページも読みました。
    書いてることの全てを理解できたわけではないですが、
    Officeの処理をする際には、オブジェクトの開放について
    特に注意が必要だという事はわかりました。

    ご紹介頂いた方法通りに解放処理を行う事にします。

    とても勉強になりました。皆様有難うございました。

    <追伸>
    oWBook = (Excel.Workbook)( oXls.Workbooks.Open( inFilename ) );

    oWBook = oXls.Workbooks.Open( inFilename );
    で良かったのですね。こちらも有難うございます。

    2012年7月27日 0:37
  • まあ、現在の ComObject wrapper の実装では Dispose パターンが適用されているので、using ブロックや ComponentContainer への登録を利用するなどで Dispose が呼び出されるようにしておくという一般的な .NET のリソース管理の手法でも COM 参照カウントの対応は OK ですね。

    下記の記事では null 代入によってGC の解放がはやくなるような記述もありますが、今の GC はそこまで馬鹿ではなくて null を代入したことではやく解放されることはまずありえないみたいなので目的無く null を代入することがリソース管理につながるという誤解を与えかねないので、今ならやめるべきこと・・・かな?と個人的には思いました。

    強い参照うんぬんも、ちょっと意味不明ですけどね。強い参照が他にあるなら null の代入なんて何も関係ないし、強い参照がそれ自身だけなら↑のとおりで、リソースの管理手段としては null を代入しないように教育するかんじです。

    > 以下に Office 開発系のサポートの方が書いている記事がありますのでリンクしておきます。

    > Office オートメーションで割り当てたオブジェクトを解放する - Part1
    > Office オートメーションで割り当てたオブジェクトを解放する – Part2

    2012年7月27日 3:50
  • まあ、現在の ComObject wrapper の実装では Dispose パターンが適用されているので、

    あれ、ランタイムレベルでそういったサポートって入っていましたっけ?
    それとも、特定の tlbimp ツールのようなもの、あるいは各自で作るクラスでしょうか。

    下記の記事では null 代入によってGC の解放がはやくなるような記述もありますが、今の GC はそこまで馬鹿ではなくて null を代入したことではやく解放されることはまずありえないみたいなので目的無く null を代入することがリソース管理につながるという誤解を与えかねないので、今ならやめるべきこと・・・かな?と個人的には思いました。

    このあたりは私も確たることを言えないので、自分の言葉ではなく、Microsoft 関係者が記載している何かがないかと求め、それに合う&今年書かれた記事というところからいただいたのが実情ですね。
    書いてあることが妥当かどうかを語る、あるいは本当のベストプラクティスが何かを語るには、内部挙動をきちんと知らない部分がありますし、オートメーションを十分使ってきたわけではないので避けます。(逃げのようですみません…)

    2012年7月27日 14:24
    モデレータ