Delphi-PRAXiS

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 17. Apr 2018 06:59

Excel Stringgrid in (vorhandene) Excel abspeichern
 
Moin zusammen,

ich bin es mal wieder.

Ich hatte mir folgende Routine aus dem Netz geholt und etwas überarbeitet. Ich meine auch das sie bereits funktioniert hat, jetzt ist das Programm allerdings etwas größer geworden und nun funktioniert es nicht mehr (Die Daten werden nicht in Excel übernommen). Hat jemand evtl. eine Idee, denn eigentlich dürfte zusätzlicher Programmcode bei lokalen Variablen ja keinen Einfluss haben.

Wichtig hierbei: Die Exceldatei wird vorher eingelesen (funktioniert mittlerweile 1A) und in eben diese sollen die Daten zurück geschrieben werden beim drücken auf Speichern, da diese im Stringgrid editiert werden (teilweise automatisch).

Code:
//Excel abspeichern (aus Grid)
procedure TTools.SpeichernFClick(Sender: TObject);
var
  buttonSelected : Integer;
  logf : textfile;
begin
  // Show a custom dialog
  buttonSelected := messagedlg('Daten in Excel übetragen und Speichern?',mtCustom,
                              [mbYes,mbCancel], 0);
  // Show the button type selected
  if buttonSelected = mrYes   then
  begin
    //Grid in Excel & Status anzeigen
    HinweisF.Show;
    ExcelSheets.Pages[0].Show;
    StringgridToXLS(DE_XLS, 'DE', ExcelE.Text);
    HinweisF.Caption := 'DE gespeichert';
    ExcelSheets.Pages[ExcelSheets.ActivePageIndex + 1].Show;
   //...
    StringgridToXLS(Listen_XLS, 'Filiallisten', ExcelE.Text);
    HinweisF.Caption := 'Filiallisten gespeichert';
    ExcelSheets.Pages[ExcelSheets.ActivePageIndex + 1].Show;
    StringgridToXLS(CuR_XLS, 'C&R', ExcelE.Text);
    HinweisF.Caption := 'Alles gespeichert';
    ExcelSheets.Pages[0].Show;
    Timer2.Enabled := true;

    //Log schreiben
    assignfile(logf, ExtractFilePath(ParamStr(0)) + '\Log.txt');
    reset(logf);
    append(logf);
    writeln(logf,DateToStr(Now) + ' - '
               + TimeToStr(Now) + ' - '
               + split(GetCurrentUserName, '.', 0) + ' - '
               + ExcelE.Text + ' - gespeichert');
    closefile(logf);
    LogList.Items.Clear;
    LogList.Items.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'Log.txt');
  end;
end;

//Inhalt eines TStringGrid nach Excel exportieren
function TTools.StringgridToXLS(StringGrid : TStringGrid; sheetname : String; AXLSFile : string) : Boolean;
var
  Col      : Integer;
  Data     : OleVariant;
  XLApp    : OleVariant;
  MaxCol   : Integer;
  MaxRow   : Integer;
  Range    : OleVariant;
  Row      : Integer;
  Workbook : OleVariant;
  Worksheet : OleVariant;
  Value    : OleVariant;
begin
  Result := False;
  //Verbindung zu Excel herstellen
  XLApp := CreateOleObject('Excel.Application');
  try
    if not VarIsNull(XLApp) then
    begin
      //Workbook öffnen
      XLApp.Workbooks.Open(AXLSFile);
      XLApp.Visible := false;
      if not VarIsNull(Workbook) then
      begin
        //Maximalen Bereich bestimmen
        MaxCol := Min(StringGrid.ColCount, XLApp.Workbooks[ExtractFileName(AXLSFile)].WorkSheets[sheetname].Columns.Count);
        MaxRow := Min(StringGrid.RowCount, XLApp.Workbooks[ExtractFileName(AXLSFile)].WorkSheets[sheetname].Rows.Count);
        if (MaxRow > 0) and (MaxCol > 0) then
        begin
          //Worksheet auswählen
          Worksheet := XLApp.Workbooks[ExtractFileName(AXLSFile)].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;
      XLApp.Quit;
      XLAPP   := Unassigned;
      Value   := UnAssigned;
      Data    := UnAssigned;
      Range   := UnAssigned;
      Workbook := UnAssigned;
    end;
  end;
