2017-05-04 157 views
3

我有一個Winform項目,在Microsoft Framework 3.5上創建。 用戶可能已經安裝了Windows 7或Windows XP以及Office 2007或更高版本。如何使用剪貼板將數據從Excel表格複製到DataTable?

我正在開發一個過程來獲取剪貼板數據並放入C#DataTable中。 我已經創建了一個方法從剪貼板獲取原始數據並將其上傳到DataTable中。

但在某些情況下,Excel數據顯示的值,但內部有另一個:

enter image description here

我調查的方法,以從Excel中獲得的原始數據:

string XmlFmt = "XML Spreadsheet"; 
var clipboard = Clipboard.GetDataObject(); 

if (clipboard.GetDataPresent(XmlFmt)) 
{ 
    var clipData = clipboard.GetData(XmlFmt); 
    StreamReader streamReader = new StreamReader((MemoryStream)clipData); 
    streamReader.BaseStream.SetLength(streamReader.BaseStream.Length - 1); 

    string xmlText = streamReader.ReadToEnd(); 
    var stream = new StringReader(xmlText); 

    XmlDocument xmlDocument = new XmlDocument(); 
    xmlDocument.LoadXml(xmlText); 

    DataSet dsExcelData = new DataSet(); 
    dsExcelData.ReadXml(new XmlNodeReader(xmlDocument)); 
} 

但是,此方法使用Excels數據的每個部分的配置來檢索具有多個表的DataSet: enter image description here enter image description here

基本上,我想將這些結構轉換爲只有原始數據的簡單DataTable。 有人可以幫助我一個提示如何實現這一目標? ...我不想在這個實現中使用第三方庫。

+0

dsExcelData.Tables [0]將爲您提供第一個基礎數據表 – MethodMan

回答

0

我發現了一個乾淨的防彈解決方案。下面的代碼:

首先,延伸到的XmlDocument轉換成的XElement:

/// <summary> Convert XML Document to XDocument </summary> 
/// <param name="xmlDocument">Attached XML Document</param> 
public static XDocument fwToXDocument(this XmlDocument xmlDocument) 
{ 
    using (XmlNodeReader xmlNodeReader = new XmlNodeReader(xmlDocument)) 
    { 
     xmlNodeReader.MoveToContent(); 
     return XDocument.Load(xmlNodeReader); 
    } 
} 

完整的功能:

private DataTable clipboardExcelToDataTable(bool blnFirstRowHasHeader = false) 
{ 
    string strTime = "S " + DateTime.Now.ToString("mm:ss:fff"); 
    var clipboard = Clipboard.GetDataObject(); 
    if (!clipboard.GetDataPresent("XML Spreadsheet")) return null; 

    strTime += "\r\nRead " + DateTime.Now.ToString("mm:ss:fff"); 
    StreamReader streamReader = new StreamReader((MemoryStream)clipboard.GetData("XML Spreadsheet")); 
    strTime += "\r\nFinish read " + DateTime.Now.ToString("mm:ss:fff"); 
    streamReader.BaseStream.SetLength(streamReader.BaseStream.Length - 1); 

    XmlDocument xmlDocument = new XmlDocument(); 
    xmlDocument.LoadXml(streamReader.ReadToEnd()); 
    strTime += "\r\nRead XML Document " + DateTime.Now.ToString("mm:ss:fff"); 

    XNamespace ssNs = "urn:schemas-microsoft-com:office:spreadsheet"; 
    DataTable dtData = new DataTable(); 

    var linqRows = xmlDocument.fwToXDocument().Descendants(ssNs + "Row").ToList<XElement>(); 

    for (int x = 0; x < linqRows.Max(a => a.Descendants(ssNs + "Cell").Count()); x++) 
     dtData.Columns.Add("Column {0}".fwFormat(x + 1)); 

    int intCol = 0; 

    DataRow drCurrent; 

    linqRows.ForEach(rowElement => 
     { 
      intCol = 0; 
      drCurrent = dtData.Rows.Add(); 
      rowElement.Descendants(ssNs + "Cell") 
       .ToList<XElement>() 
       .ForEach(cell => drCurrent[intCol++] = cell.Value); 
     }); 

    if (blnFirstRowHasHeader) 
    { 
     int x = 0; 
     foreach (DataColumn dcCurrent in dtData.Columns) 
      dcCurrent.ColumnName = dtData.Rows[0][x++].ToString(); 

     dtData.Rows.RemoveAt(0); 
    } 

    strTime += "\r\nF " + DateTime.Now.ToString("mm:ss:fff"); 

    return dtData; 
} 

