Einzelnen Beitrag anzeigen

David Martens

Registriert seit: 29. Sep 2003
205 Beiträge
 
Delphi XE Enterprise
 
#8

AW: virtualstringtree in excel exportieren

  Alt 15. Jul 2010, 13:40
Da muß ich ein wenig wiedersprechen.

Fakt ist man muß eine Konvertierung machen, ob StringGrid, VST oder sonstwas.

Da unser Export mit unterschiedlichen "Grids" arbeiten können soll, habe ich eine "Wrapper"-Klasse geschrieben, damit die eingentliche Exportklasse übersichtlich bleibt.

Hier ein paar Ansätze:
Delphi-Quellcode:
procedure TExcelServer.WriteRange(Row, Col, RowCount, ColCount: integer; Values: OleVariant);
begin
  ExcelWorksheet.Range[
    TranslateCell(Row + 1, Col + 1),
    TranslateCell(Row + RowCount, Col + ColCount)].Value := Values;
end;
Das ist die schnellste Variante GROSSE Datenmengen an Excel zu senden. (TranslateCell macht aus Integer die Excel Spalten/Zeilen-bezeichnungen)

Der OleVariant wird in der "Wrapper"-Klasse erzeugt:
Delphi-Quellcode:
function TDataWrapper.GetDataOle : OleVariant;
var
  i, iRow, iCol : Longint;
  bm : TBookmark;
  FNodeData : PGridTreeData;
  FNode : PVirtualNode;
begin
  Result := VarArrayCreate([0, RowCount - 1,
                            0, ColCount - 1], varVariant);

  {$REGION 'FAdvStringGridLink'}
  if Assigned(FAdvStringGridLink) then
  begin
    for iCol := 0 to ColCount - 1 do
    begin
      for iRow := 0 to RowCount - 1 do
      begin
        Result[iRow, iCol] := FAdvStringGridLink.Cells[iRow, iCol];
      end;
    end;
  end;
  {$ENDREGION}

  {$REGION 'FVirtualStringTreeLink'}
  if Assigned(FVirtualStringTreeLink) then
  begin
    with FVirtualStringTreeLink do
    begin
      FNode := GetFirst;

      for iRow := 0 to RowCount - 1 do
      begin
        FNodeData := GetNodeData(FNode);
        for iCol := 0 to ColCount - 1 do
        begin
          Result[iRow, iCol] := FNodeData^.Columns[iCol];
        end;

        FNode := GetNext(FNode);
      end;
    end;
  end;
  {$ENDREGION}

  {$REGION 'FDataSourceLink'}
  if Assigned(FDataSourceLink) then
  begin
    with FDataSourceLink.DataSet do
    begin
      DisableControls;
      bm := GetBookmark;
      First;
      iRow := 0;

      while (not Eof) do
      begin
        i := 0;
        for iCol := 0 to FieldCount - 1 do
        begin
          if Fields[iCol].Visible then
          begin
            Result[iRow, i] := Fields.Fields[iCol].AsString;
            inc(i);
          end;
        end;

        Next;
        inc(iRow);
      end;

      GotoBookmark(bm);
      FreeBookmark(bm);
      EnableControls;
    end;
  end;
  {$ENDREGION}
end;
RowCount und ColCount wird auch so ermittelt.
Der OleVariant aus GetDataOle kann wie ein Stringgrid benutzt werden, was ich für zusätzliche Formatierungen ausnutze. z.B.:
Delphi-Quellcode:
...
          {$REGION 'Werte ggf. formatieren und dann einfügen —————————————————————————————'}
          if iMonat > -1 then
          begin
            for iRow := 0 to AData.RowCount - 1 do
              OleGrid[iRow, iMonat] := '01.' + OleGrid[iRow, iMonat];
          end;

          if (FExcelServer.Version.Major >= 11) then
          begin
            if iDatum > -1 then
            begin
              OleGrid[iRow, iDatum] := StrToDateTime(OleGrid[iRow, iDatum]);
            end;
          end;
...
          {$REGION 'Daten schreiben ——————————————————————————————————————————————————————'}
          FExcelServer.WriteRange(FStartRow,
                                  FStartCol,
                                  AData.RowCount,
                                  AData.ColCount,
                                  OleGrid);
          {$ENDREGION}
...
Ich benutzt übrigens Early-binding um wenigstens ein bisschen Kontrolle zu behalten. (Sollte man bei solchen Problemen immer dazu sagen, erleichter das Verständnis ungemein)

Um jetzt auf das Wiedersprechen zu kommen:
man kann natürlich die Werte einzeln nach Excel schreibe:
Delphi-Quellcode:
  {$REGION 'FVirtualStringTreeLink'}
  if Assigned(FVirtualStringTreeLink) then
  begin
    with FVirtualStringTreeLink do
    begin
      FNode := GetFirst;

      for iRow := 0 to RowCount - 1 do
      begin
        FNodeData := GetNodeData(FNode);
        for iCol := 0 to ColCount - 1 do
        begin
          Result[iRow, iCol] := FNodeData^.Columns[iCol]; // Hier kann man statt den Variant zu füllen auch gleich Excel füttern
        end;

        FNode := GetNext(FNode);
      end;
    end;
  end;
  {$ENDREGION}
Diese Variante ist aber um mehrere Größenordnungen langsamer!

Gruß David

Geändert von David Martens (15. Jul 2010 um 13:46 Uhr)
  Mit Zitat antworten Zitat