【问题标题】:How to format rows and columns in clipboard to paste like coming from Excel?如何格式化剪贴板中的行和列以像来自 Excel 一样粘贴?
【发布时间】:2013-03-06 12:55:40
【问题描述】:

我正在处理一堆网格,这些网格不支持使用 Excel 那样的表格布局复制行和列。

我们需要能够从网格中复制一些行和列,然后将它们粘贴到 Outlook 电子邮件中,并且在正确对齐的列中具有适当的格式。如果您从 Excel 中复制,效果会很好。

如果我从网格中复制,我会得到制表符分隔的数据,这不起作用。此外,字体不像 Courier 那样等宽,因此将数据填充到相同数量的字符也不起作用。

我真的很想知道 Excel 如何设法将这种额外的格式放入剪贴板。顺便说一句,我正在使用 Delphi,但任何建议都值得赞赏。

编辑:我们不想先通过 Excel...我们想直接从网格到剪贴板,然后到电子邮件。

谢谢! 巴特

【问题讨论】:

  • Microsoft Office Excel 97-2007 二进制文件格式规范在this document 中描述。
  • @TLama 我希望 Excel 以多种格式将数据存储到 剪贴板(例如纯文本、RTF、HTML(?),不知何故 Excel 格式),但不是作为excel二进制文件格式
  • @SirRufo,这是also described。我想说 Excel 最自然的做法是粘贴那些 BIFF 记录(在我之前评论中链接的文档中描述)。
  • @tLama +1 进行清理。根据问题,剪贴板必须填充纯文本、RTF 和 HTML 格式的数据以供 Outlook 使用(因为它支持所有 3 种类型的电子邮件)

标签: excel delphi copy clipboard


【解决方案1】:

当您从 Excel 复制到剪贴板时,剪贴板上会放置许多不同的格式。您需要找到可以复制的格式之一,这将产生所需的结果。

我过去实现这一点的方法是将 HTML 放在剪贴板上。您可以使用此功能:

procedure ClipboardError;
begin
  raise EMyExceptionClass.Create('Could not complete clipboard operation.');
end;

procedure CheckClipboardHandle(Handle: Windows.HGLOBAL);
begin
  if Handle=0 then begin
    ClipboardError;
  end;
end;

procedure CheckClipboardPtr(Ptr: Pointer);
begin
  if not Assigned(Ptr) then begin
    ClipboardError;
  end;
end;

procedure PutInClipboard(ClipboardFormat: UINT; Buffer: Pointer; Count: Integer);
var
  Handle: Windows.HGLOBAL;
  Ptr: Pointer;
begin
  if Count>0 then begin
    Clipboard.Open;
    Try
      Handle := Windows.GlobalAlloc(GMEM_MOVEABLE, Count);
      Try
        CheckClipboardHandle(Handle);
        Ptr := Windows.GlobalLock(Handle);
        CheckClipboardPtr(Ptr);
        Move(Buffer^, Ptr^, Count);
        Windows.GlobalUnlock(Handle);
        Clipboard.SetAsHandle(ClipboardFormat, Handle);
      Except
        GlobalFree(Handle);
        raise;
      End;
    Finally
      Clipboard.Close;
    End;
  end;
end;

var
  HTMLClipboardFormat: UINT;

procedure PutHTMLInClipboard(Strings: TStrings);

var
  Data: TStringList;

  procedure WriteDescription(const StartOffset, EndOffset: Integer);
  begin
    while Data.Count<5 do begin
      Data.Add('');
    end;
    Data[0] := 'Version:0.9';
    Data[1] := Format('StartHTML:%.8d', [StartOffset]);
    Data[2] := Format('EndHTML:%.8d', [EndOffset]);
    Data[3] := Format('StartFragment:%.8d', [StartOffset]);
    Data[4] := Format('EndFragment:%.8d', [EndOffset]);
  end;

var
  StartOffset, EndOffset: Integer;
  Text: UTF8String;
begin
  Data := TStringList.Create;
  Try
    WriteDescription(0, 0);//write out description stub - will be replaced later
    StartOffset := Length(UTF8String(Data.Text));
    Data.AddStrings(Strings);
    EndOffset := Length(UTF8String(Data.Text))-1;
    WriteDescription(StartOffset, EndOffset);//now we know the offsets we can write proper description
    Text := Data.Text;
    PutInClipBoard(HTMLClipboardFormat, PAnsiChar(Text), Length(Text));
  Finally
    FreeAndNil(Data);
  End;
