Paste from Excel into C# app, retaining full precision

Lyall picture Lyall · Dec 23, 2011 · Viewed 8.4k times · Source

I have data in an Excel spreadsheet with values like this:

  • 0.69491375
  • 0.31220394

The cells are formatted as Percentage, and set to display two decimal places. So they appear in Excel as:

  • 69.49%
  • 31.22%

I have a C# program that parses this data off the Clipboard.

var dataObj = Clipboard.GetDataObject();
var format = DataFormats.CommaSeparatedValue;

if (dataObj != null && dataObj.GetDataPresent(format))
{
    var csvData = dataObj.GetData(format);
    // do something
}

The problem is that csvData contains the display values from Excel, i.e. '69.49%' and '31.22%'. It does not contain the full precision of the extra decimal places.

I have tried using the various different DataFormats values, but the data only ever contains the display value from Excel, e.g.:

  • DataFormats.Dif
  • DataFormats.Rtf
  • DataFormats.UnicodeText
  • etc.

As a test, I installed LibreOffice Calc and copy/pasted the same cells from Excel into Calc. Calc retains the full precision of the raw data.

So clearly Excel puts this data somewhere that other programs can access. How can I access it from my C# application?

Edit - Next steps.

I've downloaded the LibreOffice Calc source code and will have a poke around to see if I can find out how they get the full context of the copied data from Excel.

I also did a GetFormats() call on the data object returned from the clipboard and got a list of 24 different data formats, some of which are not in the DataFormats enum. These include formats like Biff12, Biff8, Biff5, Format129 among other formats that are unfamiliar to me, so I'll investigate these and respond if I make any discoveries...

Answer

afrischke picture afrischke · Jan 3, 2012

Also not a complete answer either, but some further insights into the problem:

When you copy a single Excel cell then what will end up in the clipboard is a complete Excel workbook which contains a single spreadsheet which in turn contains a single cell:

var dataObject = Clipboard.GetDataObject();
var mstream = (MemoryStream)dataObject.GetData("XML Spreadsheet");

// Note: For some reason we need to ignore the last byte otherwise
// an exception will occur...
mstream.SetLength(mstream.Length - 1);

var xml = XElement.Load(mstream);

Now, when you dump the content of the XElement to the console you can see that you indeed get a complete Excel Workbook. Also the "XML Spreadsheet" format contains the internal representation of the numbers stored in the cell. So I guess you could use Linq-To-Xml or similar to fetch the data you need:

XNamespace ssNs = "urn:schemas-microsoft-com:office:spreadsheet";

var numbers = xml.Descendants(ssNs + "Data").
              Where(e => (string)e.Attribute(ssNs + "Type") == "Number").
              Select(e => (double)e);

I've also tried to read the Biff formats using the Excel Data Reader however the resulting DataSets always came out empty...