4

In C# I need to copy data-grid rows to excel. Because some values in a row are doubles, "-Infinity" values are possible.

I've tried to copy the rows as DataFormats.UnicodeText or DataFormats.Text but this gave me the output "#NAME?" where I should see "-Infinity" (because excel automatically inserts a "=" before the minus in "-Infinity" due to the standard cell format).

When I format the cells to "Text" before pasting, excel does not automatically insert an "=" before the "-Infinity". By the way, I do not need to do any calculations with the double values in excel, so a text-format would be alright for me.

So my question is how to copy data to clipboard and paste it into excel while setting the cell format to "text".

DBD
  • 23,075
  • 12
  • 60
  • 84
iop
  • 312
  • 2
  • 4
  • 12
  • Try Pasting `1` _tab_ `2` _tab_ `'-Infinity` – James Apr 02 '13 at 13:12
  • I know this solution, but I don't want to have apostrophes in the cells. Isn't there a clipboard data format from which excel automatically assumes that the data has to be interpreted as text? – iop Apr 02 '13 at 13:55

3 Answers3

9

Starting with a Raw Clipboard viewer you can see that copying

Excel Snip

to the clipboard results in Excel throwing a large number of different formats to the clipboard.

enter image description here

Most of these aren't helpful, but some of them are internal to excel, meaning it will (almost) guarantee that the data will be the same as copied. If I were you I'd probably target XML SpreadSheet or if your feeling brave Biff12 which is also xml (but zipped). This will give you far more control over the paste than normal text.

As an example the above clip results in

<?xml version="1.0"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
 xmlns:o="urn:schemas-microsoft-com:office:office"
 xmlns:x="urn:schemas-microsoft-com:office:excel"
 xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
 xmlns:html="http://www.w3.org/TR/REC-html40">
 <Styles>
  <Style ss:ID="Default" ss:Name="Normal">
   <Alignment ss:Vertical="Bottom"/>
   <Borders/>
   <Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="11" ss:Color="#000000"/>
   <Interior/>
   <NumberFormat/>
   <Protection/>
  </Style>
  <Style ss:ID="s63">
   <NumberFormat ss:Format="@"/>
  </Style>
 </Styles>
 <Worksheet ss:Name="Sheet1">
  <Table ss:ExpandedColumnCount="2" ss:ExpandedRowCount="2"
   ss:DefaultRowHeight="15">
   <Row>
    <Cell><Data ss:Type="Number">1</Data></Cell>
    <Cell><Data ss:Type="Number">2</Data></Cell>
   </Row>
   <Row>
    <Cell><Data ss:Type="String">Test</Data></Cell>
    <Cell ss:StyleID="s63"><Data ss:Type="String" x:Ticked="1">-Infinity</Data></Cell>
   </Row>
  </Table>
 </Worksheet>
</Workbook>

So looking a little deeper still... It seem's the .Net Clipboard class does some wierd and not so wonderful things when I tried to use Clipboard.SetData to write the xml to the clipboard Clipboard Data

The clipboard starts with a load of chaff. This of course results in Excel rejecting the clipboard contents.

To get around this I use the Windows API (user32) calls to work with the clipboard

    [DllImport("user32.dll", SetLastError = true)]
    static extern uint RegisterClipboardFormat(string lpszFormat);
    [DllImport("user32.dll")]
    static extern IntPtr SetClipboardData(uint uFormat, IntPtr hMem);
    [DllImport("user32.dll", SetLastError = true)]
    static extern bool CloseClipboard();
    [DllImport("user32.dll", SetLastError = true)]
    static extern bool OpenClipboard(IntPtr hWndNewOwner);

    private static void XMLSpreadSheetToClipboard(String S)
    {
        var HGlob = Marshal.StringToHGlobalAnsi(S);
        uint Format = RegisterClipboardFormat("XML SpreadSheet");
        OpenClipboard(IntPtr.Zero);
        SetClipboardData(Format, HGlob);
        CloseClipboard();
        Marshal.FreeHGlobal(HGlob);
    }
James
  • 9,774
  • 5
  • 34
  • 58
  • This seems to be interesting. How do you copy this into clipboard? Do you use DataFormats.Html or "XML SpreadSheet"? I tried both but Excel denies to paste the string... – iop Apr 03 '13 at 08:45
  • @iop I agree, that was harder than it should have been! I've included a code sample that worked for me. – James Apr 03 '13 at 09:58
  • This is great. A lovely starting point to great code. With one post you have saved me many hours of wondering why my clipboard functionality was not working. *sigh* now to create the correct XML from inside the application. – HeXanon Feb 06 '15 at 08:12
  • `BIFF12` unfortunately has some bin parts intermixed. It's the `.xlsb` file format. – wqw Mar 10 '17 at 11:27
2

Thanks for the info. After some more searching, I found that you can use the .Net clipboard class, but you can't pass a String to SetData(). This works for me:

System::Text::UTF8Encoding^ enc = gcnew System::Text::UTF8Encoding();
System::Windows::Forms::Clipboard::SetData("XML Spreadsheet", gcnew MemoryStream(enc->GetBytes(data)));
1

The win32 api solution did not work for me. I had had random crashes. Following code works fine, also with unicode chars.

var xml = File.ReadAllText(@"..\..\SampleData\Clipboard-Example3.xml");

var stream = new MemoryStream(Encoding.UTF8.GetBytes(xml));
var dataObject = new DataObject();
dataObject.SetData("XML SpreadSheet", stream);

Clipboard.Clear();
Clipboard.SetDataObject(dataObject);
  • Hey, welcome to SO. It's a good idea to check the dates on the questions you're answering. Your answer may still be relevant as questions here turn up in search results all the time, but the question you answered is about 7 years old, and in programming that's a long time :) – Software Engineer Mar 27 '20 at 14:22
  • Hey, thanks. I would have never thought of that.Perhaps you could direct me to a better place to post this solution.I was looking for something like that without success, and placed my findings (after hours of research) in a place with a matching context. – Steffen Mantz Mar 28 '20 at 06:29
  • Sorry, .net and c# aren't my field, so I wouldn't know how to help with that. – Software Engineer Mar 28 '20 at 21:19
  • 1
    @SoftwareEngineer Adding new answers to existing, old questions is important, as new ways of doing it arrive or being discovered/created. SO dont want questions asked twice, so the only chance is to update the existing one, even if its from the distant past. The question was posted long ago, but to issue still exists. Excel is still there, xml is still used, the clipboard is also widely used. Even google thinks its important(this page was the first result). So please stop telling people not to post their discoveries to "old" questions, as it may help others and improve the quality of SO. ;) – FrankM Feb 02 '21 at 08:41
  • @FrankM - thanks, I know how SO works, I've been here for nearly as long as you have. I also know that new users often don't realise that their answers are for old questions. I didn't tell Steffan to not post an answer here, just that they should be aware of the dates. In fact, I explicitly said that this answer may still be relevant. – Software Engineer Feb 02 '21 at 09:35