end;
....
initialization
  HTMLClipboardFormat := Windows.RegisterClipboardFormat('HTML Format');

剩下的唯一事情就是生成您传递给该函数的 HTML。这取决于你。我建议您使用 Excel 将 HTML 放在剪贴板上,然后检查它生成的 HTML。将其用作您需要做什么的指南。

【讨论】:

  • 嗨@David Heffernan,您的代码中似乎缺少 PutInClipBoard(HTMLClipboardFormat, PAnsiChar(Text), Length(Text)) 过程?
  • 谢谢。我似乎无法让它工作......我想你的代码工作正常,但我只是尝试了几个解决方案并且没有一个 HTML 工作。刚刚尝试了您的代码并仅使用“html”作为文本,即使这样也无法粘贴任何内容。所以我想这与我有关,但我不知道是什么。常规文本工作正常。顺便说一句,我正在使用 Delphi 2009,这是否重要。
  • 好吧,该代码确实可以正常工作。您需要在其中获取一些 HTML。你这样做吗?
  • 好吧,我现在对自己很生气。您的代码有效,我尝试过的所有其他代码可能也有效。最初没有的原因是我正在使用没有Office的XP虚拟机进行开发。因此,我在调试时将 html 放到 VM 中的剪贴板上,但切换到我的主要 Windows 7 机器尝试将其粘贴到 Outlook 中。那没有用。但是当我刚刚在我的 Windows 7 机器上启动应用程序时它确实有效。非常感谢!
【解决方案2】:

您可以使用此功能将网格内容导出到 Excel 和其他支持表格数据的应用程序:

procedure ExportDBGrid(DBGrid: TDBGrid; toExcel: Boolean);
var
  bm: TBookmark;
  col, row: Integer;
  sline: String;
  mem: TStringList;
  ExcelApp: Variant;
begin
  Screen.Cursor := crHourglass;
  try
    DBGrid.DataSource.DataSet.DisableControls;
    bm := DBGrid.DataSource.DataSet.GetBookmark;
    DBGrid.DataSource.DataSet.First;

    // create the Excel object
    if toExcel then
    begin
      ExcelApp := CreateOleObject('Excel.Application');
      ExcelApp.WorkBooks.Add(1); //xlWBatWorkSheet);
      ExcelApp.WorkBooks[1].WorkSheets[1].Name := 'Grid Data';
    end;

    // First we send the data to a memo
    // works faster than doing it directly to Excel
    mem := TStringList.Create;
    try
      sline := '';

      // add the info for the column names
      for col := 0 to DBGrid.FieldCount-1 do
        if Assigned(DBGrid.Fields[col]) then
          if DBGrid.Fields[col].Visible then
            sline := sline + DBGrid.Fields[col].DisplayLabel + #9;
      mem.Add(sline);

      // get the data into the memo
      for row := 0 to DBGrid.DataSource.DataSet.RecordCount-1 do
      begin
        sline := '';
        for col := 0 to DBGrid.FieldCount-1 do
          if Assigned(DBGrid.Fields[col]) then
            if DBGrid.Fields[col].Visible then
              sline := sline + DBGrid.Fields[col].AsString + #9;
        mem.Add(sline);
        DBGrid.DataSource.DataSet.Next;
      end;

      // we copy the data to the clipboard
      Clipboard.AsText := mem.Text;
    finally
      mem.Free;
    end;
    // if needed, send it to Excel
    // if not, we already have it in the clipboard
    if toExcel then
    begin
      ExcelApp.Workbooks[1].WorkSheets['Grid Data'].Paste;
      ExcelApp.Visible := true;
    end;
  finally
    DBGrid.DataSource.DataSet.GotoBookmark(bm);
    DBGrid.DataSource.DataSet.EnableControls;
    Screen.Cursor := crDefault;
  end;
end;

【讨论】:

  • 干得好,但你应该提到它需要安装 Excel
  • 它是,对于那些知道的人。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-12-06
  • 2015-07-29
  • 1970-01-01
  • 1970-01-01
  • 2013-07-08
  • 1970-01-01
  • 2017-12-23
相关资源
最近更新 更多