Entwickler-Ecke

WinForms - Ich habe einen sehr großen Denkfehler...


Csharp-programmierer - Do 03.09.15 19:48
Titel: Ich habe einen sehr großen Denkfehler...
Hallo Leute. Leider stehe ich total auf dem Schlauch was mein Projekt angeht. Es handelt sich um meinen Editor, wo ich beliebig viele Steuerelemente hinzufüge. Ich kann zwar der neu erzeugten RichTextBox einen Namen geben, weiß aber nicht, wie ich sie ansteuern kann.

Erzeugen der Steuerelemente:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
string s = Path.Combine(Properties.Settings.Default.speichern, this.textBox1.Text);
            s += ".txt";
            string y = Path.ChangeExtension(s, ".txt");

            if (!File.Exists(y))
            {
                File.Create(y);
            }
            TabPage t = new TabPage(y);
            t.Name = y;
            this.tabControl1.Controls.Add(t);


            RichTextBox r = new RichTextBox();
            r.Name = y;
            r.Dock = DockStyle.Fill;
            t.Controls.Add(r);

            File.WriteAllText(y, r.Text);


Speichern des Projekts:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
int x = this.tabControl1.TabPages.Count;
            string[] y = Directory.GetFiles(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "XD"));
            for(int i = 0; i < x; i++)
            {
                foreach(string s in y)
                {
                    using(StreamWriter w = new StreamWriter(s))
                    {
                        //Hier muss ich ja die RichTextBoxen befüllen, komme aber nicht an die Namen ran.
                    }
                }
            }


Ich weiß allgemein nicht, welcher Name sinnvoll ist für beliebig viele RichTextBoxen. Ich habe es einfach mal mit einem Pfad versucht. Für bessere Vorschläge wäre ich dafür.

Nun kann ich ja nicht den Pfad.Text = streamWritrer2.ReadToEnd(); einsetzen, da das Programm nicht weiß, dass es sich hier um eine RTB handelt. Das Programm weiß ja nicht, welche RichTextBoxen erstellt werden. Ich hoffe ich versteht mein Problem.
MFG


jfheins - Do 03.09.15 20:42

Mach doch in deinem Formular eine private List<RichTextBox> in der du dir alle Steuerelemente merkst. Beim speichern kannst du dann durch diese Liste durchgehen.


Ralf Jansen - Do 03.09.15 20:43

Das speichern verstehe ich nicht. Wieso iterierst du über einen Ordner und suchst Dateien die XD heißen? Und willst dann mit dem Pfad zu diesen Dateien RichTextBoxen finden um dann in die Dateien zu schreiben? Wobei du in den TextBoxen Namen scheinbar Pfade auf *.txt Dateien verwaltest. Ohne Erklärung ergibt sich mir kein Sinn.
Zitat:
Ich weiß allgemein nicht, welcher Name sinnvoll ist für beliebig viele RichTextBoxen. Ich habe es einfach mal mit einem Pfad versucht. Für bessere Vorschläge wäre ich dafür.

Allgemein ist es am besten nicht auf die Namen angewiesen zu sein.
Du hast das z.B. so gebaut das die TabPages und die darauf liegende RichTextBox gleich heißen. Was dir zeigt Namen von Controls müssen nicht eindeutig sein. Irgendwas nach Namen zu finden ist also ein Problem. Man kann nicht nur ein Control (oder kein Control) finden sondern auch theoretisch mehrere. Die Namen sind eigentlich nur ein nettes Goodie für den Winforms Editor damit dort Controls auch einen anzeigbare Namen haben wenn man keine Variablen für die Controls benutzt.

Ein guter Ansatz ist Logik, UI und Daten nie in irgendeiner Form in Abhängigkeit zu bringen wie du es machst. Also Metadaten -> UI -> Metadaten. Schreibe dein Programm so das es theoretisch auch ohne UI auskommt bzw. die UI leicht austauschbar wäre dann stellen sich solche Problem gar nicht wie du sie gerade hast. Ganz praktisch, verwalte die geöffneten Dateien unabhängig von den Controls die du zur Anzeige dafür benutzt. Das kannst du z.B. tun in dem du einfach ein Dictionary benutzt in das du die Pfade reinschreibst und dazu einfach halt das dazugehörende Control (das funktioniert nur solange es natürlich nur ein Control gibt).

Also einfach irgendwo zentral das Dictionary anlegen.

C#-Quelltext
1:
Dictionary<string, Control> mappings = new Dictionary<string, Control>();                    

Dann wenn du bisher TabPage/RichTextBox erzeugst gibt denen irgendeinen Namen aber eben nichts woran Logik hängt und schreib ein entsprechendes Pfad -> Control mapping ins Dictionary.

C#-Quelltext
1:
2:
3:
4:
.... // dein erster gezeigter Code
t.Controls.Add(r);
mappings.Add(y, r);  // spätestens hier sollte Auffallen das deine Variablennamen echt besch.. sind
File.WriteAllText(y, r.Text); // dein Richtextbox ist initial leer warum schreibst einfach ein nichts weg? Die Zeile scheint überflüssig

