Delphi-PRAXiS
Seite 2 von 3     12 3      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi Excel Stringgrid in (vorhandene) Excel abspeichern (https://www.delphipraxis.net/196031-excel-stringgrid-vorhandene-excel-abspeichern.html)

Moombas 18. Apr 2018 11:31

AW: Excel Stringgrid in (vorhandene) Excel abspeichern
 
Folgendes wäre der aktuell komplette Code, der mit Excel was zu tun hat. Das Problem tritt nur beim
Delphi-Quellcode:
Workbook.saveas(AXLSFile, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, False, EmptyParam, EmptyParam, GetUserDefaultLCID);
auf, dies scheint bei einer xml somit nicht zu funktionieren, da brauche ich eine Alternative:

Delphi-Quellcode:
unit Toolbox;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ComCtrls, Vcl.Grids, IniFiles,
  Vcl.FileCtrl, shellapi, ComObj, Vcl.ExtCtrls, Math, System.UITypes,
  Vcl.WinXCtrls, directorywatch;

type
  TTools = class(TForm)
   //...
    procedure FormCreate(Sender: TObject);
    procedure AbbrechenClick(Sender: TObject);
    procedure SpeichernEClick(Sender: TObject);
    Procedure FindDirs(DirPath: String; StringList:TStrings; Recurse: Boolean = false);
    procedure ProgramsClick(Sender: TObject);
    Procedure ListFiles(Box : TListbox);
    procedure ProgramsDblClick(Sender: TObject);
    procedure DataDblClick(Sender: TObject);
    function Split(text: string; delimiter: char; p: integer): string;
    procedure ManuellClick(Sender: TObject);
    procedure SuchePBClick(Sender: TObject);
    function CopyDir(SourceDirectory: string; DestinationDirectory: string): boolean;
    procedure KopierenClick(Sender: TObject);
    function Delete(const AFile: string): boolean;
    procedure FertigClick(Sender: TObject);
    function GetCurrentUserName: string;
    function Xls_To_StringGrid(AGrid: TStringGrid; Sheetname : string): Boolean;
    procedure EinlesenFClick(Sender: TObject);
    procedure GridColWidth(grd:TStringGrid;min,max:word);
    procedure SucheFBClick(Sender: TObject);
    procedure XLS_ManuellClick(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure DE_XLSClick(Sender: TObject);
    procedure SucheFEClick(Sender: TObject);
    procedure SuchePEClick(Sender: TObject);
    function StringgridToXLS(StringGrid : TStringGrid; sheetname : String) : Boolean;
    procedure SpeichernFClick(Sender: TObject);
    procedure SucheFEChange(Sender: TObject);
    procedure SuchePEChange(Sender: TObject);
    function RefToCell(Col, Row : Integer) : string;
    function StringToVariant(const SourceString : string) : Variant;
    function FindStrAndCount(const TargetString, Symbol: String): Integer;
    procedure LogListClick(Sender: TObject);
    procedure FilialenPSClick(Sender: TObject);
    procedure FilialStartChange(Sender: TObject);
    procedure FilialEndeEnter(Sender: TObject);
    procedure FilialStartClick(Sender: TObject);
    procedure NewLog(Path : string);
    procedure Timer3Timer(Sender: TObject);
    function ReportFileTimes(const FileName: string) : TDateTime;
    procedure LogEinlesenClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormDestroy(Sender: TObject);
  private
    { Private-Deklarationen }
    Watch: TDirectoryWatch;
    procedure OnNotify(const Sender: TObject; const Action: TWatchAction; const FileName: string);
  public
    { Public-Deklarationen }
  end;

var
  Tools                       : TTools;
  FileAction, LogName, LogDatei: String;
  olddate, newdate, FileChange : TDateTime;
  XLApp                       : OleVariant;

implementation

{$R *.dfm}

//Excel bei Programmende schließen
procedure TTools.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if not VarIsEmpty(XLApp) then
      begin
        XLApp.DisplayAlerts := false;
        XLApp.Quit;
        XLAPP   := Unassigned;
      end;
end;

//Grundeinstellungen laden
procedure TTools.FormCreate(Sender: TObject);
var
  Ini: TIniFile;
begin
  Ini:=TIniFile.Create(ExtractFilePath(ParamStr(0)) + '\Settings.ini');
  try
    InstallE.Text    := Ini.ReadString('Install', 'Ordner', 'nicht festgelegt!');
    ExcelE.Text      := Ini.ReadString('Install', 'Excel', 'nicht festgelegt!');
    OriginalE.Text   := Ini.ReadString('Install', 'Originale', 'nicht festgelegt!');
    DataEC.Checked   := Ini.ReadBool('Checkbox', 'Daten', False);
    LogEC.Checked    := Ini.ReadBool('Checkbox', 'Log', False);
    ErgebnisEC.Checked:= Ini.ReadBool('Checkbox', 'Ergebnis', False);
    ExcelC.Checked   := Ini.ReadBool('Checkbox', 'Excel', False);
    FertigEC.Checked := Ini.ReadBool('Checkbox', 'Fertig', False);

    DatenC.Checked   := DataEC.Checked;
    LogC.Checked     := LogEC.Checked;
    ErgebnisC.Checked := ErgebnisEC.Checked;
    FertigC.Checked  := FertigEC.Checked;

    KopierenE.Text   := InstallE.Text;
  finally
    Ini.Free;
  end;

  // Create Excel-OLE Object
  XLApp := CreateOleObject('Excel.Application');
  XLApp.Visible := False;
  XLApp.Workbooks.OpenXML(ExcelE.text);

  //Logdatei einlesen
  LogList.Items.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'Log.txt');

  //Programm zum arbeiten vorbereiten
  FindDirs(OriginalE.Text,Programs.Items);
  if ExcelC.Checked = true then EinlesenFClick(Sender);
  ExcelSheets.Pages[0].Show;
  Menu.Pages[1].Show;
end;

//Excel bei Programmabsturz schließen
procedure TTools.FormDestroy(Sender: TObject);
begin
  if not VarIsEmpty(XLApp) then
      begin
        XLApp.DisplayAlerts := false;
        XLApp.Quit;
        XLAPP   := Unassigned;
      end;
end;

//Excel in ein Stringgrid übertragen
function TTools.Xls_To_StringGrid(AGrid: TStringGrid; Sheetname : string): Boolean;
const
  xlCellTypeLastCell = $0000000B;
var
  Sheet: OLEVariant;
  RangeMatrix: Variant;
  x, y, k, r: Integer;
begin
  Result := False;
  x := 0;
  y := 0;
  try
    Sheet := XLApp.Workbooks[ExtractFileName(ExcelE.text)].WorkSheets[sheetname];
    // In order to know the dimension of the WorkSheet, i.e the number of rows
    // and the number of columns, we activate the last non-empty cell of it
    XLApp.Workbooks[ExtractFileName(ExcelE.text)].WorkSheets[sheetname].select;
    Sheet.Cells.SpecialCells(xlCellTypeLastCell, EmptyParam).Activate;

    x := XLApp.ActiveCell.Row;                  // Get the value of the last row
    y := XLApp.ActiveCell.Column;               // Get the value of the last column

    // Set Stringgrid's row &col dimensions.
    AGrid.RowCount := x + 4;
    AGrid.ColCount := y;

    RangeMatrix := XLApp.Range['A1', XLApp.Cells.Item[X, Y]].Value;    // Assign the Variant associated with the WorkSheet to the Delphi Variant

    //  Define the loop for filling in the TStringGrid
    k := 1;
    repeat
      for r := 1 to y do
        AGrid.Cells[(r - 1), (k - 1)] := RangeMatrix[K, R];
      Inc(k, 1);
      AGrid.RowCount := k + 1;
    until k > x;
    // Unassign the Delphi Variant Matrix
    RangeMatrix := Unassigned;

  finally
    // Quit Excel
    if not VarIsEmpty(XLApp) then
    begin
      XLApp.DisplayAlerts := false;
      Sheet := Unassigned;
      Result := True;
    end;
  end;
end;

//Inhalt eines TStringGrid nach Excel exportieren
function TTools.StringgridToXLS(StringGrid : TStringGrid; sheetname : String) : Boolean;
var
  Col     : Integer;
  Data    : OleVariant;
  MaxCol  : Integer;
  MaxRow  : Integer;
  Range   : OleVariant;
  Row     : Integer;
  Workbook : OleVariant;
  Worksheet : OleVariant;
  Value   : OleVariant;
begin
  Result := False;
  //Verbindung zu Excel herstellen
  try
    if not VarIsNull(XLApp) then
    begin
      //Workbook öffnen
      XLApp.Workbooks.OpenXML(ExcelE.Text);
      if not VarIsNull(Workbook) then
      begin
        //Maximalen Bereich bestimmen
        MaxCol := Min(StringGrid.ColCount, XLApp.Workbooks[ExtractFileName(ExcelE.Text)].WorkSheets[sheetname].Columns.Count);
        MaxRow := Min(StringGrid.RowCount, XLApp.Workbooks[ExtractFileName(ExcelE.Text)].WorkSheets[sheetname].Rows.Count);
        if (MaxRow > 0) and (MaxCol > 0) then
        begin
          //Worksheet auswählen
          Worksheet := XLApp.Workbooks[ExtractFileName(ExcelE.Text)].WorkSheets[sheetname];
          //Bereich auswählen
          Range := Worksheet.Range[RefToCell(1, 1), RefToCell(MaxCol, MaxRow)];
          if not VarIsNull(Range) then
          begin
            //Daten aus Grid holen
            Data := VarArrayCreate([1, MaxRow, 1, MaxCol], varVariant);
            for Row := 0 to Pred(MaxRow) do
            begin
              for Col := 0 to Pred(MaxCol) do
              begin
                Value := StringToVariant(StringGrid.Cells[Col, Row]);
                Data[Succ(Row), Succ(Col)] := Value
              end;
            end;
            //Daten dem Excelsheet übergeben
            Range.Value := Data;
            Range.Columns.AutoFit;
            Result := True;
          end;
        end;
      end;
    end;
  finally
  if not VarIsEmpty(XLApp) then
    begin
      XLApp.DisplayAlerts := False;
      Workbook.saveas(ExcelE.Text, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, False, EmptyParam, EmptyParam, GetUserDefaultLCID);
      Value  := UnAssigned;
      Data   := UnAssigned;
      Range  := UnAssigned;
      Workbook := UnAssigned;
    end;
  end;
end;

Jumpy 18. Apr 2018 12:28

AW: Excel Stringgrid in (vorhandene) Excel abspeichern
 
Was mir mMn auf die Schnelle aufgefallen ist:

Sowohl im Form.Create als auch in StringgridToXLS öffnest du jeweils die Datei. Einmal müsste doch eigentlich reichen?


Edit: Was man auch immer mal versuchen könnte: Visible und DisplayAlerts auf True setzen, dann sieht man wenigstens, was den Excel zu mecker hat.

Moombas 18. Apr 2018 13:22

AW: Excel Stringgrid in (vorhandene) Excel abspeichern
 
Excel selber hat gar nichts angezeigt.

Das mit dem 2'öffnen habe ich ich korrigiert. Auch Ist mir aufgefallen, das Workbooks wieder kein wert zugewiesen wurde und dies geändert. nun bekomme ich den Hinweis, das "saveas" vom automatisierungsobjekt nicht unterstützt wird. -.-

HolgerX 18. Apr 2018 14:30

AW: Excel Stringgrid in (vorhandene) Excel abspeichern
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hmm..

Um das hier mal abzukürzen...

Ein kleines Beispiel, basierend auf deinem Code, jedoch alle Excel-Sachen in eigene Unit verfrachtet und deutlich aufgeräumt.. ;)