的過程需要15秒,讀取〜25,000行。

適用於任何類型的數據。 基本上,該方法創建一個具有相同結構的Excel WorkSheet的網格。 合併行或列將填滿第一個單元格。 默認情況下,所有列都將是字符串DataType。

2

如果他們是平坦的數據,你可以這樣做。

private class Field 
{ 
     public string Valor { get; set; } 
} 

private class Row 
{ 
     public List<Field> Fields { get; set; } 

     public Row(string value) 
     { 
      Fields = new List<Field>(); 
      var fieldsString = value.Split(new char[] {'\t'}); 
      foreach (string f in fieldsString) 
      { 
       Fields.Add(new Field {Valor = f}); 
      } 
    } 
} 

public Parse() 
{ 
    var data = Clipboard.GetDataObject(); 
    var datos = (string)data.GetData(DataFormats.Text); 
    var stringRows = datos.Split(new Char[] {'\r', '\n'}, StringSplitOptions.RemoveEmptyEntries); 
    var table = new List<Row>(stringRows.Length) ; 
    foreach (string stringRow in stringRows) 
    { 
     table.Add(new Row(stringRow)); 
    } 
} 
+0

謝謝,但不適合我的需要。嘗試獲取剪貼板數據爲文本將檢索包含「掩碼」的值。以值「12312451512412」爲例的單元格將檢索到「1.23125E + 13」。或者「0%」而不是「0.3%」。 – MiBol

0
在某些情況下

,Excel數據顯示的值,但內部有另一個。

使用XML方法是多個數據表內部結構的原因。試試這個方法,而不是:

private void PasteFromExcel() 
{ 
    DataTable tbl = new DataTable(); 
    tbl.TableName = "ImportedTable"; 
    List<string> data = new List<string>(ClipboardData.Split('\n')); 
    bool firstRow = true; 

    if (data.Count > 0 && string.IsNullOrWhiteSpace(data[data.Count - 1])) 
    { 
     data.RemoveAt(data.Count - 1); 
    } 

    foreach (string iterationRow in data) 
    { 
     string row = iterationRow; 
     if (row.EndsWith("\r")) 
     { 
      row = row.Substring(0, row.Length - "\r".Length); 
     } 

     string[] rowData = row.Split(new char[] { '\r', '\x09' }); 
     DataRow newRow = tbl.NewRow(); 
     if (firstRow) 
     { 
      int colNumber = 0; 
      foreach (string value in rowData) 
      { 
       if (string.IsNullOrWhiteSpace(value)) 
       { 
        tbl.Columns.Add(string.Format("[BLANK{0}]", colNumber)); 
       } 
       else if (!tbl.Columns.Contains(value)) 
       { 
        tbl.Columns.Add(value); 
       } 
       else 
       { 
        tbl.Columns.Add(string.Format("Column {0}", colNumber)); 
       } 
       colNumber++; 
      } 
      firstRow = false; 
     } 
     else 
     { 
      for (int i = 0; i < rowData.Length; i++) 
      { 
       if (i >= tbl.Columns.Count) break; 
       newRow[i] = rowData[i]; 
      } 
      tbl.Rows.Add(newRow); 
     } 
    } 

    DataGridView1.DataSource = tbl; 
} 

編號:http://www.seesharpdot.net/?p=221

編輯:

我已經做了一些測試,甚至使用「XML電子表格」剪貼板格式的數據可以得到存儲在指數註釋:

enter image description here

你可以檢測nd轉換這些數字:Parse a Number from Exponential Notation

+0

感謝分享,但無效。在這個例子中,剪貼板數據的單元格值爲「1.23125E + 13」,但單元格的實際值爲「12312451512412」。這是我想以XML SpreadSheet的形式讀取剪貼板的主要原因之一。 – MiBol