end;

//Hilfsfunktionen
function TTools.StringToVariant(const SourceString : string) : Variant;
var
  FloatValue : Extended;
begin
  if TryStrToFloat(SourceString, FloatValue) then
    //Result := FloatValue //außer Kraft gesetzt, da ich (aktuell) nur Strings brauche, auch bei Zahlenwerten wegen führender Null
    Result := SourceString
  else
    Result := SourceString;
end;

Jumpy 17. Apr 2018 08:00

AW: Excel Stringgrid in (vorhandene) Excel abspeichern
 
if not VarIsNull(Workbook) then

Workbook ist mMn an der Stelle Null weil nicht zugewiesen. Weiter oben hätte es wohl heißen müssen:

Workbook:=XLApp.Workbooks.Open(AXLSFile);

statt nur

XLApp.Workbooks.Open(AXLSFile);

-------

In der Folge könnte man dann auch statt:

Worksheet := XLApp.Workbooks[ExtractFileName(AXLSFile)].WorkSheets[sheetname]

einfach schreiben:

Workbook.WorkSheets[sheetname]

p80286 17. Apr 2018 08:11

AW: Excel Stringgrid in (vorhandene) Excel abspeichern
 
Was mir fehlt, ist eine Speicherung der übergebenen Daten und falls ich es richtig vermute, warum öffnest Du die bereits vorhandene Excel-Datei ein weiteres Mal?

Gruß
K-H

Moombas 17. Apr 2018 08:31

AW: Excel Stringgrid in (vorhandene) Excel abspeichern
 
@Jumpy: Da hast du recht, sehe ich genauso. Das ist aber der Teil, den ich unangetastet gelassen hatte. Aber auch eine Definition dessen ändert leider nichts. (Ein showmessage als Kontrolle, beweist zumindest das er die korrekten Werte ausliest für maxcol und maxrow).

@K-H: Was meinst du mit erneut öffnen? Die Exceldatei wird in die Stringgrids eingelesen (je Sheet ein Stringgrid). Und diese wird dann im Programm bearbeitet (manuell oder automatisch) und soll danach gespeichert werden können (wozu die oben genannte Routine genutzt werden soll).


Ich selber vermute, das er nicht die richtigen Daten zum speichern übergibt, kann aber aktuell den Denkfehler nicht finden.

...

Problem gefunden: Er übernimmt die richtigen Daten ABER bei der Speicherabfrage nimmt er quasi "nicht speichern". Wie kann ich ihm sagen, das er die Änderungen speichern soll?

KodeZwerg 17. Apr 2018 08:52

AW: Excel Stringgrid in (vorhandene) Excel abspeichern
 
Close Methode und alle weiteren Excel Dinge mit Workbooks.

Moombas 17. Apr 2018 09:11

AW: Excel Stringgrid in (vorhandene) Excel abspeichern
 
Dein Hinweis @ Codezwerg hat mir nicht wirklich geholfen, da dort kein Bezug auf das speichern genommen wird.

Habe nun woanders folgenden Code gefunden:

Code:
Workbook.saveas(AXLSFile, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, False, EmptyParam, EmptyParam, GetUserDefaultLCID);
Damit funktioniert es nun.

Jumpy 17. Apr 2018 09:21

AW: Excel Stringgrid in (vorhandene) Excel abspeichern
 
In StringgridToXLS machst du das Workbook auf, schreibst den Kram rein und machst es wieder zu wenn du XLApp.Quit aufrufst. Und du hast nirgendwo gespeichert.

Es hilft nicht in der Aufrufendene Prozedur zu speichern, dann ist schon alles wieder weg!

Deine Ganze Logik ist irgendwo komisch.

Du solltest in der Prozedur in die vom Einlesen noch geöffnete Excel-Datei schreiben und nicht jedes mal fürs reinschreiben die Datei wieder öffnen (die ja wahrscheinlich eh noch geöffnet ist).

KodeZwerg 17. Apr 2018 09:23

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

