2013-06-04 17 views
10

「最も簡単な回答は見つけにくい」というケースに遭遇していると思いますが、これを私に与える検索はありません簡単な方法。これは、既存のVSTO(C#)プロジェクト内でExcel 2010およびVS 2010です。ワークシートをデータソースとして使用したVSTO Excelの簡単な例

私は、DataGridViewのソースとして使用したい4列のデータを含むExcelワークシートを持っています。誰かが(1)特定のワークシートからデータを取得し、それにカスタムオブジェクトを設定するために、C#コードスニペットを提供できますか? (2)オブジェクト(IEnumerableリストのような)をDatagridviewにバインドし、(3)グリッドに固有の更新および削除機能のスニペットを使用してソースワークシートにフィードバックします。

私はここで多くを求めていることは知っていますが、VSTOの情報の多くは分断されていて、必ず見つかるとは限りません。ありがとう!

+0

あなたはこれを見ましたか? http://stackoverflow.com/questions/15828/reading-excel-files-from-c-sharp – juanvan

+0

これは私の質問ではっきりとわかりません。私はExcel Workbookを自分のプロジェクトとして取り組んでいます。だから私はExcel *内のデータ*を読み込もうとしています。私はある時点でListObjectの例が必要だと思っていますが、私は肯定的ではありません。 –

+1

概念的には、VSTOを使用すると、Excelプロセス内で実行されているマネージコードからExcelオブジェクトモデルにアクセスできます。したがって、VSTOではなくExcelオブジェクトモデルを検索する必要があります。ほとんどの例はVBAにありますが、管理対象のC#に移植するのは簡単です。あなたはListObjectがおそらく一番簡単な方法だと思います。 – Joe

答えて

15

は編集:グレート、私はちょうど私があなたの大きな部分を逃したことに気づきました質問を受け取り、ワークシートに更新および削除を戻します。私はそれが可能かどうかは全く考えていませんが、私の解決策は役に立たないと思います。とにかくそれをここに残しておきます、多分それはどんな形であれ助けてくれるかもしれません。


なぜVSTOが必要ですか?私の知る限り、VSTOはOfficeアドインに使用されています。しかし、DataGridViewにデータを表示したいので、私は、ワークブックにアクセスするだけのWinFormsアプリケーションがあると仮定します。この場合、Office Interopを使用してブックを開くことができます。 Microsoft.Office.Interop.Excelへの参照をプロジェクトに追加し、using Microsoft.Office.Interop.Excel;ステートメントを追加するだけです。エクセル相互運用のための

MSDNのリファレンスドキュメントは、ここで見つけることができます:http://msdn.microsoft.com/en-us/library/ms262200%28v=office.14%29.aspx

私は多分他の誰かが残りを行うことができます、あなたにExcelの一部を与えるでしょう。

まず、オープンExcelとワークブック:

Application app = new Application(); 
// Optional, but recommended if the user shouldn't see Excel. 
app.Visible = false; 
app.ScreenUpdating = false; 
// AddToMru parameter is optional, but recommended in automation scenarios. 
Workbook workbook = app.Workbooks.Open(filepath, AddToMru: false); 

はその後何とか正しいワークシートを取得します。いくつかの可能性があります:

// Active sheet (should be the one which was active the last time the workbook was saved). 
Worksheet sheet = workbook.ActiveSheet; 
// First sheet (notice that the first is actually 1 and not 0). 
Worksheet sheet = workbook.Worksheets[1]; 
// Specific sheet. 
// Caution: Default sheet names differ for different localized versions of Excel. 
Worksheet sheet = workbook.Worksheets["Sheet1"]; 

次に正しい範囲を取得してください。あなたは、必要なデータがどこにあるかをどのように知っているかを指定しなかったので、固定列にあると仮定します。

// If you also know the row count. 
Range range = sheet.Range["A1", "D20"]; 
// If you want to get all rows until the last one that has some data. 
Range lastUsedCell = sheet.Cells.SpecialCells(XlCellType.xlCellTypeLastCell); 
string columnName = "D" + lastUsedCell.Row; 
Range range = sheet.Range["A1", columnName]; 

値を取得します:2次元オブジェクト配列は、その後、あなたのDataGridViewのデータソースとして使用することができることを

// Possible types of the return value: 
// If a single cell is in the range: Different types depending on the cell content 
// (string, DateTime, double, ...) 
// If multiple cells are in the range: Two dimensional array that exactly represents 
// the range from Excel and also has different types in its elements depending on the 
// value of the Excel cell (should always be that one in your case) 
object[,] values = range.Value; 

。私はWinFormsを何年も使用していないので、直接バインドできるかどうか、またはデータを特定の形式にする必要があるかどうかはわかりません。

最後に近いExcelが再び:あなたがCOMオブジェクトへのすべての参照が解放されていることを確認する必要があるため正しく相互運用機能を使用した後、Excelを閉じ

workbook.Close(SaveChanges: false); 
workbook = null; 
app.Quit(); 
app = null; 
// Yes, we really want to call those two methods twice to make sure all 
// COM objects AND all RCWs are collected. 
GC.Collect(); 
GC.WaitForPendingFinalizers(); 
GC.Collect(); 
GC.WaitForPendingFinalizers(); 

は、タスクそのものです。私がこれを行う最も簡単な方法は、別々の方法でExcelとブック(最初と最後のコードブロック)を開いて閉じることを除いてすべての作業を行うことです。これにより、Quitが呼び出されたときに、そのメソッドで使用されているすべてのCOMオブジェクトが有効範囲外になります。

1

のでSheet1_Startupイベント

Excel.Range range1 = this.Range["A1", missing]; 
var obj = range1.Value2.ToString(); 

にあなたが次のセルに移動する必要があるでしょう、その後

range1 = this.Range["A2", missing]; 
    obj = New list(range1.Value2.ToString()); 
+0

しかし、これらは単一のセルからの単なる値です。ワークシートや範囲全体をデータセットとして扱う方法はありませんか? –

+0

this.Range ["A1"、 "A5"]を使うことができました。しかしあなたはリストに入っています。私はDSにそれを手に入れる方法がわかりません。申し訳ありません – juanvan

+0

@JimBeam:すべてのセルを1つずつ読み書きするのではなく、範囲全体を読み書きする方法を調べてください。 –

3

これは私が書いた最も醜いコードの一つであるが、それは概念の証明として動作します:)その

 
Column1  Column2  Column3  Column4 
------------------------------------------------------ 
Data-1-1 Data-2-1 Data-3-1 Data-4-1 
Data-1-2 Data-2-2 Data-3-2 Data-4-2 
.... 