In dem Beispiel wird ein Excel-Dokument (OpenDialog) geöffnet und das angegeben Sheet ein das Grid eingelesen.
Hierbei wird angenommen, das die erste ROW im Excel-Dokument die Header-Zeile ist.
(Musterdatei beigelegt)

Beim Speichern werden die Daten aus dem Grid ins Excel-Dokument geschrieben und dieses gespeichert.

Das eigentliche Schließen von Excel erfolgt erst im OnClose des Formulares.

(Erstellt mit D6)

Moombas 18. Apr 2018 15:02

AW: Excel Stringgrid in (vorhandene) Excel abspeichern
 
Auch mit deiner Unit habe ich nun das Problem, das er die Zellinhalte der Worksheets durcheinander schmeißt. Das hatte ich zwischenzeitlich auch, weiß aber aktuell nicht woran es liegt.

Das Problem mit der Namensgebung lag an der Excel und konnte ich beheben.

Dennis07 18. Apr 2018 17:18

AW: Excel Stringgrid in (vorhandene) Excel abspeichern
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ähm... mal ne simple Frage: Wieso nicht einfach das StringGrid als CSV speichern?
Das lässt sich in Excel problemlos (sogar problemloser als XLS) öffnen, bearbeiten, speichern, und erzeugt keinen Overhead, der eh nicht notwendig wäre. Denn TStringGrids unterstützen doch eh nur Strings soweit ich weiß.
Dann hättest du fast einen "Einzeiler", und null Probleme, selbst wenn Excel nicht installiert ist oder nicht gefunden wird (oder die API sich geänndert hat).