beim speichern dann einfach über das Dictionary iterieren und die Sachen wegschreiben

C#-Quelltext
1:
2:
foreach (var mapping in mappings)
    File.WriteAllText(mapping.Key, mapping.Value.Text);


icho2099 - Do 03.09.15 21:51

Kann es sein, dass du Name des controls mit Name der Variablen für das controls verwechselst?
Dynamisch erzeugte controls musst du dir schon selber verwalten. Praktisch dazu in einer dynamischen Liste, in der du dann die controls über den Index direkt ansprechen kannst.
Wenn du über die Liste iterierst kannst du auch das control über seinen Namen finden.


Csharp-programmierer - Fr 04.09.15 12:27

Vielen Dank für die Antworten. Doch nun tritt andauern ein Fehler auf:

TabPage hinzufügen:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
  string s = Path.Combine(Properties.Settings.Default.speichern, this.textBox1.Text);
            s += ".txt";
            string y = Path.ChangeExtension(s, ".txt");
            //Die TextBox enthält den Namen für die TabPage
            if (!File.Exists(y))
            {
                File.Create(y);
            }
            TabPage t = new TabPage(y);
            t.Name = y;
            this.tabControl1.Controls.Add(t);


            RichTextBox r = new RichTextBox();
            r.Dock = DockStyle.Fill;
            t.Controls.Add(r);
            Mapping.Add(y, r);

            File.WriteAllText(y, r.Text);


Projekt speichern:

C#-Quelltext
1:
2:
3:
4:
foreach(var s in Mapping)
            {
                File.WriteAllText(s.Key, s.Value.Text);
            }


Wenn ich nun eine TabPage hinzufüge, dann tritt folgender Fehler auf:

Zusätzliche Informationen: Der Prozess kann nicht auf die Datei "C:\Users\x\Desktop\XD\cy.txt" zugreifen, da sie von einem anderen Prozess verwendet wird.

Und markiert wird: File.WriteAllText(y, r.Text);

Leider komme ich auch hier nicht weiter. Mfg


Ralf Jansen - Fr 04.09.15 12:50

File.Create erzeugt einen FileStream hält also einen Handle auf die Datei offen. Auch wenn du die Rückgabe nicht weiter benutzt das Objekt existiert trotzdem und solange das nicht freigegeben wird kannst du über keinen anderen Weg die Datei beschreiben. Das wird zwar irgendwann freigegeben werden, denn niemand hat eine Variable dafür erzeugt und hat damit eine Referenz auf das Objekt, es ist also ein unbenutztes Objekt aber der Garbage Collector läuft halt unregelmäßig irgendwann im Zweifel immer zu spät. Hier zumindest nicht zufällig zwischen File.Create und File.WriteAllText.

Du hast zwei Optionen je nachdem was du für dein File möchtest. Soll es tatsächlich exclusiv sein. Also kein anderer kann mit den Dateien arbeiten während sie in deinem Programm geöffnet sind oder soll es nicht-exclusiv sein. Also die Datei nur gesperrt sein solange du tatsächlich aus ihr liest oder in sie rein schreibst?

Im ersten Fall merk dir das FileStream Object das aus File.Create kommt und erledige alle Operationen auf dieser Datei über diesen Filestream. Wenn du in der Anwendung das File schließt mußt du zu diesem Zeitpunkt eben den Filestream schließen. im zweiten Fall nach jeder Operation halt explizit die Verbindung zum File schließen.

File.WriteAllText öffnet und schließt die Datei selbst da mußt du nix tun aber File.Create gibt wie gesagt den Filestream zurück den du zum Besipiel sofort schließen könntest(z.B per File.Create(y).Dispose();) Ich verstehe aber immer noch nicht wieso du am Anfang die Datei schon leer erzeugst? Ich würde die dann erzeugen wenn ich die zum erstenmal tatsächlich speichere und nicht selbst vorher explizit. File.WriteAllText überschreibt zwar es erzeugt aber auch die Datei und schreibt dann rein wenn die noch nicht existiert haben sollte. Den ganzen Teil mit dem Test auf File Exists und dann eventuellem erzeugen und am Anfang einmal leer überschreiben per WriteAllText würde ich ersatzlos streichen. File.WriteAllText zu dem Zeitpunkt wenn du tatsächlich explizit was speichern willst sollte reichen.


Csharp-programmierer - Fr 04.09.15 13:28

Ich habe es ja auch mit der StreamWriter- Methode Probiert und es hat auch immer einen Fehler angezeigt. Gäbe es dazu auch eine Option?


Th69 - Fr 04.09.15 14:00

Falls du Ralf nicht richtig verstanden hast, entferne einfach die folgenden Zeilen:

C#-Quelltext
1:
2:
3:
4:
if (!File.Exists(y))
{
    File.Create(y);
}

Das ist unabhängig davon, ob du dann File.WriteAllText oder einen StreamWriter zum Beschreiben der Datei benutzt.