Excelファイルが正確に50行が含まれているように私は、例のワークブックを作成しました ハードコーディングされた範囲セレクタについて説明します。 フォームを作成し、データビューグリッドを追加し、MyExcelDataのデータソースを作成し、MyExcelDataのインスタンスをvar data = new MyExcelData(pathToExcelFile);のように作成し、グリッドにバインドするだけです。

コードは醜いですが、多くの前提がありますが、要件を実装しています。 Excelを開いてプログラムを作成すると、グリッド上の更新がセルの編集後にExcelに反映されることがわかります。削除された行もExcelから削除されます。あなたのエクセルのプライマリキーがあるかどうか分からなかったので、IDとして行インデックスを使用しました。

ところで、私はVSTOに関しては本当に悪いです。あなたがより良い方法を知っていれば、私に通知してください。

public class MyExcelDataObject 
{ 
    private readonly MyExcelData owner; 
    private readonly object[,] realData; 
    private int RealId; 
    public MyExcelDataObject(MyExcelData owner, int index, object[,] realData) 
    { 
     this.owner = owner; 
     this.realData = realData; 
     ID = index; 
     RealId = index; 
    } 

    public int ID { get; set; } 

    public void DecrementRealId() 
    { 
     RealId--; 
    } 

    public string Column1 
    { 
     get { return (string)realData[RealId, 1]; } 
     set 
     { 
      realData[ID, 1] = value; 
      owner.Update(ID); 
     } 
    } 
    public string Column2 
    { 
     get { return (string)realData[RealId, 2]; } 
     set 
     { 
      realData[ID, 2] = value; 
      owner.Update(ID); 
     } 
    } 
    public string Column3 
    { 
     get { return (string)realData[RealId, 3]; } 
     set 
     { 
      realData[ID, 3] = value; 
      owner.Update(ID); 
     } 
    } 
    public string Column4 
    { 
     get { return (string)realData[RealId, 4]; } 
     set 
     { 
      realData[ID, 4] = value; 
      owner.Update(ID); 
     } 
    } 
} 

public class MyExcelData : BindingList<MyExcelDataObject> 
{ 
    private Application excel; 
    private Workbook wb; 
    private Worksheet ws; 

    private object[,] values; 

    public MyExcelData(string excelFile) 
    { 
     excel = new ApplicationClass(); 
     excel.Visible = true; 
     wb = excel.Workbooks.Open(excelFile); 
     ws = (Worksheet)wb.Sheets[1]; 

     var range = ws.Range["A2", "D51"]; 
     values = (object[,])range.Value; 

     AllowEdit = true; 
     AllowRemove = true; 
     AllowEdit = true; 

     for (var index = 0; index < 50; index++) 
     { 
      Add(new MyExcelDataObject(this, index + 1, values)); 
     } 
    } 

    public void Update(int index) 
    { 
     var item = this[index - 1]; 

     var range = ws.Range["A" + (2 + index - 1), "D" + (2 + index - 1)]; 
     range.Value = new object[,] 
      { 
       {item.Column1, item.Column2, item.Column3, item.Column4} 
      }; 
    } 

    protected override void RemoveItem(int index) 
    { 
     var range = ws.Range[string.Format("A{0}:D{0}", (2 + index)), Type.Missing]; 
     range.Select(); 
     range.Delete(); 
     base.RemoveItem(index); 

     for (int n = index; n < Count; n++) 
     { 
      this[n].DecrementRealId(); 
     } 
    } 
} 