Delphi-Quellcode:
procedure LoadCSV(Lines: TStrings);
var
  Index: Integer;
  Cols: TArray<string>;
  I: Integer;
begin
  StringGrid1.RowCount := Lines.Count;
  for Index := 0 to Lines.Count - 1 do
  begin
    Cols := Lines.Strings[index].Split([';', ','], '"', '"');
    StringGrid1.ColCount := length(cols);
    for I := 0 to StringGrid1.ColCount - 1 do
    begin
      StringGrid1.Rows[index].Strings[I] := Cols[I];
    end;
  end;
end;
Das wäre zum Laden. Das Speichern ginge noch einfacher...

p80286 18. Apr 2018 22:30

AW: Excel Stringgrid in (vorhandene) Excel abspeichern
 
Zitat:

Zitat von Moombas (Beitrag 1399715)
Auch mit deiner Unit habe ich nun das Problem, das er die Zellinhalte der Worksheets durcheinander schmeißt. Das hatte ich zwischenzeitlich auch, weiß aber aktuell nicht woran es liegt.

Wie sieht das "Durcheinanderschmeißen" denn aus? wird der erste mit dem 2, der 3. mit dem 4. Datensatz vertauscht? oder sind Spalten vertauscht?
Verschwinden leere Zellen?


Gruß
K-H