In diesem Beispiel werden alle geöffneten Arbeitsmappen geschlossen. Wurden an einer Arbeitsmappe Änderungen vorgenommen, fordert Microsoft Excel den Benutzer durch Anzeigen der entsprechenden Eingabeaufforderungen und Dialogfelder zum Speichern der Änderungen auf.
Ich dachte das es das ist was Du wolltest, tut mir leid wenn es nicht der Fall ist.

Moombas 18. Apr 2018 08:58

AW: Excel Stringgrid in (vorhandene) Excel abspeichern
 
@K-H: Bin gerade dabei deinem Tipp zu folgen und Excel bzw. die Datei früh zu öffnen (Programmstart) um sie später nicht immer öffnen und schließen zu müssen. Für das Einlesen der Excel funktioniert dies zwar, aber nicht für das speichern. Entweder bekomme ich die Rückmeldung im laufenden Programm "Ungültige varianten Operation" oder er schmeißt ab dem 2. Tabellenblatt alles falsch in Excel.

Ich habe folgenden Werte global deklariert:
XLApp -> Excel
AXLSFile -> Exceldatei
Workbook -> Arbeitsbuch

Und ich musste wegen eines Namenskonflikts auf openxml gehen, dann funktioniert aber meine obige Lösung nicht mehr. Weiß jemand wie ich eine Datei, die mit Openxml geöffnet wurde speicher?

Jumpy 18. Apr 2018 09:58

AW: Excel Stringgrid in (vorhandene) Excel abspeichern
 
Zeig doch mal etwas mehr Code. Vielleicht steht da jetzt noch was mit deiner alten Variante vermischt.

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.

Moombas 19. Apr 2018 08:31

AW: Excel Stringgrid in (vorhandene) Excel abspeichern
 
@Holger: mit deinem Tool in meinem integriert.
Delphi-Quellcode:
 if StringGridToXLS(FXLApp, DE_XLS, 'DE') then SaveExcel(FXLApp) else CloseExcel(FXLApp);
//...
@jumpy: Da ich die "extrahierte" Version von Holger nutze kommt die angesprochene Zeile so nicht mehr vor.