PS:軽量オブジェクトを使用したいと思いますが、不要な合併症を追加します。

+0

"これは最も醜いコードの1つです" - Dijkstraに加えて、シェイクスピアはあまりにも誇りではありません。 –

4

UPDATE:

私は速いアプローチの新しいコードで私の以前のメソッドを置き換えます。 System.Arrayは、非常に効率的で高速な方法でデータを読み取り、バインドすることができます。デモはthis linkからダウンロードできます。


Excel 2003ブックでVSTOアプリケーションを開発しました。構文の点で大きな違いはないので、2007/2010年には何の努力を払っても使用できません。

enter image description here

私はあなたが私はあなたが使用することを想定していますので、データを表示するウィンドウを開くために使用されるイベントを知りませんでした。

SheetFollowHyperlink 

Showdata.csで宣言された静的なブックオブジェクトを使用します。ここで私は現在のシート上のリンクを追加して、それがDataGridViewのとウィンドウがポップアップ表示されますあなたのThisworkbook.cs

private void ThisWorkbook_Startup(object sender, System.EventArgs e) 
     { 
      ShowData._WORKBOOK = this; 
     } 
private void ThisWorkbook_SheetFollowHyperlink(object Sh, Microsoft.Office.Interop.Excel.Hyperlink Target) 
     { 
      System.Data.DataTable dTable = GenerateDatatable(); 
      showData sh = new showData(dTable); 
      sh.Show(); // You can also use ShowDialog() 
     } 

ためのコードです。

  private System.Data.DataTable GenerateDatatable() 
    { 
     Range oRng = null; 
     // It takes the current activesheet from the workbook. You can always pass any sheet as an argument 

     Worksheet ws = this.ActiveSheet as Worksheet; 

     // set this value using your own function to read last used column, There are simple function to find last used column 
     int col = 4; 
     // set this value using your own function to read last used row, There are simple function to find last used rows 
     int row = 5; 

//メソッド 文字列strRange = "A1" によって返されたその4と5とさせて頂きます。 string andRange = "D5";

 System.Array arr = (System.Array)ws.get_Range(strRange, andRange).get_Value(Type.Missing); 
     System.Data.DataTable dt = new System.Data.DataTable(); 
     for (int cnt = 1; 
      cnt <= col; cnt++) 
      dt.Columns.Add(cnt.Chr(), typeof(string)); 
     for (int i = 1; i <= row; i++) 
     { 
      DataRow dr = dt.NewRow(); 
      for (int j = 1; j <= col; j++) 
      { 
       dr[j - 1] = arr.GetValue(i, j).ToString(); 
      } 
      dt.Rows.Add(dr); 
     } 
     return dt; 
    } 

ここでは、ユーザーが値を表示および編集できる形式です。 拡張メソッドとChr()を追加して、数値をそれぞれのアルファベットに変換すると便利です。

public partial class ShowData : Form 
    { 
     //use static workbook object to access Worksheets 
     public static ThisWorkbook _WORKBOOK; 

     public ShowData(System.Data.DataTable dt) 
     { 
      InitializeComponent(); 
      // binding value to datagrid 
      this.dataGridView1.DataSource = dt; 
     } 

     private void RefreshExcel_Click(object sender, EventArgs e) 
     { 
      Worksheet ws = ShowData._WORKBOOK.ActiveSheet as Worksheet; 
      System.Data.DataTable dTable = dataGridView1.DataSource as System.Data.DataTable; 

      // Write values back to Excel sheet 
      // you can pass any worksheet of your choice in ws 
      WriteToExcel(dTable,ws); 
     } 

     private void WriteToExcel(System.Data.DataTable dTable,Worksheet ws) 
    { 
     int col = dTable.Columns.Count; ; 
     int row = dTable.Rows.Count; 

     string strRange = "A1"; 
     string andRange = "D5"; 

     System.Array arr = Array.CreateInstance(typeof(object),5,4); 
     for (int i = 0; i < row; i++) 
     { 
      for (int j = 0; j < col; j++) 
      { 
       try 
       { 
        arr.SetValue(dTable.Rows[i][j].ToString(), i, j); 
       } 
       catch { } 
      } 

     } 
     ws.get_Range(strRange, andRange).Value2 = arr; 
     this.Close(); 
    } 
    public static class ExtensionMethods 
    { 
     static string alphabets = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 
     public static string Chr(this int p_intByte) 
     { 

      if (p_intByte > 0 && p_intByte <= 26) 
      { 
       return alphabets[p_intByte - 1].ToString(); 
      } 
      else if (p_intByte > 26 && p_intByte <= 700) 
      { 
       int firstChrIndx = Convert.ToInt32(Math.Floor((p_intByte - 1)/26.0)); 
       int scndIndx = p_intByte % 26; 
       if (scndIndx == 0) scndIndx = 26; 
       return alphabets[firstChrIndx - 1].ToString() + alphabets[scndIndx - 1].ToString(); 
      } 

      return "NA"; 
     } 

    } 
関連する問題