Moombas 19. Apr 2018 06:48

AW: Excel Stringgrid in (vorhandene) Excel abspeichern
 
@Dennis07: Weil beim csv meines Wissens Formatierungen (also auch Farbvorgaben etc.) verloren gehen, die ich aber brauche. (Zu deiner Bemerkung mit den "strings": Das ist in Ordnung, ich möchte nur strings verwenden, selbst bei den Zahlen).

@K-H: Im Prinzip füllt er z.B. die erste Spalte nahezu komplett mit dem Wert aus der 2. Zeile. Wenn dort also "abc" steht, enthält nach dem speichern (beim einlesen ist dies noch korrekt) die erste Zeile in nahezu jeder Zeile "abc". Es hat ja schon mal funktioniert, daher wundere ich mich jetzt darüber.

Ich rufe es übrigens ohne OpenDialog auf, sondern direkt, da hier keine Dialoge etc. stattfinden sollen. Aber daran kann es ja eigentlich nicht liegen oder?

vorher (Eindeutige Zelleninhalte entfernt):
http://www.bilder-upload.eu/upload/4...1524118487.jpg

nachher (Eindeutige Zelleninhalte entfernt):
http://www.bilder-upload.eu/upload/b...1524118516.jpg

Es tauchen halt z.B. die Spaltenbeschriftungen innerhalb der Tabelle auch mehrfach auf, die Erste (und glaube zweite) Zeile ist auch noch korrekt, aber danach geht es halt los. Und alles was an Zahlen zu sehen ist, gehört halt eigentlich in die erste Spalte und zwar immer Zahl - 4 Felder frei - Zahl -... (in Excel sind hier 5 Zellen verbunden, das hat aber zwischenzeitlich einwandfrei funktioniert, stört also nicht). Aber den Zahlen fehlt hier die führende 0 (Die Zahlen müssen immer 4-Stellig sein, also theoretisch 0001 - 9999).