...ggf. mache ich es nachher testweise nochmal so, wie ich es ursprünglich gefunden hatte (https://www.delphi-treff.de/tipps-tr...l-exportieren/) und schaue dann ob ich es nochmal so hin bekomme, dann es dort raus zu nehmen.

p80286 19. Apr 2018 08:32

AW: Excel Stringgrid in (vorhandene) Excel abspeichern
 
Mit folgendem Schnipsel, kann man aus einem Stringgrid in eine Excel-Tabelle schreiben:
Delphi-Quellcode:
if forcestring then begin
      excel.activesheet.Cells.NumberFormat := '@';
  end;
  for i:=0 to sg.rowcount-1 do begin
    for j:=0 to sg.colcount-1 do begin
      zelle:=sg.cells[j,i];
      {----  Fehler 800A03EC bei mehr als 255 Spalten!}
      excel.activesheet.cells(i+1,j+1):=zelle; {cells1..x/1..y }
    end;
  end;
(um den Spaltenfehler hab ich mich damals leider nicht mehr gekümmert)

Damit wird auch 007 übernommen.

Gruß
K-H

Moombas 19. Apr 2018 09:11

AW: Excel Stringgrid in (vorhandene) Excel abspeichern
 
Es scheint ein Problem der Exceldatei zu sein.

Und zwar durch folgendes ausgelöst:

Exceldatei ("jungfräulich") mit den Daten gefüllt - ok
Exceldatei 1mal eingelesen - ok
Exceldatei 1mal gespeichert - ok (Eintragungen passen)
Exceldatei 2. Mal einlesen - Fehler -> Namenskonflikt (_Filterdatabase)
aus Exceldatei alle versteckten Namen entfernt mit folgendem Macro:
Code:
Sub EntferneNamen()
Dim i As Name
For Each DefName In ActiveWorkbook.Names
DefName.Delete
Next DefName
End Sub
Exceldatei 3. Mal einlesen - ok (Eintragungen passen, keine Fehlermeldung mehr)
Exceldatei 2. Mal speichern - alles durcheinander

Das Problem scheint also mit dem Namenskonflikt zu sein, denn wenn dieser nicht auftreten würde, bräuchte ich das Makro nicht auszuführen, welches scheinbar "zu viel" löscht und zu dem Fehler führt. -.-

Aktuell scheint die Lösung zu sein, Excel direkt noch zu speichern:
Delphi-Quellcode:
function StringGridToXLS(AXLApp : OleVariant; AGrid : TStringGrid; Sheetname : string):Boolean;
const
  xlCellTypeLastCell = $0000000B;
var
  Sheet: OLEVariant;
  MaxCol : Integer;
  MaxRow : Integer;
  Range : OleVariant;
  Data : OleVariant;

  Col : Integer;
  Row : Integer;
  R1,R2 : string;
begin
  Result := False;
  try
      //Worksheet auswählen
      if (Sheetname <> '') and HaveSheet(AXLApp, Sheetname) then
        begin
          Sheet := AXLApp.WorkSheets[sheetname];
        end else
          Sheet := AXLApp.WorkSheets[1];
      Sheet.select;

      // Der vorhandenen Daten im Grid, nicht der möglichen in Excel...
      MaxCol := AGrid.ColCount;
      MaxRow := AGrid.RowCount;

      if (MaxRow > 0) and (MaxCol > 0) then
      begin
        //Bereich auswählen
        R1 := RefToCell(1, 1);
        R2 := RefToCell(MaxCol, MaxRow);
        Range := Sheet.Range[R1, R2];
        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
                Data[Succ(Row), Succ(Col)] := AGrid.Cells[Col, Row];
              end;
            end;
            //Daten dem Excelsheet übergeben
            Range.Value := Data;
            Range.Columns.AutoFit;
            Result := True;
          end;
      end;
  finally
    SaveExcel(AXLApp);
    Range := UnAssigned;
    Sheet := Unassigned;
    Data := Unassigned;
  end;
end;

Moombas 20. Apr 2018 11:15

AW: Excel Stringgrid in (vorhandene) Excel abspeichern
 
So, ich arbeite nun mit dem Programm und seit der letzten Änderung (zwischendurch das Speichern) trat kein Namenskonflikt und auch sonst kein Problem mehr mit Excel auf :)

Danke für eure Hilfe und Unterstützung ;) Evtl. hilft der Thread auch anderen weiter, es war ja doch ein recht spezielles Problem.

Chemiker 21. Apr 2018 10:38

AW: Excel Stringgrid in (vorhandene) Excel abspeichern
 
Hallo Moombas,

ich will mal grundsätzlich die Frage stellen, ob Dein Vorgehen Zielführend ist.

Ich habe das so verstanden:
1. Excel-Datei wird in einem Stringgrid eingelesen
2. In dem Stringgrid werden einigen Daten verändert
3. Das Stringgrid mit den veränderten Daten wird zurück in die Excel-Datei geschrieben

Da die Synchronisierung zwischen Deinen Programm und Excel zu Problemen führen kann, stelle ich mal 2 Alternativen vor.

Was spricht dagegen, die Daten die verändert werden sollen direkt in Excel mit Hilfe Deines Programms zu ändern, ohne den Umweg über das Stringgrid.

Die andere Möglichkeit ist, die Daten aus der Excel-Datei in einem Stringgrid einzulesen, die Daten im Stringgrid zu verändern und anschließend in einer XLS-Datei ohne Excel zu speichern.

Bis bald Chemiker

Moombas 2. Mai 2018 12:54

AW: Excel Stringgrid in (vorhandene) Excel abspeichern
 
@Chemiker:

Der Umweg über das Stringgrid ist kein Umweg :) Die Daten der Exceldatei sollen über das Stringgrid geändert werden können und zusätzlich werden diese teilweise automatisiert geändert.

Und wie gesagt seit der letzten Änderung ist nur einmal ein Problem aufgetreten, welches aber wohl an einer anderen Stelle lag.


Alle Zeitangaben in WEZ +1. Es ist jetzt 04:24 Uhr.

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