C# docx Datei Text suchen und ersetzen, danach Ausdruck

Sylar

Commander
Registriert
März 2011
Beiträge
3.065
Hallo,

ich möchte ein Word-Dokument bearbeiten in C# (Etiketten Ausdruck) und da muss ich die "Hausnummer 15" ersetzen mit der Eingabe vom textbox.
Im Word ist die "Hausnummer 15" aber mit einem Styleref hinterlegt und die anderen Etiketten sollten dann automatisch die Hausnummer 15 übernehmen. Das funktioniert ja aktuell wenn ich es im Word bearbeiten möchte.
Jetzt möchte ich aber eine GUI Basteln, wo eine Start- und Endnummer gibt und diese dann fortlaufend verändert und diese dann ausgedruckt wird. zB 16-18
zB Hausnummer 16 = 10 Seiten
Hausnummer 17 = 10 Seiten
Hausnummer 18 = 10 Seiten

Diese soll er dann zum Drucker senden.

Code:
using Word = Microsoft.Office.Interop.Word;

private void btnDrucken_Click(object sender, EventArgs e)
        {
            string filePath = @"C:\Users\Ausdruck.docx";
            string searchString = "Hausnummer 15";
            string replaceString = txtbxStart.Text;

            ReplaceTextInWordDocument(filePath, searchString, replaceString);
        }

public static void ReplaceTextInWordDocument(string filePath, string searchText, string replaceText)
        {
            
            Word.Application wordApp = null;
            Word.Document doc = null;

            try
            {
                // 1. Word Anwendung starten
                wordApp = new Word.Application();
                wordApp.Visible = false; // Optional: Anwendungsfenster nicht anzeigen

                // 2. Dokument öffnen
                object file = filePath;
                object nul = Type.Missing;
                doc = wordApp.Documents.Open(ref file, ref nul, ref nul,
                    ref nul, ref nul, ref nul, ref nul, ref nul,
                    ref nul, ref nul, ref nul, ref nul, ref nul,
                    ref nul, ref nul, ref nul);

                // 3. Suchen und Ersetzen
                               
                    Word.Find findObject = wordApp.Selection.Find;
                    findObject.ClearFormatting();
                    findObject.Text = searchText;
                    findObject.Replacement.ClearFormatting();
                    findObject.Replacement.Text = replaceText;

                
                    object replaceAll = Word.WdReplace.wdReplaceAll;
                    findObject.Execute(ref nul, ref nul, ref nul, ref nul, ref nul,
                        ref nul, ref nul, ref nul, ref nul, ref replaceAll,
                        ref nul, ref nul, ref nul, ref nul, ref nul);
                              

                // 4. Dokument speichern und schließen
                object saveChanges = Word.WdSaveOptions.wdSaveChanges;
                doc.Close(ref saveChanges, ref nul, ref nul);
                wordApp.Quit(ref saveChanges, ref nul, ref nul);
                MessageBox.Show("Erleidgt");
            }
            catch (Exception ex)
            {
                MessageBox.Show("Fehler");
                //Console.WriteLine($"Fehler: {ex.Message}");
            }
            finally
            {
                // Ressourcen freigeben
                if (doc != null)
                {
                    System.Runtime.InteropServices.Marshal.ReleaseComObject(doc);
                    doc = null;
                }
                if (wordApp != null)
                {
                    System.Runtime.InteropServices.Marshal.ReleaseComObject(wordApp);
                    wordApp = null;
                }
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }

Aktuell findet er die "Hausnummer 15" und ersetzt sie aber mit "2". Zwar ersetzt er alle anderen Etiketten, was ja richtig ist.
Die Variabel replaceText übernimmt den Wert Hausnummer 16, nur wird diese im Word Dokument nicht übernommen und es erscheint eine 2 in den Etiketten.

Wo passiert hier der fehler?

Im weiteren hab ich noch eine "Feldfunktion" im Word, wo ein Kalender hinterlegt wird und ich wähle dort das Wunschdatum aus und die restlichen Etiketten werden auf das Wunschdatum geändert, wenn ich es drucken möchte.

Diese möchte ich auch gerne in der GUI integrieren und er soll das Datum dann ändern
 
C# zu debuggen ist ja nicht schwer. Also was steht denn zur Laufzeit in der VAR replaceText?
 
@tRITON
Dier variablen übernimmt er das Richtig, aber er nimmt nicht die Variable sondern, irgendwie muss ich den StringreplaceText in ein Objekt umwandeln, damit ich es bei object replaceAll übergeben kann.

Ich glaube hier habe ich den Fehler drinnen.
Code:
 findObject.Replacement.Text = replaceText;

               
                    object replaceAll = Word.WdReplace.wdReplaceOne;
                    findObject.Execute(ref nul, ref nul, ref nul, ref nul, ref nul,
                        ref nul, ref nul, ref nul, ref nul, ref replaceAll,
                        ref nul, ref nul, ref nul, ref nul, ref nul);

@Enurian
Hab es jetzt mit wdReplaceOne ersetzt. Und er ersetzt mir jetzt das Richtige Wort, ABER mit dem falschen Ergebnis. Es steht jetzt überall 1 drinnen. anstatt "Hausnummer 18".
 

Anhänge

  • replaceall.png
    replaceall.png
    152,1 KB · Aufrufe: 84
Nochmal: du schreibst es in den falschen Parameter. Die Nummern 1/2 sind die Werte des enums.
 
Nur mal als Anmerkung: fuer so etwas (Programmieren / Report) gibt es egtl. sog. Reports, da kannst du Etiketten erstellen/designen und dann die "Platzhalter" bzw. Variablen fuellen und direkt auf einen Drucker drucken oder als PDF exportieren und musst nicht Word "missbrauchen".

Vorallem duerfte es deutlich performanter sein, aber ok, die Reports kosten was (und die Firmen haben ja oft kein Geld ;-)).

Beispiel: List and Label, DevExpress.
 
Mich wundert es eh, dass man nicht mit "using Microsoft.Office.Interop.Word;" es lösen kann. bzw. Microsoft keine Schnittstelle fürs suchen und ersetzen gibt für C#

Ich habe Aspose Words for .NET gefunden und das funktioniert, aber kostet halt enorm viel, das es sich nicht. lohnt.
Ich möchte die Etiketten nicht nocheinmal neugestalten.

Aktuell muss ich halt den Text ändern und immer ausdrucken. Beim Drucker muss ich es jedesmal bestätigen, dass es sich um Etiketten handelt und nicht um Papier
 
D
Sylar schrieb:
Mich wundert es eh, dass man nicht mit "using Microsoft.Office.Interop.Word;" es lösen kann. bzw. Microsoft keine Schnittstelle fürs suchen und ersetzen gibt für C#

Die Schnittstelle gibt es und nennt sich "Microsoft.Office.Interop.Word".

Code:

public static void ReplaceTextInWordDocument(string filePath, string searchText, string replaceText)
{
Word.Application wordApp = null;
Word.Document doc = null;

try
{
// 1. Word Anwendung starten
wordApp = new Word.Application();
wordApp.Visible = false;

// 2. Dokument öffnen
object file = filePath;
object nul = Type.Missing;
doc = wordApp.Documents.Open(ref file, ref nul, ref nul,
ref nul, ref nul, ref nul, ref nul, ref nul,
ref nul, ref nul, ref nul, ref nul, ref nul,
ref nul, ref nul, ref nul);

// 3. Gesamten Inhalt auswählen
doc.Content.Select();

// 4. Suchen und Ersetzen
Word.Find findObject = wordApp.Selection.Find;
findObject.ClearFormatting();
findObject.Text = searchText;
findObject.Replacement.ClearFormatting();
findObject.Replacement.Text = replaceText;

// Replace-Parameter richtig setzen
findObject.Execute(
FindText: searchText,
ReplaceWith: replaceText,
Replace: Word.WdReplace.wdReplaceAll
);

// 5. Dokument speichern und schließen
object saveChanges = Word.WdSaveOptions.wdSaveChanges;
doc.Close(ref saveChanges, ref nul, ref nul);
wordApp.Quit(ref saveChanges, ref nul, ref nul);
MessageBox.Show("Erledigt");
}
catch (Exception ex)
{
MessageBox.Show($"Fehler: {ex.Message}");
}
finally
{
// Ressourcen freigeben
if (doc != null)
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(doc);
doc = null;
}
if (wordApp != null)
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(wordApp);
wordApp = null;
}
GC.Collect();
GC.WaitForPendingFinalizers();
}
}

oder etwas simpler:

public static void ReplaceTextInWordDocument(string filePath, string searchText, string replaceText)
{
Word.Application wordApp = null;
Word.Document doc = null;

try
{
wordApp = new Word.Application { Visible = false };
doc = wordApp.Documents.Open(filePath);

// Range-basierter Ansatz (effizienter als Selection)
Word.Range range = doc.Content;
range.Find.Execute(
FindText: searchText,
ReplaceWith: replaceText,
Replace: Word.WdReplace.wdReplaceAll
);

doc.Save();
MessageBox.Show("Text erfolgreich ersetzt");
}
catch (Exception ex)
{
MessageBox.Show($"Fehler beim Ersetzen: {ex.Message}");
}
finally
{
doc?.Close();
wordApp?.Quit();

// COM-Objekte freigeben
if (doc != null) Marshal.ReleaseComObject(doc);
if (wordApp != null) Marshal.ReleaseComObject(wordApp);
}
}
 
  • Gefällt mir
Reaktionen: Sylar
Danke, habe die V1 genommen und es funktioniert. Habe noch einiges umgebaut, aber es funktioniert jetzt tadellos.
Ich gebe einen Start-, einen Stop-Wert und die Anzahl der Seiten und druckt es. Zumindest mal als PDF funktioniert es.
 
  • Gefällt mir
Reaktionen: dh9
Wenn ich das Word Dokument Drucke kommt Standardmäßig die Meldung
"Ihre Seitenränder sind ziemlich schmal. Möglicherweise werden Teile des Inhalts beim Drucken abgeschnitten. Möchten Sie trotzdem drucken?" Ich glaube diese Meldung liegt am Druckertreiber.

Wenn ich mein fertiges Programm ausführe, sehe ich die oben genannte Meldung nicht nicht. bzw. wenn ich Word auf Visible setze, dann zeigt er mir
"Bitte warten Sie einen Moment, Word muss zuerst das Drucke abschließen."
 
Wie kann ich die Abfrage von Word bestätigen im C#, damit er am Drucker druckt?
Oder wie kann ich diese Abfrage im Word abstellen, damit er druckt.

Weil jetzt funktioniert es ja nur als PDF. Aber wenn ich es an den Drucker sende (Konica Bizhub C3350), dann kommt es nur in die Warteschleife vom Drucker, aber der Druck wird nicht durchgeführt
 
Habe es endlich geschafft, aber ich glaub mein System ist überfordert, weil die Druckaufträge recht lange brauchen, bis sie zum Drucker gehen.
Der Nachteil ist, wenn ich zB Hausnummer 15-20 eingebe, dann öffnet er 6x MS Word und sendet es dann zum Drucker und irgendwie ist mein Notebook da etwas überfordert und es braucht eine Zeit, bis es abgearbeitet wird.

Ich habe ein Makro geschriebn, der mir die Abfrage in Word automatisch bestätigt bzw. gar nicht anzeigt, somit geht er gleich an den Drucker.
 
also wenn die Auftraege von Word (oder PDF, oder egtl. egal was) in der Windows Druckwarteschlange ankommen, ist deine Arbeit als Programmierer erledigt, du hast es sozusagen geschafft.

Und Windows war leider noch nie wirklich performant beim Drucken...
 
Das stimmt. Ich muss es mal paar mal Testen, wie gut es läuft oder ob ich es ändern muss. Man kommt halt später drauf, das manches nicht so schlau gelöst worden ist.

Mache es auch nur Hobbymäßig und bin kein richtiger Programmierer. Möchte halt mir die Arbeit ersparen, wiel es immer wieder kommt.

Es wäre halt effizienter, wenn ich Word einmal starte, das Dokument öffne, dann in der For-Schleife die Elemente Ersetze und ausdrucke.
 
das sollte gehen, indem du die Word.App nur einmal instanziierst und das Word.Document jedes mal neu instanziierst.

Hauptfunktion:

public static void ProcessMultipleWordDocuments(string[] filePaths, string searchText, string replaceText)
{
Word.Application wordApp = null;
try
{
wordApp = new Word.Application();
wordApp.Visible = false;

foreach (string filePath in filePaths)
{
ProcessSingleDocument(wordApp, filePath, searchText, replaceText);
}

MessageBox.Show("Alle Dokumente bearbeitet");
}
catch (Exception ex)
{
MessageBox.Show($"Fehler: {ex.Message}");
}
finally
{
if (wordApp != null)
{
object saveChanges = Word.WdSaveOptions.wdDoNotSaveChanges;
object nul = Type.Missing;
wordApp.Quit(ref saveChanges, ref nul, ref nul);
System.Runtime.InteropServices.Marshal.ReleaseComObject(wordApp);
wordApp = null;
}
GC.Collect();
GC.WaitForPendingFinalizers();
}
}

Search & Replace und Print:

private static void ProcessSingleDocument(Word.Application wordApp, string filePath, string searchText, string replaceText)
{
Word.Document doc = null;
try
{
object file = filePath;
object nul = Type.Missing;
doc = wordApp.Documents.Open(ref file, ref nul, ref nul, ref nul, ref nul, ref nul, ref nul, ref nul, ref nul, ref nul, ref nul, ref nul, ref nul, ref nul, ref nul, ref nul);

doc.Content.Select();
Word.Find findObject = wordApp.Selection.Find;
findObject.ClearFormatting();
findObject.Text = searchText;
findObject.Replacement.ClearFormatting();
findObject.Replacement.Text = replaceText;
findObject.Execute(FindText: searchText, ReplaceWith: replaceText, Replace: Word.WdReplace.wdReplaceAll);

doc.PrintOut();

object saveChanges = Word.WdSaveOptions.wdDoNotSaveChanges;
doc.Close(ref saveChanges, ref nul, ref nul);
}
finally
{
if (doc != null)
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(doc);
doc = null;
}
}
}

Aufruf bspw.:

string directoryPath = @"C:\Verzeichnis";
string[] docxFiles = Directory.GetFiles(directoryPath, "*.docx");

ProcessMultipleWordDocuments(docxFiles, "alter Text", "neuer Text");
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: Sylar
Vielen Dank.

Ist es theoretisch "einfacher"/übersichtlichter wenn man in der Methode eine weitere Aufruft wie bei deinem Beispiel?

Habe es so gelöst mit einer For-Schleife nach dem Word gestartet wird. Die For-Schleife hat mir dann kopf zerbrechen gemacht, weil ja der Wert geändert worden ist und nicht mehr "Haus 15" war sondern zB Haus 7 und er den Text nicht mehr ersetzt. Somit musste ich es am Ende wieder auf "Haus 15" zurücksetzen, was ich nicht so schön finde.

Muss es aber morgen noch einmal am Drucker testen, welche Fehler ich noch habe. Aber ich glaube es sieht gut aus.

Code:
public void ReplaceTextInWordDocument3(string filePath, string searchText, string replaceText, string searchDate, string replaceDate, int Seiten)
        {
            Word.Application wordApp = null;
            Word.Document doc = null;

            try
            {
                // 1. Word Anwendung starten
                wordApp = new Word.Application();
                wordApp.Visible = true;
                progressBar.Value = 10;

                // 2. Dokument öffnen
                object file = filePath;
                object nul = Type.Missing;
                doc = wordApp.Documents.Open(ref file, ref nul, ref nul,
                ref nul, ref nul, ref nul, ref nul, ref nul,
                ref nul, ref nul, ref nul, ref nul, ref nul,
                ref nul, ref nul, ref nul);
                Statuslabel.Text = "Dokument wird geöffnet...";
                progressBar.Value = 20;
                doc.Application.Run("AutoExec");

                //Start-Wert bis Stop-Wert für die For-Schleife
                for (int i=Convert.ToInt32(txtbxStart.Text); i<=Convert.ToInt32(txtbxStop.Text);i++)
                {

                    // 3. Gesamten Inhalt auswählen
                    doc.Content.Select();
                    Statuslabel.Text = "Dokument wird gelesen...";
                    progressBar.Value = 30;

                    // 4. Suchen und Ersetzen für Haus Wert
                    Word.Find findObject = wordApp.Selection.Find;
                    findObject.ClearFormatting();
                    findObject.Text = searchText;
                    findObject.Replacement.ClearFormatting();
                    findObject.Replacement.Text = "Haus 15";
                    Statuslabel.Text = "Haus wird gesucht...";
                    progressBar.Value = 40;

                    // Replace-Parameter richtig setzen für Haus Wert
                    findObject.Execute(
                    FindText: searchText,
                    ReplaceWith: "Haus " + Convert.ToString(i),
                    Replace: Word.WdReplace.wdReplaceAll);
                    Statuslabel.Text = "Haus wird gesucht und Ersetzt...";
                    progressBar.Value = 50;

                    //ReplaceDatum
                    Word.Find findDate = wordApp.Selection.Find;
                    findDate.ClearFormatting();
                    findDate.Text = searchDate;
                    findDate.Replacement.ClearFormatting();
                    findDate.Replacement.Text = replaceDate;
                    Statuslabel.Text = "Datum wird gesucht...";
                    progressBar.Value = 60;

                    // Replace-Parameter richtig setzen
                    findDate.Execute(
                    FindText: searchDate,
                    ReplaceWith: replaceDate,
                    Replace: Word.WdReplace.wdReplaceAll);
                    Statuslabel.Text = "Datum wird gesucht und Ersetzt...";
                    progressBar.Value = 70;

                    // 5. Dokument speichern und schließen
                    wordApp.DisplayAlerts = Word.WdAlertLevel.wdAlertsNone;

                    TimeSpan time = new TimeSpan(800);

                    doc.Application.Run("AutoExec"); //Deaktiviert die Meldung und Druckt das Dokument aus

                    doc.PrintOut(20, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Seiten); //Seiten ist die Anzahl wie viele gedruckt wird
                    Statuslabel.Text = "Es wird gedruckt...";
                    progressBar.Value = 80;

                    //Setzt den aktuellen Haus auf Haus 15 zurück, damit die Suche wieder von vorne beginnen kann
                    findObject.Execute(
                    FindText: "Haus "+ Convert.ToString(i),
                    ReplaceWith: "Haus 15",
                    Replace: Word.WdReplace.wdReplaceAll);

                }
                object saveChanges = Word.WdSaveOptions.wdDoNotSaveChanges;
                
                doc.Close(SaveChanges: Word.WdSaveOptions.wdDoNotSaveChanges);
                wordApp.Quit(saveChanges, ref nul, ref nul);
                progressBar.Value = 90;                
                Statuslabel.Text = "Fertig";
                progressBar.Value = 100;
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Fehler: {ex.Message}");
            }
            finally
            {
                // Ressourcen freigeben
                if (doc != null)
                {
                    System.Runtime.InteropServices.Marshal.ReleaseComObject(doc);
                    doc = null;
                }
                if (wordApp != null)
                {
                    System.Runtime.InteropServices.Marshal.ReleaseComObject(wordApp);
                    wordApp = null;
                }
                GC.Collect();
                GC.WaitForPendingFinalizers();
            }
        }
 
  • Gefällt mir
Reaktionen: dh9
Zurück
Oben