HolgerX 19. Apr 2018 08:03

AW: Excel Stringgrid in (vorhandene) Excel abspeichern
 
Hmm...

Zitat:

Zitat von Moombas (Beitrag 1399762)
Ich rufe es übrigens ohne OpenDialog auf, sondern direkt, da hier keine Dialoge etc. stattfinden sollen. Aber daran kann es ja eigentlich nicht liegen oder?

Nope, der Dialog gibt dir auch nur den Dateinamen zurück!

Zitat:

Zitat von Moombas (Beitrag 1399762)
Es tauchen halt z.B. die Spaltenbeschriftungen innerhalb der Tabelle auch mehrfach auf, die Erste (und glaube zweite) Zeile ist auch noch korrekt, aber danach geht es halt los. Und alles was an Zahlen zu sehen ist, gehört halt eigentlich in die erste Spalte und zwar immer Zahl - 4 Felder frei - Zahl -... (in Excel sind hier 5 Zellen verbunden, das hat aber zwischenzeitlich einwandfrei funktioniert, stört also nicht). Aber den Zahlen fehlt hier die führende 0 (Die Zahlen müssen immer 4-Stellig sein, also theoretisch 0001 - 9999).

1.)
Hast Du dein XLS-Dokument mit meinem Testtool geöffnet und gespeichert?
Oder nur mit deinem Tool?

2.)
Was Excel aus den übergebenen Daten macht, darauf hast Du nur dann Einfluss, wenn Du die Spalten/Cells/Ranges vor übergeben der Werte expliziert formatierst.

Excel geht immer hin und 'versucht' die übergebenen Daten 'selber' zu interpretieren.
Und da können auch Formatierungen verloren gehen.
Gerade bei Strings mit Zahlen, denn eine '0001' ist als Zahl nunmal '1'.
Wenn Du die führenden 000 haben willst, dann muss Du das Feld explizit als Text definieren.

3.)
Haben deine Eingaben im Stringgrid vielleicht Tabs oder #0?
Dann könnte dies Excel fehlinterpretieren und Spalten verschieben.

4.)
Geh mal hin und formatiere deine Strings aus dem Grid mal (zum Testen) zu AnsiString, wenn Du sie in die Range packst.

Jumpy 19. Apr 2018 08:05

AW: Excel Stringgrid in (vorhandene) Excel abspeichern
 
Das könnte daran liegen, dass das Stringrid 0-basiert und Excel 1-basiert seine Zeilen und Spalten zählt. Betrifft dann diesen Bereich:

Delphi-Quellcode:
      Range := Worksheet.Range[RefToCell(1, 1), RefToCell(MaxCol, MaxRow)];
          if not VarIsNull(Range) then
          begin
            //Daten aus Grid holen
            Data := VarArrayCreate([1, MaxRow, 1, MaxCol], varVariant);
            for Row := 0 to Pred(MaxRow) do
            begin
              for Col := 0 to Pred(MaxCol) do
              begin
                Value := StringToVariant(StringGrid.Cells[Col, Row]);
                Data[Succ(Row), Succ(Col)] := Value
              end;
            end;
            //Daten dem Excelsheet übergeben
            Range.Value := Data;
            Range.Columns.AutoFit;
            Result := True;
Desweiteren kann es hier ggf. Sinn machen, für den Excel-Teil Used Range zu benutzen:
MaxCol := Min(StringGrid.ColCount, XLApp.Workbooks[ExtractFileName(ExcelE.Text)].WorkSheets[sheetname].UsedRange.Columns.Count);
MaxRow analog.


Alle Zeitangaben in WEZ +1. Es ist jetzt 11:37 Uhr.
Seite 2 von 3     12 3      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz