Laden...

Anzahl Checkbox checked als Wert für for Schleife

Erstellt von Tommylik vor 2 Jahren Letzter Beitrag vor 2 Jahren 1.617 Views
T
Tommylik Themenstarter:in
51 Beiträge seit 2019
vor 2 Jahren
Anzahl Checkbox checked als Wert für for Schleife

Guten Abend,

Ich komme einfach nicht weiter mit meinem Problem.

Damit bekomme ich die Gesamtanzahl der Checkboxen auf der tabPage.


CheckBox[] controls = tpFileSave.Controls.OfType<CheckBox>().ToArray();

Und controls.Length kann ich auch für die For Schleife nutzen.


private void BtnTest_Click(object sender, EventArgs e)
        {
            CheckBox[] controls = tpFileSave.Controls.OfType<CheckBox>().ToArray();        

            for (int i = 0; i < controls.Length; i++)
            {
                Control[] c1 = this.Controls.Find("txtSourceFilePathApp" + i, true);

                string sourceFilePath = c1[0].Text;
                
                Control[] c2 = this.Controls.Find("txtFileNameApp" + i, true);

                string fileName = c2[0].Text;

...
...


Ich brauche aber nicht die Gesamtanzahl der Checkboxen sondern die Anzahl der Checkboxen die Checked sind.
Und das möchte ich der For Schleife übergeben.

Hiermit bekomme ich den Wert weiß aber nicht wie ich das der Schleife übergeben kann,
weil ich daraus nicht Length entnehmen kann.


int i = tpFileSave.Controls.OfType<CheckBox>().Count(c => c.Checked);

Wer ist bereit mir bei meinem Problem zu helfen?

Vielen Dank

Grüße Tommylik

16.806 Beiträge seit 2008
vor 2 Jahren

Du verlierst leider kein Wort, ob Du WPF oder WinForms verwendest und hast auch nicht im entsprechenden Forenbereich den Thread erstellt.
Magst den Helfern nicht nen paar mehr Infos geben?

Meine Frage gleich vorweg: wieso verwendest Du nicht einfach Data Binding, was man immer tun sollte?

Das Zusammenbauen von Control-Namen ist eher so nen Anti-Ding. Und immer auch so nen kleiner Hinweis, dass man die Architektur / das Konzeptverständnis nich ganz so im Griff hat 😉[FAQ] Variablennamen zur Laufzeit zusammensetzen / Dynamisches Erzeugen von Controls

3.825 Beiträge seit 2006
vor 2 Jahren

Ich würde das mit einem if machen :


CheckBox[] checkboxes = ...;
for (int i = 0; i < checkboxes.Length; i++)
    if (checkboxes[i].Checked)
    {
         ....
    }

Grüße Bernd

Workshop : Datenbanken mit ADO.NET
Xamarin Mobile App : Finderwille Einsatz App
Unternehmenssoftware : Quasar-3

T
Tommylik Themenstarter:in
51 Beiträge seit 2019
vor 2 Jahren

Hallo Abt,

Vielen Dank für deine Antwort.

wieso verwendest Du nicht einfach Data Binding, was man immer tun sollte?

Jetzt meine Frage vorweg: Wie kommst du auf Data Binding wenn ich so wenig Infos gebe?

Tut mir Leid das ich den Thread im falschen Forenbereich geschrieben habe. Kannst du es ändern oder ändern lassen?
Damit ich dazu lerne warum ist es der falsche Forenbereich?

Also es ist eine WinForms. Ich habe ein tabControl mit 3 tabPages.
Die eine tabPage heißt tpFileSave. Auf diesem tabPage sind mehrer Textboxen untereinander angeordnet.
Vorne an erste Stelle ist immer eine Checkbox die diese Textboxen sozusagen aktiviert.
Die Anzahl der Schleifendurchgänge werden durch die angeklickten Checkboxen bestimmt.
Vorher hatte ich zu jeder Checkbox auch einen Button und somit auch die entsprechenden Methoden.
Bis auf einen Button habe ich alle anderen gelöscht. Jetzt soll das alles mit einem Button ablaufen.

Ich hoffe diese Info ist ausreichend.

Grüße Tommylik

P
441 Beiträge seit 2014
vor 2 Jahren

Jetzt meine Frage vorweg: Wie kommst du auf Data Binding wenn ich so wenig Infos gebe?

Die Antwort hat Abt dir gleich mitgegeben: Weil man das immer tun sollte 🙂

Mit Databinding würdest du jetzt einfach die ViewModel Instanzen zählen, bei denen die IsChecked Property true ist.

Verstehe ich richtig, dass du nur über die Checkboxen iterieren willst Checked sind, aber deren Index brauchst um Controlnamen zusammenzubauen?
Dann kannst du entweder die Lösung von BerndFfm nehmen oder auf etwas Linq zurückgreifen. Databinding wäre aber trotzdem zu empfehlen - damit würdest du die Logik auch anschaulicher bekommen.


CheckBox[] checkboxes = ...;
var boxes =  checkboxes.Select((checkbox, idx) => new { Index = idx, Box = checkbox }).Where(checkbox => checkBox.Box.Checked);
foreach (var box in boxes)
{

}

3.825 Beiträge seit 2006
vor 2 Jahren

Ich verstehe auch nicht was da im Code genau gemacht werden soll.

Ich denke das kann man viel eleganter oder einfacher machen.


string sourceFilePath = ""; if (cbSourceFile.Checked) sourceFilePath = txtSourceFilePath.Text;
string fileName = "";       if (cbFileNameApp.Checked) fileName = txtFileNameApp.Text;
...

Das wären 30 Zeilen bei 30 Checkboxen.

Grüße Bernd

Workshop : Datenbanken mit ADO.NET
Xamarin Mobile App : Finderwille Einsatz App
Unternehmenssoftware : Quasar-3

T
Tommylik Themenstarter:in
51 Beiträge seit 2019
vor 2 Jahren

Hallo Bernd,

Vielen Dank für deine Antwort.

Dein Beispiele schaue ich mir mal an mal sehen ob ich es vervollständigen kann.

@Papst

Auch dir vielen Dank für deine Hilfe.

Mit Databinding würdest du jetzt einfach die ViewModel Instanzen zählen, bei denen die IsChecked Property true ist.

Data Binding muss ich mir erstmal anschauen weiß ich noch gar nichts drüber.

Verstehe ich richtig, dass du nur über die Checkboxen iterieren willst Checked sind, aber deren Index brauchst um Controlnamen zusammenzubauen?

Ja:
Wie viele Checkboxen sind aktiviert und der Wert soll dann der For Schleife übergeben werden.


​for (int i = 0; i < "AnzahlCheckboxen.checked".Length; i++)

Nein:
Ich weiß jetzt nicht genau was du mit Controlnamen zusammenzubauen meinst aber
ich würde dazu sagen das der String-Wert aus der TextBox "txtSourceFilePathApp" + #Index der Checkbox die aktiviert ist, an die string Variable "sourceFilePath" übergeben wird.


Control[] c1 = this.Controls.Find("txtSourceFilePathApp" + i, true);
                string sourceFilePath = c1[0].Text;             

Hier nochmal der ganze Code.


private void BtnTest_Click(object sender, EventArgs e)
        {          
           ======================================
            Das kommt weg weil Gesamtanzahl kann ich nicht gebrauchen.
            CheckBox[] controls = tpFileSave.Controls.OfType<CheckBox>().ToArray();            
            
            ======================================

            for (int i = 0; i < "AnzahlCheckboxen.checked".Length; i++)
            {
                Control[] c1 = this.Controls.Find("txtSourceFilePathApp" + i, true);
                string sourceFilePath = c1[0].Text;                

                Control[] c2 = this.Controls.Find("txtFileNameApp" + i, true);
                string fileName = c2[0].Text;                

                Control[] c3 = this.Controls.Find("txtExtApp" + i, true);
                string ext = c3[0].Text; 

                ComponentResult cr = LeakResult();

                string praegeCodeNrFileName = cr.PraegeCode;
                string praegeCodeNrFolderName = cr.FolderName;          
                string targetPath = txtChooseTargetPath.Text;

                MoveFiles(sourceFilePath, targetPath, praegeCodeNrFolderName);
                DummyDatei(praegeCodeNrFolderName, praegeCodeNrFileName, fileName, targetPath, ext);
            }

        }

oder auf etwas Linq zurückgreifen

Ich denke wenn du auch schon Databinding empfiehlst sollte ich nicht mit Linq anfangen.
Was bedeuten eigentlich diese beiden Pfeile? Ich habe nachgelesen es sind Lambda Expression aber was sagen sie aus?

nochmal
@Bernd

Ich hoffe du verstehst es jetzt besser.

Vielen Dank nochmal für Eure Hilfe.

Grüße Tommylik

C
2.121 Beiträge seit 2010
vor 2 Jahren

Hiermit bekomme ich den Wert weiß aber nicht wie ich das der Schleife übergeben kann, weil ich daraus nicht Length entnehmen kann.

Ist nicht das i das du hier erhältst schon genau diese Anzahl?

Nächster Punkt. Dein Code sieht für mich aus als bräuchtest du nicht nur die Zahl der angehakten Checkboxen, sondern solltest auch wissen welche das sind.
Also solltest du trotzdem alle durchlaufen und in der Schleife aber nur diejenigen berücksichtigen die Checked sind.
Sonst würdest du bei einer angehakten Checkbox mit dem Zähler 1 in deine Schleife gehen und immer nur Textbox 0 ansprechen, egal welche Checkbox angehakt ist.

Statt dem Gebastel mit den Namen könntest du alle Checkboxen und Textboxen jeweils in ein Array oder eine Liste packen. Dann kannst du mit Index auf diese Elemente zugreifen und brauchst nicht ständig mit Controls.Find hantieren.

Dann irgendwann später wenn du verstanden hast was du da tust und wie man Listen, Array etc. benutzt, kannst du dir ansehen was Databinding und Linq ist.
Aber bitte alles der Reihe nach verstehen. Kein Arbeitgeber will von dir hören dass du weißt wie man Linq schreibt und deshalb nicht mehr wissen musst was ein Array ist.

C
55 Beiträge seit 2020
vor 2 Jahren

oder auf etwas Linq zurückgreifen

Ich denke wenn du auch schon Databinding empfiehlst sollte ich nicht mit Linq anfangen.
Was bedeuten eigentlich diese beiden Pfeile? Ich habe nachgelesen es sind Lambda Expression aber was sagen sie aus?

Mit den geposteten Linqquery kannst alle unchecked Checkbox aus dem Array herausfiltern, so dass nur noch die Checkboxen mit dem entsprechenden Index übrig bleiben, die checked sind.
Wenn dir die Lösung zu komplizierst ist, dann hat Bernd auch schon eine gepostet. Du kannst auch einfach über die Checkbox iterieren und prüfen ob eine Checkbox checked ist oder nicht nicht. Ein Objekt vom Typ Checkbox bsitzt eine boolean Variable Checked, die true oder false sein. Ist der Wert true ist die Checkbox markiert und false dann unmarkiert.

Ich würde das mit einem if machen :

  
CheckBox[] checkboxes = ...;  
for (int i = 0; i < checkboxes.Length; i++)  
    if (checkboxes[i].Checked)  
    {  
         ....  
    }  
  

Grüße Bernd

4.931 Beiträge seit 2008
vor 2 Jahren

Der ganze Ansatz mittels X per Designer erstellten Check- und TextBoxen ist schon falsch (unprofessionell). Stattdessen würde man passende Container-Controls (z.B. DataGridView oder TableLayoutPanel) benutzen oder aber ein User-Control (für eine "Zeile") dafür entwickeln und diese dann dynamisch erzeugen und in einer Liste ablegen (beachte wirklich den von Abt verlinkten Artikel dazu).

PS: @BerndFfm, dein 2. Code paßt nicht zu der Anforderung.

309 Beiträge seit 2020
vor 2 Jahren

Solche Magic Strings solltest du sowieso vermeiden, am besten mit den Methoden die oben genannt sind.
Änderst du im Designer einen Namen,... wird es erst zur Laufzeit krachen.

16.806 Beiträge seit 2008
vor 2 Jahren

Data Binding muss ich mir erstmal anschauen weiß ich noch gar nichts drüber.

Data Binding ist ein Grundkonzept.
In WinForms basteln leider viele ihre UI wie Du zusammen, statt eben solches zu verwenden, weil WinForms im Gegensatz zu WPF einen dazu nicht zwingt.
Trotzdem - wie Du hier an der Resonanz der Helfer auch erkennen kannst - ist Binding der richtige Weg, der Dir vor allem in der Zukunft viel Stress und Codeaufwand ersparen und viel Logik erleichtern wird.
Es bietet sich daher immer an die Konzepte der Technologie, die man verwendet, mal durchzulesen, bevor man loslegt 🙂

Da haben sich paar Leute wirklich Mühe gegeben Docs dazu zu schreiben.
Das darf man durchaus honorieren, indem man diese liest 🙂Datenbindung - Windows Forms .NET Framework

Klar, bei kurz hingeklatschen Mini-Tools wägt man immer ab, wie sauber man alles macht.
Aber ernst gemeinten Anwendungen sollte man das schon anwenden.

Ich weiß jetzt nicht genau was du mit Controlnamen zusammenzubauen meinst aber
ich würde dazu sagen das der String-Wert aus der TextBox "txtSourceFilePathApp" + #Index der Checkbox die aktiviert ist, an die string Variable "sourceFilePath" übergeben wird.

Und genau das macht man nicht. Deswegen hab ich Dir den Link gegeben, in dem erklärt wird, wieso das eine schlechte Idee ist.
Magst den nicht lesen, mh? 🙂

Ich denke wenn du auch schon Databinding empfiehlst sollte ich nicht mit Linq anfangen.
Was bedeuten eigentlich diese beiden Pfeile? Ich habe nachgelesen es sind Lambda Expression aber was sagen sie aus?

Linq / Lambda hat nichts mit Data Binding zutun sondern ist ein Syntaxzucker, mit dem sich Abfragen vereinfachen lassen.
Ist seit >15 Jahren in der C# Sprache enthalten und sollte wirklich zum absoluten Grundfähigkeiten gehören.

Wenn Du nicht weißt was die Pfeile bedeuten, dann les doch einfach nach. Dafür gibts doch Dokumentation.
Macht doch kein Sinn, dass wir Dir nun quasi die Sätze aus den Docs rauskopieren 🙂Sprachintegrierte Abfrage (Language-Integrated Query, LINQ) in C#
Lambda expressions - C# reference

3.825 Beiträge seit 2006
vor 2 Jahren

PS: @BerndFfm, dein 2. Code paßt nicht zu der Anforderung.

Ich habe erst nach längerem Studium des Source Codes verstanden dass er die Checked Checkboxes durchgehen will um dann den Namen eines Controls zusammenzubasteln um dann einen Wert zuzuweisen, mit if abgefragt.

Deshalb würde ich die Schleife und das Zusammenbasteln des Control Namens einfach weglassen und nur das if verwenden :

if (cbFileNameApp.Checked) fileName = txtFileNameApp.Text;

oder wenn das benutzt werden soll zum Verschieben von Dateien noch einfacher :

if (cbFileNameApp.Checked) MoveFiles(sourceFilePath, targetPath, praegeCodeNrFolderName);

Grüße Bernd

PS.: ich verstehe aber noch nicht genau was der Code oben genau machen soll. Es wäre gut wenn der Autor mal beschreiben würde was er überhaupt machen will.

Workshop : Datenbanken mit ADO.NET
Xamarin Mobile App : Finderwille Einsatz App
Unternehmenssoftware : Quasar-3

4.931 Beiträge seit 2008
vor 2 Jahren

Er hat X CheckBoxen mit jeweils zugehörigen TexteBoxen und möchte für jede 'checked' CheckBox eine Aktion ausführen (und da er sie anscheinend einzeln im Designer platziert und benannt hat, benutzt er dafür jetzt Controls.Find(...)).

Und alleine schon die Reihenfolge der CheckBoxen muß ja nicht zwingend zu den Indizes passen...

T
Tommylik Themenstarter:in
51 Beiträge seit 2019
vor 2 Jahren

Guten Morgen,

Ich sage mal Danke an alle die mir geschrieben haben.

@TH69

Stattdessen würde man passende Container-Controls (z.B. DataGridView oder TableLayoutPanel) benutzen

Ich habe mir die Mühe gemacht und habe ein TableLayoutPanel erstellt genauso wie ich es schon auf der tpSaveFile habe. Siehe Bilder.
Ich habe in beiden Fällen 4 Textboxen auf 10 Reihen mit der Checkbox vorne dran.
Damit das Tool überhaupt was machen kann, braucht man 1 aktivierte Zeile (Checkbox) mit den 4 ausgefüllten Textboxen.

Textbox1 = Applikationsname - Nur informativ, weil es möglich sein kann eine App mehrmals zu nutzen. Für den Ablauf uninteressant.
Textbox2 = Filename to save - Unter welchen Namen möchtest du die Dummy Dateien speichern.
Textbox3 = File SourcePath - Wo sind die Quelldateien die verschoben werden.
Textbox4 = Welche Dateiendung haben die Dateien.

So das muss man für jede Zeile machen die man aktiviert. Es ist ein Individuelles zusammenstellen der Strings.
Ein Beispiel von einer unserer Anlagen die 5 verschiedene Applikationen hat.
-Dadurch habe ich 5 unterschiedliche Dateinamen für die Dummy-Dateien

  • 5 Quellverzeichnisse
  • 3 verschiedene und 2 gleiche Dateiendungen.

Ob mit TableLayoutPanel oder ohne ich muss die Strings über die Textboxen zusammenstellen damit der Anwender
die Freiheit hat die Bezeichnungen zu nutzen wie er es möchte. Bei mir bekommen die Dummy Dateien den App-Namen.
Ein anderer Anwender möchte sie aber anders benennen dann soll er das dürfen.

Der ganze Ansatz mittels X per Designer erstellten Check- und TextBoxen ist schon falsch (unprofessionell).

In meinem Fall jetzt, wie kann mir das TableLayoutPanel dabei behilflich sein das es besser wird.

@ JimStark

Kannst du mir sagen was für dich Magic Strings sind damit ich weiß, was ich nicht tun sollte?

@ Abt

Magst den nicht lesen, mh? 🙂

Ich habe den gelesen und jetzt schon 2 Mal, nachdem TH69 das auch erwähnt hatte.
Und du hast bestimmt auch recht, dass das der beste Weg ist. Wie du schon gesagt hast,
gehört das, was ich hier zusammen bastle zu den hingeklatschen Tools.
Es sind um die 1500 Zeilen Spaghetti Code, weil ich alles 10 Mal angelegt habe. Jede Zeile die aktiviert wird, ruft eine Methode auf.
Aber egal dafür, das ich keine Ahnung habe, bin ich aber stolz darauf, was ich geschafft habe.

Trotzdem - wie Du hier an der Resonanz der Helfer auch erkennen kannst - ist Binding der richtige Weg, der Dir vor allem in der Zukunft viel Stress und Codeaufwand ersparen und viel Logik erleichtern wird.

Ich denke das es Angst ist das ich es nicht wieder zum Laufen bekomme, wenn ich das jetzt umbaue mit List-Controls, Linq und Data-Binding nutze was sehr kompliziert aussieht.
Das Programm ist wirklich Mini. Es verbindet sich mit der SPS. Bekommt von der SPS ein Signal.
Und verschiebt alle Dateien (aktivierte Checkbox), die im Quellverzeichnis sind in das Zielverzeichnis.

@ BerndFfm

Es wäre gut, wenn der Autor mal beschreiben würde, was er überhaupt machen will.

Das was ich zusammengebastelt habe funktioniert ja schon.
Ich wollte die 10 Methoden, die ich aufrufe auf eine Methode einkürzen.

Dafür wollte ich nur wissen wie ich die Anzahl der gecheckten Checkboxen mit Lenght in einer For-Schleife nutzen kann.


   
      for (int i = 0; i < "AnzahlCheckboxen.checked".Length; i++)                      


So das wärs jetzt vielen Dank für die Hilfe.

Grüße Tommylik

4.931 Beiträge seit 2008
vor 2 Jahren

Wie schon geschrieben, nützt dir die Anzahl der gecheckten CheckBoxen gar nichts, da du dann ja immer noch nicht die zugehörigen Indizes weißt (ich hoffe, du verstehst, wie unlogisch dies daher ist?).

Sind denn die 10 Reihen fix oder kann es auch mehr oder weniger geben?
Aber alleine schon das manuelle Erzeugen der einzelnen Controls im Designer ist doch viel zu aufwendig (insb. falls mal Änderungen erfolgen sollen).

Erzeuge eine Methode zum Hinzufügen einer Reihe (d.h. eine CheckBox und die vier TextBoxen - welche auch wieder mittels einer Methode implementiert sein sollte) und rufe dann in einer Schleife diese auf.

Wenn du nicht genau weißt, wie der Code dafür aussehen soll, dann schau dir in der bisherigen zugehörigen "designer.cs"-Datei den Code dafür an und kopiere ihn.

Und dann kannst du beim TableLayoutPanel die Methode GetControlFromPosition in einer Schleife aufrufen, um an die einzelnen Check- und TextBoxen zu gelangen (d.h. entsprechend casten).

Die im Designer erzeugten Sub-Controls löschst du dann wieder (außer evtl. das TableLayoutPanel).
So sollte der Gesamtcode dann wirklich minimal sein (und viel flexibler und wartbarer).

PS: Bei meinen Spielen (s. WinForms-Framework für (2-Personen) Karten-/Brettspiele) benutze ich ganz selten den Designer (nur für Dialoge oder UserControls), die Controls der MainForm erstelle ich immer dynamisch.

PPS: Magic Strings sind die hartcodierten Namen deiner Controls (d.h. wenn man im Designer mal den falschen Namen oder Index angibt, knallt dein Code).

16.806 Beiträge seit 2008
vor 2 Jahren

Aber egal dafür, das ich keine Ahnung habe, bin ich aber stolz darauf, was ich geschafft habe.

Das spricht Dir auch absolut niemand ab, das darf man ruhig sein 🙂

Die Helfer zeigen Dir hier, wie Du Dein Ziel besser / überhaupt lösen kannst - und dass der Ansatz von Dir vielleicht nicht der beste ist.
Aktuell hast Du ein Vorgehen (zB über den Designer) und schreibst halt sehr viel Code, der nicht notwendig ist bzw. sogar im Weg stehen kann.
Es lohnt sich also durchaus sich die entsprechenden Hinweise anzuschauen / zu verfolgen.

Kein Satz fällt hier, der Dich von der Zielerreichung abhalten soll; im Gegenteil 😉

T
Tommylik Themenstarter:in
51 Beiträge seit 2019
vor 2 Jahren

Hallo TH69,

Wie schon geschrieben, nützt dir die Anzahl der gecheckten CheckBoxen gar nichts, da du dann ja immer noch nicht die zugehörigen Indizes weißt (ich hoffe, du verstehst, wie unlogisch dies daher ist?).

Leider verstehe ich es nicht. Aber, wenn du sagst, es nützt mir nicht dann will ich den Weg gar nicht weiter verfolgen.

Sind denn die 10 Reihen fix oder kann es auch mehr oder weniger geben?
Aber alleine schon das manuelle Erzeugen der einzelnen Controls im Designer ist doch viel zu aufwendig (insb. falls mal Änderungen erfolgen sollen).

Ja die sind Fix. Aber du hast recht, es ist mühselige Feinarbeit.

Erzeuge eine Methode zum Hinzufügen einer Reihe (d.h. eine CheckBox und die vier TextBoxen - welche auch wieder mittels einer Methode implementiert sein sollte) und rufe dann in einer Schleife diese auf.
Wenn du nicht genau weißt, wie der Code dafür aussehen soll, dann schau dir in der bisherigen zugehörigen "designer.cs"-Datei den Code dafür an und kopiere ihn.

Die eine CheckBox und die vier TextBoxen kann ich mit einem Usercontrol machen oder ist das Usercontrol schon zu viel?
Die Methode "Steuerelemente_hinzufügen füttere ich mit dem Code aus dem Designer, um es mir leicht zu machen.
Das Aufrufen mit der Schleife verstehe ich nicht so richtig da ich ja nicht weiß, wo oder wie ich den Wert für den Schleifenzähler festlegen soll.

Und dann kannst du beim TableLayoutPanel die Methode GetControlFromPosition in einer Schleife aufrufen, um an die einzelnen Check- und TextBoxen zu gelangen (d.h. entsprechend casten).

Danke für den Link muss ich mir anschauen habe ich noch nicht gemacht.
Funktioniert die Methode auch noch wenn das TableLayoutPanel auf einem Tabcontrol platziert ist??

PPS: Magic Strings sind die hartcodierten Namen deiner Controls (d.h. wenn man im Designer mal den falschen Namen oder Index angibt, knallt dein Code).

Meinst du mit hartkodierten Namen, wenn ich einem Steuerelement einen anderen Namen gebe als der Designer?
z.B. button1 vom Designer und ich Schreibe btnVerschiebeDateien?

Vielen Dank nochmal für deine Hilfe.

Grüße Tommylik

4.931 Beiträge seit 2008
vor 2 Jahren

Beim TableLayoutPanel brauchst du kein UserControl, da ja jedes Control in eine eigene Zelle platziert wird (bei einem UserControl würde man eine Zeile davon erzeugen und dann jeweils in eine einspaltige TableLayoutPanel oder aber in ein Panel packen).

Und deine eigene CreateRow-Methode rufst du dann in einer Schleife 10 mal auf (am besten im Form-Konstruktor nach InitializeComponent!).
Du übergibst am besten dann die Referenz auf das TableLayoutPanel an diese Methode (oder nicht ganz so flexibel benutzt direkt das auf der TabPage platzierte TableLayoutPanel).

Wenn du dir unsicher mit dem Code dafür bist, dann kannst du ja mal deinen Versuch hier reinstellen.
Vom Prinzip aber einfach


void CreateRow(TableLayoutPanel tableLayoutPanel, int row)
{
  var checkBox = new CheckBox()
    {
         //Parent = TableLayoutPanel; // Edit: Merke gerade, daß dieses hier überflüssig ist, da es implizit durch SetCellPosition gesetzt wird
         // weitere Eigenschaften
    };
   
    panel.SetCellPosition(checkBox, new TableLayoutPanelCellPosition(0, row); // 0 ist der Spaltenindex (d.h. diesen dann in dem unteren Code entsprechend ändern!)

   // gleiches wie oben dann für die 4 TextBoxen
   // ...
]

Ob du die 10 Zeilen schon initial anlegst oder dynamisch in der Methode mußt du selbst entscheiden.

Ich hoffe, daß du dann dieses Prinzip verinnerlicht hast und bei einem nächsten Projekt (oder auch noch für andere Teile dieses Projekts) dann sofort diesen Weg benutzt, anstatt mühselig im Designer X-mal die gleichen Controls anzulegen.

Und dann wäre der nächste Schritt halt "Data Binding", d.h. daß man einem Control nur noch die Daten (als Liste o.ä.) gibt und dieses selbständig diese Daten anzeigt. Dafür eignet sich bei WinForms am besten das DataGridView - aber dann solltest du dafür ein eigenständiges Textprojekt zum Herumexperimentieren anlegen.

Viel Erfolg noch!

PS: Bzgl. der Logik: Wenn z.B. die erste und letzte CheckBox "gecheckt" sind (d.h. die Indizes 0 und 9), was nützt dir dann die Info, daß die Anzahl davon 2 ist? Die Schleife würde dann ja nur von 0 - 1 zählen.

PPS: Es geht bei den MagicStrings um die Angabe für die Controls.Find-Methode (da vom Compiler nicht überprüft werden kann, ob du die ganzen Controls auch so im Designer angelegt hast). Es gibt halt im Framework manche Methoden, welche man besser nicht (oder nur selten) nutzen sollte. Bei dieser Methode müssen ja jeweils immer alle auf der Form platzierten Controls auf Namensgleichheit überprüft werden - und das kostet auch Performance).

3.825 Beiträge seit 2006
vor 2 Jahren

Ich glaube ja immer noch dass Dir die Anzahl der angeklickten Checkboxen überhaupt nichts nützt.
Das was Du oben beschrieben hast würde ich so machen :


if (checkbox1.Checked) ExecLine(txtSourceFilePathApp1, txtFileNameApp1, txtExtApp1);
if (checkbox2.Checked) ExecLine(txtSourceFilePathApp2, txtFileNameApp2, txtExtApp2);
if (checkbox3.Checked) ExecLine(txtSourceFilePathApp3, txtFileNameApp3, txtExtApp3);
if (checkbox4.Checked) ExecLine(txtSourceFilePathApp4, txtFileNameApp4, txtExtApp4);
if (checkbox5.Checked) ExecLine(txtSourceFilePathApp5, txtFileNameApp5, txtExtApp5);
if (checkbox6.Checked) ExecLine(txtSourceFilePathApp6, txtFileNameApp6, txtExtApp6);
if (checkbox7.Checked) ExecLine(txtSourceFilePathApp7, txtFileNameApp7, txtExtApp7);
if (checkbox8.Checked) ExecLine(txtSourceFilePathApp8, txtFileNameApp8, txtExtApp8);
if (checkbox9.Checked) ExecLine(txtSourceFilePathApp9, txtFileNameApp9, txtExtApp9);
if (checkbox10.Checked) ExecLine(txtSourceFilePathApp10, txtFileNameApp10, txtExtApp10);

Wenn es unbedingt eine Schleife sein muss :


for (int i = 1; i < 11; i++)
    if (((CheckBox)this.Controls("checkbox1" + i.ToString()).)Checked)
        ExecLine(this.Controls("txtSourceFilePathApp" + i.ToString()), this.Controls("txtFileNameApp1" + i.ToString()), this.Controls("txtExtApp1" + i.ToString()));

oder


for (int i = 1; i < 11; i++) ExecLine(i);

Hier besteht die Gefahr dass wenn ein Controlname falsch geschrieben ist es erst zur Laufzeit einen Fehler gibt.

Ich bin immer für die einfache Lösung.

Grüße Bernd

Workshop : Datenbanken mit ADO.NET
Xamarin Mobile App : Finderwille Einsatz App
Unternehmenssoftware : Quasar-3

T
Tommylik Themenstarter:in
51 Beiträge seit 2019
vor 2 Jahren

Guten Abend,

@ TH69
Vielen Dank für die Menge an Informationen. Es ist sehr interessant und macht viel Spaß das jetzt rauszufinden.

Hier meine ersten geh versuche mit Dynamischen Laden. Ich habe die Checkbox und die vier Textboxen genommen.
Das TabelLayoutPanel habe ich mal weggelassen. Funktioniert noch nicht so richtig.
Es wird die Checkbox angezeigt und die letzte Textbox. Die 3 Textboxen da zwischen fehlen.

Ich dachte es liegt vielleicht daran das ich das nicht im Form-Konstruktor nach InitializeComponent aufrufe,
sondern mit Button aufgerufen habe. Aber das ist egal.


public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        // Variable Textboxen
        private TextBox[] myTextBoxes;
        
        private void button2_Click(object sender, EventArgs e)
        {
             CheckBox checkBox1 = new CheckBox();

            // 
            // checkBox1
            // 
            checkBox1.AutoSize = true;
            checkBox1.Location = new System.Drawing.Point(10, 115);
            checkBox1.Name = "checkBox1";
            checkBox1.Size = new System.Drawing.Size(15, 15);
            checkBox1.TabIndex = 1;
            checkBox1.Text = "checkBox1";
            checkBox1.UseVisualStyleBackColor = true;

            myTextBoxes = new TextBox[4];
            for (int i = 0; i < myTextBoxes.Length; i++)
            {

                // Neue TextBoxen erzeugen
                myTextBoxes[i] = new TextBox();

                // 
                // txtAppName
                // 
                myTextBoxes[i].Font = new System.Drawing.Font("Consolas", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
                myTextBoxes[i].Location = new System.Drawing.Point(33, 107);
                myTextBoxes[i].Name = "txtAppName" + i;
                myTextBoxes[i].Size = new System.Drawing.Size(250, 26);
                myTextBoxes[i].TabIndex = 2;

                // 
                // txtFileNameApp
                //                 
                myTextBoxes[i].Font = new System.Drawing.Font("Consolas", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
                myTextBoxes[i].Location = new System.Drawing.Point(292, 107);
                myTextBoxes[i].Name = "txtFileNameApp" + i;
                myTextBoxes[i].Size = new System.Drawing.Size(200, 26);
                myTextBoxes[i].TabIndex = 3;

                // 
                // txtSourceFilePathApp
                // 
                myTextBoxes[i].Font = new System.Drawing.Font("Consolas", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
                myTextBoxes[i].Location = new System.Drawing.Point(500, 107);
                myTextBoxes[i].Name = "txtSourceFilePathApp" + i;
                myTextBoxes[i].Size = new System.Drawing.Size(280, 26);
                myTextBoxes[i].TabIndex = 4;

                // 
                // txtExtApp
                // 
                myTextBoxes[i].Font = new System.Drawing.Font("Consolas", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
                myTextBoxes[i].Location = new System.Drawing.Point(788, 107);
                myTextBoxes[i].Name = "txtExtApp" + i;
                myTextBoxes[i].Size = new System.Drawing.Size(80, 26);
                myTextBoxes[i].TabIndex = 5;
                

                Controls.Add(checkBox1);
                // TextBox auf dem Formular plazieren
                Controls.Add(myTextBoxes[i]);

            }
        }
    }

Was ist falsch an diesem Code? Ich bekomme ja keinen Fehler. Irgendetwas mit dem Zeichnen passt da nicht.

@ BerndFfm

Vielen Dank auch dir für die ganzen Code Snippets.

Ich glaube ja immer noch dass Dir die Anzahl der angeklickten Checkboxen überhaupt nichts nützt.
Das was Du oben beschrieben hast würde ich so machen :

Ja, das ist richtig und hat ja TH69 auch schon gesagt.

Was bedeutet ExecLine? In Google finde ich nichts was mir das erklären könnte. Visual Studio kennt es auch nicht.

Vielen Dank an alle für die tolle Hilfe.

Grüße Tommylik

4.931 Beiträge seit 2008
vor 2 Jahren

Du verwendest für die Eigenschaften der 4 Textboxen ja immer dieselbe Variable myTextBoxes[i] (deren Werte du jeweils überschreibst), so daß du letztendlich 4 mal die letzte TextBox erzeugst.

Bei 4 individuellen TextBoxen (mit jeweils unterschiedlicher Position und Größe) solltest du 4 getrennte Aufrufe machen:


myTextBoxes = new TextBox[4];
for (int i = 0; i < myTextBoxes.Length; i++)
{
     // Neue TextBoxen erzeugen
     myTextBoxes[i] = new TextBox();
}

//
// txtAppName
//
myTextBoxes[0].Font = new Font("Consolas", 12F, FontStyle.Regular, GraphicsUnit.Point, (byte)0);
myTextBoxes[0].Location = new Point(33, 107);
//myTextBoxes[0].Name = "txtAppName" + i; // Name benötigst du für dynamisch erzeugte nicht mehr
myTextBoxes[0].Size = new Size(250, 26);
myTextBoxes[0].TabIndex = 2;

// ... analog für die weiteren TextBoxen [1]-[3]
// ...

for (int i = 0; i < myTextBoxes.Length; i++)
{
     // TextBox auf dem Formular plazieren
     Controls.Add(myTextBoxes[i]);
}

Eleganter ginge es noch mit einer eigenen Methode CreateTextBox(...) sowie den verschiedenen Positionen, Größen etc. in jeweils einem eigenen Array:


Point[] locations = { new Point(33, 107), /* ... */ };
Size[] sizes = { new Size(250, 26), /* ... */ };

for (int i = 0; i < myTextBoxes.Length; i++)
{
     myTextBoxes[i] = CreateTextBox(locations[i], size[i], /* ... */);

    Controls.Add(myTextBoxes[i]);
}

So hältst du Methoden und Daten getrennt voneinander.

PS: Mit ExecLine(...) ist einfach eine Methode gemeint, welche die Daten dann verarbeitet.
Ich halte den Code von @BerndFfm trotzdem für Frickelcode.

T
Tommylik Themenstarter:in
51 Beiträge seit 2019
vor 2 Jahren

Hallo TH69,

Ich war leider einige Zeit Arbeitstechnisch wo anders eingesetzt so das ich keine Zeit hatte.

Vielen Dank für deine letzte Antwort.
Das muss ich mir noch alles genauer anschauen und noch mehr lesen.

Aber ich habe jetzt erstmal deine Vorschlag mit dem Herumexperimentieren in angriff
genommen und will erstmal mehr mit Listen und Arrays machen.
Es bringt mir nicht Steuerelemente dynamisch erstellen zu können, wenn ich doch noch
nicht weiß wie ich z.B. alle Textboxen die auf einem TabPage sind in eine Liste bekomme.

Und ich denke das ist hier jetzt mein Problem.

Ich möchte folgendes mit einer Liste und for Schleife erledigen.


private void Test_Click(object sender, EventArgs e)
        {           
                 if (chkActivateLocalApp1.Checked)
                {
                    txtLocalAppName1.Enabled = true;                    
                }

                if (chkActivateLocalApp2.Checked)
                {
                    txtLocalAppName2.Enabled = true;                   
                }

                if (chkActivateLocalApp3.Checked)
                {
                    txtLocalAppName3.Enabled = true;                   
                }

                if (chkActivateLocalApp4.Checked)
                {
                    txtLocalAppName4.Enabled = true;                   
                }

                if (chkActivateLocalApp5.Checked)
                {
                    txtLocalAppName5.Enabled = true;                  
                }

                if (chkActivateLocalApp6.Checked)
                {
                    txtLocalAppName6.Enabled = true;                   
                }

                if (chkActivateLocalApp7.Checked)
                {
                    txtLocalAppName7.Enabled = true;                   
                }

                if (chkActivateLocalApp8.Checked)
                {
                    txtLocalAppName8.Enabled = true;                  
                }       
        }

Mein Versuch mit einer Liste und Schleife.
Wenn ich mit dem Debugger und Haltepunkte schaue sind alle Werte vorhanden wie es sein sollte
aber es wird kein Textfeld auf Enabled gesetzt.
Mit der Code Variante oben schon.

Wo ist mein Fehler??


private void Login_Click(object sender, EventArgs e)
        {
                 List<TextBox> txtLocalAppNameList = new List<TextBox>
                {
                new TextBox { Text = txtLocalAppName1.Text },
                new TextBox { Text = txtLocalAppName2.Text },
                new TextBox { Text = txtLocalAppName3.Text },
                new TextBox { Text = txtLocalAppName4.Text },
                new TextBox { Text = txtLocalAppName5.Text },
                new TextBox { Text = txtLocalAppName6.Text },
                new TextBox { Text = txtLocalAppName7.Text },
                new TextBox { Text = txtLocalAppName8.Text }
                };      

                CheckBox[] checkBoxes = new CheckBox[8];

                checkBoxes[0] = chkActivateLocalApp1;
                checkBoxes[1] = chkActivateLocalApp2;
                checkBoxes[2] = chkActivateLocalApp3;
                checkBoxes[3] = chkActivateLocalApp4;
                checkBoxes[4] = chkActivateLocalApp5;
                checkBoxes[5] = chkActivateLocalApp6;
                checkBoxes[6] = chkActivateLocalApp7;
                checkBoxes[7] = chkActivateLocalApp8;

                for (int i = 0; i < checkBoxes.Length; i++)
                {
                    if (checkBoxes[i].Checked == true)
                    {
                        txtLocalAppNameList[i].Enabled = true;                       
                    }
                }
        }

Mit dem 2. Code habe ich keinen Code mehr der sich widerholt aber dennoch viel zu viele Codezeilen.
Wie kann ich das besser machen?

Vielen Dank noch mal für deine Hilfe.

Grüße Tommylik

16.806 Beiträge seit 2008
vor 2 Jahren

Naja, Du erzeugst die Textboxen schließlich in der Methode auch immer neu und wirfst sie auch immer in eine neue Liste.
So funktioniert halt OOP nicht.

Musst schon mit Klassenvariablen arbeiten und das Zeug auch nur ein mal erzeugen.

4.931 Beiträge seit 2008
vor 2 Jahren

Das steht doch in dem von Abt verlinkten Artikel [FAQ] Variablennamen zur Laufzeit zusammensetzen / Dynamisches Erzeugen von Controls, wie man es richtig macht (sogar passendes Beispiel mit einer List<TextBox>) (unter "Wie kann man - alle Warnungen im Sinn - die Designer Elemente in ein Array übertragen?").

Und genauso kannst du es dann mit den CheckBoxen machen (eigenartigerweise ist dieser Code sogar korrekt, wenn auch nicht so elegant).

Und wie Abt schrieb, wenn du die Listen dann als Klassenmember anlegst (und im Konstruktor - nach InitializeComponent() - füllst), dann werden diese auch nicht jedesmal wieder neu angelegt.

Bei dir scheinen wirklich die Grundlagen der OOP noch nicht zu sitzen.

PS: Trotzdem ist das dynamische Anlegen der Elemente viel eleganter und auch einfacher, als Dutzende von Elementen mit dem Designer zu platzieren (und deren individuellen Eigenschaften zu setzen)...

T
Tommylik Themenstarter:in
51 Beiträge seit 2019
vor 2 Jahren

Hallo,

Vielen Dank für Eure Antworten.

Bei dir scheinen wirklich die Grundlagen der OOP noch nicht zu sitzen.

Ich habe bisher noch nichts gefunden das mir das erklären könnte.
90% aller Videos oder Tutoriell erklären das mit der Klasse Auto.

Und wie Abt schrieb, wenn du die Listen dann als Klassenmember anlegst (und im Konstruktor - nach InitializeComponent() - füllst), dann werden diese auch nicht jedesmal wieder neu angelegt.

Also nach dem InitializeComponent();
Ich habe das jetzt so umgesetzt:


public partial class MainForm : Form
    {      
         List<TextBox> txtLocalAppNameList = new List<TextBox>();   
         CheckBox[] checkBoxes;    

        public MainForm()
        {
            InitializeComponent();

                txtLocalAppNameList = new List<TextBox>();
                
                txtLocalAppNameList.Add(new TextBox { Text = txtLocalAppName1.Text });
                txtLocalAppNameList.Add(new TextBox { Text = txtLocalAppName2.Text });
                txtLocalAppNameList.Add(new TextBox { Text = txtLocalAppName3.Text });
                txtLocalAppNameList.Add(new TextBox { Text = txtLocalAppName4.Text });
                txtLocalAppNameList.Add(new TextBox { Text = txtLocalAppName5.Text });
                txtLocalAppNameList.Add(new TextBox { Text = txtLocalAppName6.Text });
                txtLocalAppNameList.Add(new TextBox { Text = txtLocalAppName7.Text });
                txtLocalAppNameList.Add(new TextBox { Text = txtLocalAppName8.Text });

               checkBoxes = new CheckBox[8]; 
               
                checkBoxes[0] = chkActivateLocalApp1;
                checkBoxes[1] = chkActivateLocalApp2;
                checkBoxes[2] = chkActivateLocalApp3;
                checkBoxes[3] = chkActivateLocalApp4;
                checkBoxes[4] = chkActivateLocalApp5;
                checkBoxes[5] = chkActivateLocalApp6;
                checkBoxes[6] = chkActivateLocalApp7;
                checkBoxes[7] = chkActivateLocalApp8;                     
        }

        private void Test_Click(object sender, EventArgs e)
        {      
                for (int i = 0; i < checkBoxes.Length; i++)
                {
                    if (checkBoxes[i].Checked == true)
                    {
                        txtLocalAppNameList[i].Enabled = true;                       
                    }
                }
          }

So funktioniert es auch nicht keine Ahnung warum.
Ist es wenigstens ein bisschen richtig, weil da würde ich mich echt freuen.
Man ist das kompliziert.

Grüße Tommylik

16.806 Beiträge seit 2008
vor 2 Jahren

Es ist zumindest bisschen richtiger, was OOP und die Referenzen betrifftt.
Machst das halt immer noch alles relativ statisch, was unnötig ist.

Und ehrlich gesagt ist das eigentlich nicht so super kompliziert, wenn Du das Grundprinzip von OOP und Referenzen verstehst.
Ich mein, im Endeffekt musst auch nur Abtippen was der Link, den wir Dir gegeben haben, so zeigt.

Wenn Du von allen Controls jeweils 8 Stück hast, dann erstell einfach eine For-Schleife mit 8 Durchgängen, die Dir die Controls dynamisch erstellen und auf der Form platzieren.
Über den jeweiligen Index kannst dann auf die Schwester-Controls (TextBox 8 = CheckBox 8) zugreifen; entweder über den Index selbst oder weil Du Dir das zB. in nem extra Dictionary speicherst.

Ich weiß auch ehrlich gesagt gerade nicht, wie man es noch einfacher erklären soll.

4.931 Beiträge seit 2008
vor 2 Jahren

Kannst du nicht einfach mal den (Teil vom) Artikel anschauen?
So soll der Code aussehen:


TextBox[] textBoxes = new TextBox[] { this.textBox1, this.textBox2, this.textBox3 };

bzw. als Klassenmember nur


textBoxes = new TextBox[] { this.textBox1, this.textBox2, this.textBox3 };

(bzw. beides als List<TextBox>)

Das wirst du jetzt ja wohl auf deine beiden Arrays übertragen können (also nix mit neuen TextBoxen anlegen, nur Referenzen eintragen!)...

Man ist das kompliziert.

Eigentlich nicht!

T
Tommylik Themenstarter:in
51 Beiträge seit 2019
vor 2 Jahren

Servus,

Vielen Dank für Eure Antworten.

Kannst du nicht einfach mal den (Teil vom) Artikel anschauen?

Habe ich, aber ich verstehe das Beispiel leider nicht wie du vielleicht gehofft hast.


TextBox[] textBoxes = new TextBox[] { this.textBox1, this.textBox2, this.textBox3 };

Keine Ahnung wo das hinkommt im Code, das steht in dem Beispiel nicht mit dabei.

Ich habe es jetzt anders gelöst.


public partial class MainForm : Form
    {
         List<TextBox> txtLocalAppNameList = new List<TextBox>();         

        public MainForm()
        {
            InitializeComponent();

            txtLocalAppNameList.Add(txtLocalAppName1);
            txtLocalAppNameList.Add(txtLocalAppName2);
            txtLocalAppNameList.Add(txtLocalAppName3);
            txtLocalAppNameList.Add(txtLocalAppName4);
            txtLocalAppNameList.Add(txtLocalAppName5);
            txtLocalAppNameList.Add(txtLocalAppName6);
            txtLocalAppNameList.Add(txtLocalAppName7);
            txtLocalAppNameList.Add(txtLocalAppName8);              
        }

        private void Test_Click(object sender, EventArgs e)
        {
                checkBoxes = new CheckBox[8];

                checkBoxes[0] = chkActivateLocalApp1;
                checkBoxes[1] = chkActivateLocalApp2;
                checkBoxes[2] = chkActivateLocalApp3;
                checkBoxes[3] = chkActivateLocalApp4;
                checkBoxes[4] = chkActivateLocalApp5;
                checkBoxes[5] = chkActivateLocalApp6;
                checkBoxes[6] = chkActivateLocalApp7;
                checkBoxes[7] = chkActivateLocalApp8;

                for (int i = 0; i < checkBoxes.Length; i++)
                {
                    if (checkBoxes[i].Checked == true)
                    {
                        txtLocalAppNameList[i].Enabled = true;
                    }
                }
        }

So funktioniert es wenigstens. Button klicken und alle Textboxen sind Enable.
Ich bin aber sehr enttäuscht ich dachte man könnte, wenn man mit Listen und Arrays arbeite, viel mehr Code Zeilen einsparen.
Meine If Konstruktion hat 42 Zeilen (siehe Beispiel oben). Jetzt sind es auch nicht viel weniger.
Na ja es funktioniert das ist die Hauptsache.

Vielen Dank für Eure Hilfe nochmal.

Grüße Tommylik

16.806 Beiträge seit 2008
vor 2 Jahren

Ich bin aber sehr enttäuscht ich dachte man könnte, wenn man mit Listen und Arrays arbeite, viel mehr Code Zeilen einsparen.

Da musst Du aber ehrlich gesagt mit / über Dich selbst enttäuscht sein; denn das liegt eher an Dir als an der Sprache.
Technologisch funktioniert das einwandfrei; Du beachtest halt aber nicht, wie man das generell macht.
Dein Code offenbart einfach, dass Dir noch die Grundlagen fehlen. Da musst einfach noch bisschen lernen, da ist kein Meister vom Himmel gefallen.

zB hat Dein Code nicht eine einzige Umsetzung zu [FAQ] Variablennamen zur Laufzeit zusammensetzen / Dynamisches Erzeugen von Controls, was für Dein Vorhaben aber wichtig ist.

Keine Ahnung wo das hinkommt im Code, das steht in dem Beispiel nicht mit dabei.

Dir fehlt da einfach noch das Verständnis, die Brücke aus Beispielen auf Deinen Code anwenden zu können.


TextBox[] textBoxes = new TextBox[] { this.textBox1, this.textBox2, this.textBox3 };

ist nichts anderes als Beispielcode. Du schreibst - ohne es zu merken - es halt mit der Liste.


txtLocalAppNameList.Add(txtLocalAppName1);
txtLocalAppNameList.Add(txtLocalAppName2);
txtLocalAppNameList.Add(txtLocalAppName3);
txtLocalAppNameList.Add(txtLocalAppName4);
txtLocalAppNameList.Add(txtLocalAppName5);
txtLocalAppNameList.Add(txtLocalAppName6);
txtLocalAppNameList.Add(txtLocalAppName7);
txtLocalAppNameList.Add(txtLocalAppName8);

Macht inhaltlich nichts anderes; halt wieder mit mehr Code.
Du könntest auch einfach folgendes schreiben, was dann dem Beispiel entspricht.


TextBox[] textBoxes = new TextBox[] { txtLocalAppName1, txtLocalAppName2, txtLocalAppName3, txtLocalAppName4, ... };

Aber wie gesagt; das ist einfach die Fähigkeit Beispielcode auf realen Code anwenden zu können.
Das kommt mit der Zeit, vor allem wenn man das Verständnis von Quellcode entwickelt und versteht, was man macht.
Das kann Dir leider niemand abnehmen.

3.825 Beiträge seit 2006
vor 2 Jahren

Hallo Tommy,

die Namen der Controls zur Laufzeit zusammensetzen, was Du ursprünglich machen wolltest, ist oft keine gute Lösung. Zum einen kann der Compiler nicht gut optimieren, zum anderen können Laufzeitfehler auftreten die der Compiler nicht beim Übersetzen merken kann.

Die Übertragung der angelegten Controls in ein Array, wie oben von Th69 vorgeschlagen, finde ich auch die beste Lösung : Anlegen der Controls im Designer und trotzdem Zugriff über eine Schleife.

Ich schreibe nochmal wie das aussehen kann :


TextBox[] tb = new TextBox[] { textBox1, textBox2, textBox3 };
CheckBox[] cb = new CheckBox[] { checkBox1, checkBox2, checkBox3 };
for (int i = 0; i < cb.Length; i++) tb[i].Enabled = cb[i].Checked;

Kürzer geht es kaum.

Wenn jemand eine Checkbox wieder auf unchecked setzt soll dann das zugehörige Textfeld wieder disabled werden ? Das fehlt in deinem Code.

Grüße Bernd

Workshop : Datenbanken mit ADO.NET
Xamarin Mobile App : Finderwille Einsatz App
Unternehmenssoftware : Quasar-3

T
Tommylik Themenstarter:in
51 Beiträge seit 2019
vor 2 Jahren

Hallo,

Vielen Dank für die Antworten und für den Code-Snippet.

Ich tue mich sehr schwer damit, weil ich auch nicht weiß wann was genau wo hin muss.
Jetzt habe ich wieder eine neue Variante. Funktioniert, aber ob das richtig ist weiß ich nicht.
Also nach den Programmier Regeln.


public partial class MainForm : Form
    {      
        public MainForm()
        {
            InitializeComponent();          
        }

        private void Test_Click(object sender, EventArgs e)
        {
                TextBox[] tb = new TextBox[] { txtLocalAppName1, txtLocalAppName2, txtLocalAppName3, txtLocalAppName4, 
                txtLocalAppName5, txtLocalAppName6, txtLocalAppName7, txtLocalAppName8 };

                for (int i = 0; i < cb.Length; i++)
                {
                  tb[i].Enabled = (cb[i].Checked || string.IsNullOrEmpty(tb[i].Text));                 
                }
        }

Wenn jemand eine Checkbox wieder auf unchecked setzt soll dann das zugehörige Textfeld wieder disabled werden ? Das fehlt in deinem Code.

Die Textboxen werden mit einem anderen Button (speichern) auf disable gesetzt wenn sie Text enthalten damit nichts geändert werden kann.
Das sind sozusagen Konfigurationstextboxen die einmal beschrieben werden.
Der TestButton wird ein Loginbutton der die Textboxen auf Enabled setzt wenn man Änderungen machen muss.

So funktioniert es wie ich es möchte. Ob es richtig ist und auch sinnvoll kann ich nicht beurteilen.

Mal eine Frage, wenn ich eine Liste erstelle und die Textboxen da rein schreibe kann ich doch über den Index auf die einzelnen
Textboxen zugreifen oder ?
Wenn ich mehrere Textboxen in einer Reihe habe plus eine Checkbox und daraus ein User Control machen würde
und dann dieses User Control (erstmal mit dem Designer) mehrmals untereinander auf der Form platziere und
dann diese User Controls in eine Liste packe, wie spreche ich dann die Textboxen in dem User Control an?
Und was wäre wenn da noch ein TabControl dazwischen liegt?

Ich werde das mal ausprobieren ich hoffe es klappt.

Ihr habt ja gesagt das ich noch einiges über die OOP lernen muss.
Es gibt sehr viel hier im I-Net und wie ich schon mal gesagt habe wird das meistens mit der Klasse Auto erklärt.
Ich werde hauptsächlich mit Verzeichnissen und Dateien arbeiten und verstehe nicht wie man OOP mit
solchen Elementen in Verbindung bringen kann. Ich hoffe ihr versteht mich was ich sagen möchte.
Ich habe schon einige Videos über OOP geschaut aber keines von den Videos kann mir behilflich sein
mit dem was ich machen möchte.
Gibt es vielleicht ein Buch was ich kaufen könnte mit dem ich OOP besser verstehen lerne?

Vielen Dank nochmal für Eure Hilfe.

Grüße Tommylik

T
Tommylik Themenstarter:in
51 Beiträge seit 2019
vor 2 Jahren

Servus,

So, ich habe noch weiter gemacht und musste feststellen das, was ich Euch beim letzten Mal geschrieben habe, ist Blödsinn,
weil ich so ja nur in dem Test_Click Event die Arrays nutzen kann.
Ich habe das jetzt mal so umgebaut das ich es auf die ganze Mainform anwenden konnte. Habe einige hundert Zeilen
Code herausschmeißen können, weil ich viele Schleifen nutzen konnte.
Ich hänge die Datei mal an, weil sie trotzdem ich sie verkleinert habe noch über 800 Zeilen hat.
Wenn Ihr möchtet, könnt Ihr mir mal Eure Meinung sagen.

Was könnten meine Fehler sein das ein User Control größer wird wie die Vorlage?
Beim Erstellen des User Controls hat es genau die gleichen Maße wie die Steuerelemente auf meiner Form.
(3 Textboxen und 2 Checkboxen in einer Reihe angeordnet.)
Ziehe ich dann, dass User Control aus der Toolbox auf die Form, ist es fast doppelt so groß.

Eine Frage hätte ich noch.
Es sind viele Zeilen Code für die Propperties.Settings in der angehängten Datei.
Könnte man das auch verkleinern? Oder sogar in eine Klasse auslagern?

Wenn ja, gehe ich weiter auf die Suche.

Vielen Dank für Eure Hilfe.

Grüße Tommylik

309 Beiträge seit 2020
vor 2 Jahren

Es sind viele Zeilen Code für die Propperties.Settings in der angehängten Datei.
Könnte man das auch verkleinern? Oder sogar in eine Klasse auslagern?

Eine elegantere Alternative wäre mit dem Microsoft.Extensions.Configuration-Namespace zuarbeiten. Das kann man dann mit dem Optionspattern machen Optionsmuster in .NET oder als anderes Beispiel

16.806 Beiträge seit 2008
vor 2 Jahren

Sorry um das zu sagen JimStark, und ich seh auch, dass das Dein Blog ist, den Du verlinkt hast, aber das ist kein gutes Beispiel.
Da sind Fehler drin wie zB static option naming, was ein Anti-Pattern ist 😉
Siehe auch Optionsmusterleitfaden für .NET-Bibliotheksautoren

4.931 Beiträge seit 2008
vor 2 Jahren

Hallo Tommylik,

wenn du dir die Settings.Designer.cs mal (mit einem Texteditor) anschaust, dann siehst du, daß intern einfach nur der Index-Operator Item[String] der Basisklasse ApplicationSettingsBase benutzt wird, d.h. diesen kannst du also einfach selber nutzen, z.B.


for (int i = 0; i< tbLocalAppName.Length; i++) // die Arrays solltest du besser im Plural benennen: tbLocalAppNames
    tbLocalAppName[i].Text = (string)Properties.Settings.Default["LocalAppName" + (i+1)]; // für "LocalAppName" etc. könntest du dir auch Konstanten anlegen

Edit: Alternativ dazu könntest du eine eigene "Settings.cs" anlegen (ruhig auch im Unterordner Properties) mit


namespace <ProjectNamespace>.Properties
{
    internal sealed partial class Settings
    {
       // ...
    }
}

welche die Funktionalität um Methoden erweitert, z.B.


public string GetLocalAppName(int nIndex)
{
   return (string)["LocalAppName" + (nIndex+1)];
}

und diese dann aufrufen:


tbLocalAppName[i].Text  = Properties.Settings.Default.GetLocalAppName(i + 1);

(ich benutze dafür auch immer using Properties;, damit ich Properties nicht mehr explizit schreiben muß).


Und auch deine Click-Methoden kannst du stark verkürzen, z.B.


var textBox = sender as TextBox;
if (textBox != null)
  if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
      textBox.Text = folderBrowserDialog1.SelectedPath;

(bwz. mittels sender is TextBox textbox, s.a. [FAQ] Casten aber richtig: Boxing/Unboxing - () / is / as / Pattern Matching)

PS: Auch deine Settings-Werte könntest du als Array ablegen (anstatt individuell) - aber das machen wir im nächsten Schritt, wenn du obigen Umbau erledigt hast...

T
Tommylik Themenstarter:in
51 Beiträge seit 2019
vor 2 Jahren

Hallo an alle,

Vielen Dank für Eure Antworten.

@TH69

Vielen Dank für die Code Beispiele. Dadurch sind nochmal ein haufen Codezeilen rausgeflogen.

wenn du dir die Settings.Designer.cs mal (mit einem Texteditor) anschaust, dann siehst du, daß intern einfach nur der Index-Operator Item[String] der Basisklasse ApplicationSettingsBase benutzt wird, d.h. diesen kannst du also einfach selber nutzen, z.B.

Die Items sind die "propertyName" die ich in der Settings.settings angelegt habe.

Ich habe jetzt für alle eine Schleife angelegt.


public void GetLocalPathConfigSettings()
        {
            this.txtChooseTargetPath.Text = Properties.Settings.Default.TargetPath;

            for (int i = 0; i < tbLocalAppNames.Length; i++)
            {
                tbLocalAppNames[i].Text = (string)Properties.Settings.Default["LocalAppName" + (i + 1)];
            }

            for (int i = 0; i < tbSourceFilePathLocalApps.Length; i++)
            {
                tbSourceFilePathLocalApps[i].Text = (string)Properties.Settings.Default["LocalAppFilePath" + (i + 1)];
            }

            for (int i = 0; i < tbSetPointFileLocalApps.Length; i++)
            {
                tbSetPointFileLocalApps[i].Text = (string)Properties.Settings.Default["SetPointLocalApp" + (i + 1)];
            }

            for (int i = 0; i < cbActivateLocalApps.Length; i++)
            {
                cbActivateLocalApps[i].Checked = (bool)Properties.Settings.Default["LocalAppAktiv" + (i + 1)];
            }

            for (int i = 0; i < cbActivateCode2LocalApps.Length; i++)
            {
                cbActivateCode2LocalApps[i].Checked = (bool)Properties.Settings.Default["ActivateCode2LocalApp" + (i + 1)];
            }         
          
        }

Für die Save Methoden auch.


public void SaveLocalPathConfigSettings()
        {
            Properties.Settings.Default.TargetPath = this.txtChooseTargetPath.Text;


            for (int i = 0; i < tbLocalAppNames.Length; i++)
            {
                Properties.Settings.Default["LocalAppName" + (i + 1)] = tbLocalAppNames[i].Text;
            }

            for (int i = 0; i < tbSourceFilePathLocalApps.Length; i++)
            {
                Properties.Settings.Default["LocalAppFilePath" + (i + 1)] = tbSourceFilePathLocalApps[i].Text;
            }

            for (int i = 0; i < tbSetPointFileLocalApps.Length; i++)
            {
                Properties.Settings.Default["SetPointLocalApp" + (i + 1)] = tbSetPointFileLocalApps[i].Text;
            }

            for (int i = 0; i < cbActivateLocalApps.Length; i++)
            {
                Properties.Settings.Default["LocalAppAktiv" + (i + 1)] = cbActivateLocalApps[i].Checked;
            }

            for (int i = 0; i < cbActivateCode2LocalApps.Length; i++)
            {
                Properties.Settings.Default["ActivateCode2LocalApp" + (i + 1)] = cbActivateCode2LocalApps[i].Checked;
            }

            Properties.Settings.Default.Save();
        }

Edit: Alternativ dazu könntest du eine eigene "Settings.cs" anlegen (ruhig auch im Unterordner Properties) mit
namespace <ProjectNamespace>.Properties

Das habe ich gemacht aber das war es schon dafür brauche ich sehr viel Zeit.
Mein Problem ist zu verstehen wo was hingehört.

Es gibt zum Beispiel die Regel das man aus einer Klasse nicht auf die Controls zugreifen soll sondern Events dafür nutzt.
Man ist das kompliziert.
In der SPS zum Beispiel wenn ein Baustein zu groß wird kann ich einfach einen Teil raus nehmen
und in einen anderen Baustein packen und den Baustein dann aufrufen.

Unter anderem bin ich dabei dies hier zu lesen.
[FAQ] Kommunikation von 2 Forms
und das hier.
Zugriff von anderer Klasse auf Window Control

Aber es wäre toll wenn ich es schaffen würde alles was mit den Properties.Settings zu tun hat in die Settings.cs auszulagern.

(ich benutze dafür auch immer using Properties;, damit ich Properties nicht mehr explizit schreiben muß).

Darüber finde ich kein Beispiel das mir das erklärt.

Und auch deine Click-Methoden kannst du stark verkürzen, z.B.

Da hast du mir ja die Arbeit netter Weise abgenommen vielen Dank.


private void ChooseSourcePathLocalApp_Click(object sender, EventArgs e)
        {
            var textBox = sender as TextBox;

            if (textBox != null)
            {
                if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
                {
                    textBox.Text = folderBrowserDialog1.SelectedPath;
                }
            }
        }

Aber vielen Dank für die tolle Hilfe und die Code-Snippets

Grüße Tommylik

4.931 Beiträge seit 2008
vor 2 Jahren

Ja, so sieht dein Code doch schon viel übersichtlicher aus.

Aber es wäre toll wenn ich es schaffen würde alles was mit den Properties.Settings zu tun hat in die Settings.cs auszulagern.

Den ganzen Code wirst du nicht direkt auslagern können, da du ja auf die Controls der Form-Klasse zugreifst. Du könntest jedoch das jeweilige Array als Methodenparameter übergeben, so daß der Aufruf nur noch so aussieht:


var settings = Properties.Settings.Default;

settings.SetControlTexts(tbLocalAppNames, "LocalAppName");
settings.SetControlTexts(tbSourceFilePathLocalApps, "LocalAppFilePath");
// ...
settings.SetCheckBoxes(cbActivateLocalApps, "LocalAppAktiv");
// ...

Die Methodenparameter für die ausgelagerten Methoden dann als Control[] bzw. CheckBox[] deklarieren und die Schleife entsprechend anpassen.
Und ähnliche Methoden dann zum Setzen der Settings-Werte, z.B. SaveControlTexts(...).

Alternativ oben in deiner Datei


namespace PDE
{
    using Properties; // <-- hier einfügen

    // ...
}

und dann nur noch Settings.Default benutzen, anstatt (jedesmal) Properties.Settings.Default (das meinte ich mit "damit ich Properties nicht mehr explizit schreiben muß").
Durch meine obige Variable settings habe ich jedoch auch den Code entsprechend verkürzt.

Im Designer für die Settings-Werte (Settings.Designer.cs) gibt es oben den Button "View Code", welcher eine entsprechende Settings.cs in deinem Projekt anlegt (mit etwas auskommentierten Code), in der du dann zusätzlich diese Methoden implementieren kannst.

PS: Zum Thema "Zugriff von anderer Klasse auf Window Control" kannst du dir auch mal meinen längeren Artikel Kommunikation von 2 Forms durchlesen.

T
Tommylik Themenstarter:in
51 Beiträge seit 2019
vor 2 Jahren

Hallo TH69,

Vielen Dank für deine tolle Hilfe.

PS: Zum Thema "Zugriff von anderer Klasse auf Window Control" kannst du dir auch mal meinen längeren Artikel Kommunikation von 2 Forms durchlesen.

Denn habe ich mir durchgelesen und deshalb denke ich das ich es nicht richtig umgesetzt habe.
Ich denke das mir der Eventhandler fehlt. Und wer weiß was sonst noch.
Was halt blöd ist, das es funktioniert trotzdem das ich es falsch umgesetzt habe.

Tut mir leid, dass es mit mir so schwierig ist.

Hier meine Umsetzung:

Settings.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace PDE.Properties
{
    internal sealed partial class Settings
    {
        
        internal void GetLocalAppNames(TextBox[] tbLocalAppNames, string v)
        {
            for (int i = 0; i < tbLocalAppNames.Length; i++)
            {
                tbLocalAppNames[i].Text = (string)Settings.Default[v + (i + 1)];
            }

            //throw new NotImplementedException();
        }

        internal void GetSourceFilePathLocalApps(TextBox[] tbSourceFilePathLocalApps, string v)
        {
            for (int i = 0; i < tbSourceFilePathLocalApps.Length; i++)
            {
                tbSourceFilePathLocalApps[i].Text = (string)Settings.Default[v + (i + 1)];
            }

            //throw new NotImplementedException();
        }

        internal void GetSetPointFileLocalApps(TextBox[] tbSetPointFileLocalApps, string v)
        {
            for (int i = 0; i < tbSetPointFileLocalApps.Length; i++)
            {
                tbSetPointFileLocalApps[i].Text = (string)Settings.Default[v + (i + 1)];
            }

            //throw new NotImplementedException();
        }

        internal void GetActivateLocalApps(CheckBox[] cbActivateLocalApps, string v)
        {
            for (int i = 0; i < cbActivateLocalApps.Length; i++)
            {
                cbActivateLocalApps[i].Checked = (bool)Settings.Default[v + (i + 1)];
            }

            //throw new NotImplementedException();
        }

        internal void GetActivateCode2LocalApps(CheckBox[] cbActivateCode2LocalApps, string v)
        {
            for (int i = 0; i < cbActivateCode2LocalApps.Length; i++)
            {
                cbActivateCode2LocalApps[i].Checked = (bool)Settings.Default[v + (i + 1)];
            }

            //throw new NotImplementedException();
        }       

        internal void SaveLocalAppNames(TextBox[] tbLocalAppNames, string v)
        {
            for (int i = 0; i < tbLocalAppNames.Length; i++)
            {
                Settings.Default[v + (i + 1)] = tbLocalAppNames[i].Text;
            }
            //throw new NotImplementedException();
        }

        internal void SaveSourceFilePathLocalApps(TextBox[] tbSourceFilePathLocalApps, string v)
        {
            for (int i = 0; i < tbSourceFilePathLocalApps.Length; i++)
            {
                Settings.Default[v + (i + 1)] = tbSourceFilePathLocalApps[i].Text;
            }
            //throw new NotImplementedException();
        }

        internal void SaveSetPointFileLocalApps(TextBox[] tbSetPointFileLocalApps, string v)
        {
            for (int i = 0; i < tbSetPointFileLocalApps.Length; i++)
            {
                Settings.Default[v + (i + 1)] = tbSetPointFileLocalApps[i].Text;
            }
            //throw new NotImplementedException();
        }

        internal void SaveActivateLocalApps(CheckBox[] cbActivateLocalApps, string v)
        {
            for (int i = 0; i < cbActivateLocalApps.Length; i++)
            {
                Settings.Default[v + (i + 1)] = cbActivateLocalApps[i].Checked;
            }
            //throw new NotImplementedException();
        }

        internal void SaveActivateCode2LocalApps(CheckBox[] cbActivateCode2LocalApps, string v)
        {
            for (int i = 0; i < cbActivateCode2LocalApps.Length; i++)
            {
                Settings.Default[v + (i + 1)] = cbActivateCode2LocalApps[i].Checked;
            }
            //throw new NotImplementedException();
        }      
    }
}

MainForm.cs



using PDE.Properties;


namespace PDE
{
    public partial class MainForm : Form
    {
        public void GetLocalPathConfigSettings()
        {
            this.txtChooseTargetPath.Text = Settings.Default.TargetPath;

            var settings = Settings.Default;

            settings.GetLocalAppNames(tbLocalAppNames, "LocalAppName");
            settings.GetSourceFilePathLocalApps(tbSourceFilePathLocalApps, "LocalAppFilePath");
            settings.GetSetPointFileLocalApps(tbSetPointFileLocalApps, "SetPointLocalApp");
            settings.GetActivateLocalApps(cbActivateLocalApps, "LocalAppAktiv");
            settings.GetActivateCode2LocalApps(cbActivateCode2LocalApps, "ActivateCode2LocalApp");          
     }


public void SaveLocalPathConfigSettings()
        {
            var settings = Settings.Default;

            settings.SaveLocalAppNames(tbLocalAppNames, "LocalAppName");
            settings.SaveSourceFilePathLocalApps(tbSourceFilePathLocalApps, "LocalAppFilePath");
            settings.SaveSetPointFileLocalApps(tbSetPointFileLocalApps, "SetPointLocalApp");
            settings.SaveActivateLocalApps(cbActivateLocalApps, "LocalAppAktiv");
            settings.SaveActivateCode2LocalApps(cbActivateCode2LocalApps, "ActivateCode2LocalApp");

            Settings.Default.TargetPath = this.txtChooseTargetPath.Text;          

            Settings.Default.Save();
        }

PS: Zum Thema "Zugriff von anderer Klasse auf Window Control" kannst du dir auch mal meinen längeren Artikel Kommunikation von 2 Forms durchlesen.

Ich will mal dieses Beispiel ausprobieren zum lernen:

Für das Beispiel, daß eine SubForm den Label-Text der MainForm aktualisieren soll (z.B. um einen Status anzuzeigen) sieht der Code dann so aus:


class SubForm : Form
{
  // ...

  public class TextEventArgs : EventArgs
  {
    public TextEventArgs(string text)
    {
      Text = text; // Eigenschaft setzen
    }

    public string Text { get; set; } // Text als Eigenschaft definieren
  }

  public event EventHandler<TextEventArgs> UpdateText; // Ereignis deklarieren

  void button_Click(object sender, EventArgs e)
  {
    // ...
    
    OnUpdateText(new TextEventArgs("Test")); // Ereignis-Methode aufrufen
  }

  protected virtual void OnUpdateText(TextEventArgs e)
  {
    EventHandler<TextEventArgs> ev = UpdateText;
    if (ev != null)
      ev(this, e); // abonnierte Ereignismethode(n) aufrufen
  }
}

Und nun muß im MainForm dann noch dieses Ereignis abonniert (zugewiesen) werden:


class MainForm : Form
{
  void button_Click(object sender, EventArgs e)
  {
    SubForm subForm = new SubForm();
    subForm.UpdateText += UpdateLabelText; // Ereignis abonnieren
    subForm.ShowDialog(this);
  }

  void UpdateLabelText(object sender, SubForm.TextEventArgs e)
  {
    labelText.Text = e.Text; // Zugriff auf Eigenschaft des Ereignisses
  }
}

Ich muss es doch schaffen diesen aha-Efekt zu bekommen.

Vielen Dank nochmal für deine besondere Hilfe.

Grüße Tommylik

4.931 Beiträge seit 2008
vor 2 Jahren

Mir scheint, du machst dir immer mehr Arbeit als nötig ist.
Du brauchst doch nicht für jedes konkrete Array eine eigene Methode (dafür sind doch die Parameter da), sondern (bisher) nur zwei verschiedene: für den Zugriff auf Text und Checked. Daher auch meine allgemeinen Namen: SetControlTexts und SetCheckBoxes.
(Du könntest jedoch weitere Hilfsmethoden erstellen, welche die Namen der Settings-Werte intern benutzen, um sie nicht beim Afurf kennen zu müssen - oder alternativ als String-Konstanten zur Verfügung stellen).

Und da du ja die Variable settings übernommen hast, kannst du diese auch für deine anderen Aufrufe (anstatt Settings.Default) benutzen - damit der Code einheitlich und übersichtlicher ist (und ein ganz, ganz kleiner Performance-Gewinn, da nicht jedesmal die Eigenschaft Default ausgelesen werden muß - aber das ist wirklich nur sekundär).

Und beim SubForm-Beispiel sollte der Aha-Effekt darin bestehen, daß diese SubForm keine Abhängigkeit zur MainForm hat, d.h. diese also nicht kennt.
So kannst du diese Klasse (bzw. Datei) auch von einer anderen Form aus aufrufen (oder sogar in einem anderen Projekt verwenden).

Und nach diesem Prinzip (Vermeidung von Abhängigkeiten bzw. Trennung von Zuständigkeiten) sollte möglichst jedes Projekt aufgebaut sein.

Ich hoffe, ich überfordere dich nicht zu sehr, aber es freut mich, daß du meine Tipps umsetzt. 😉

Bei deinem jetzigen Code könnte man noch mehr Code einsparen (optimieren), z.B. sind deine beiden Methoden ScanLocalPathes_Tick und ScanNetPathes_Tick doch inhaltlich fast identisch, so daß du auch daraus eine Methode mit entsprechenden Parametern erzeugen könntest.
Ähnlich einige deiner anderen Methoden bzw. Copy und Move bzw. Local und Net - dafür wäre es aber hilfreich, wenn du Delegates (welche die Grundlage von Ereignissen (event) sind) verstanden hast.

T
Tommylik Themenstarter:in
51 Beiträge seit 2019
vor 2 Jahren

Hallo TH69,

Vielen Dank für deine Antwort und da muss ich dir gleich antworten.

Mir scheint, du machst dir immer mehr Arbeit als nötig ist.

Das ist ja kein wunder da ich ja oft nicht weiß was ich tue.

Du brauchst doch nicht für jedes konkrete Array eine eigene Methode (dafür sind doch die Parameter da), sondern (bisher) nur zwei verschiedene: für den Zugriff auf Text und Checked. Daher auch meine allgemeinen Namen: SetControlTexts und SetCheckBoxes.

Ok aber damit ich es besser verstehe und damit ich weiß wo ich ansetzen muss, das was ich umgesetzt habe ist falsch?
Verstehe ich dich richtig das ich die ganzen Methoden in der Settings.cs auf 2 Methoden einkürzen kann?
Ich tue mich halt schwer damit das was du mir als Hilfe zur Verfügung stellst an den richtigen stellen einzusetzen.

Ich hoffe, ich überfordere dich nicht zu sehr, aber es freut mich, dass du meine Tipps umsetzt. 😉

Nein, aber es ist auch nicht gerade ein Kinderspiel. Ich möchte halt nicht nicht aufgeben. Es macht mir spaß.
Aber jetzt mal die Gegenfrage wie viel Geduld hast?

Bei deinem jetzigen Code könnte man noch mehr Code einsparen (optimieren),

Darüber sollten wir nachdenken wenn ich das Thema Klasse > Form verstanden habe.
Und so aufgebaut ist wie es sein sollte.

Vielen Dank für deine bisherige Hilfe, Mühe und Zeit.

Grüße Tommylik

16.806 Beiträge seit 2008
vor 2 Jahren

Verstehe ich dich richtig das ich die ganzen Methoden in der Settings.cs auf 2 Methoden einkürzen kann?
Ich tue mich halt schwer damit das was du mir als Hilfe zur Verfügung stellst an den richtigen stellen einzusetzen.

Ja, damit vermeidestb Du doppelten / dreifachen... Code. Das Prinzip nennt sich DRY => Don’t repeat yourself
Du erstellst eine Methode und übergibst Referenzen, auf die dann Aktionen ausgeführt werden und kannst so die Methode entsprechend wiederverwenden.

T
Tommylik Themenstarter:in
51 Beiträge seit 2019
vor 2 Jahren

Hallo Abt,

Vielen Dank für deine Antwort.

Das Prinzip nennt sich DRY => Don’t repeat yourself

Das kenne ich und deswegen bin ich ja dabei das mit den Schleifen zu lernen.
Ich möchte ja selber keinen Code der sich wiederholt. Nur die Umsetzung ist halt nicht so einfach.

Dann habe ich noch eine Frage. Ich kann also alles was in der Settings.cs auf 2 Methoden einkürzen.
Ist denn was ich in der MainForm gemacht habe richtig?

Oder muss das so aussehen? Mit SetControlTexts und SetCheckBoxes


public void GetLocalPathConfigSettings()
        {         
            var settings = Settings.Default;

            settings.SetControlTexts(tbLocalAppNames, "LocalAppName");
            settings.SetControlTexts(tbSourceFilePathLocalApps, "LocalAppFilePath");
            settings.SetControlTexts(tbSetPointFileLocalApps, "SetPointLocalApp");

            settings.SetCheckBoxes(cbActivateLocalApps, "LocalAppAktiv");
            settings.SetCheckBoxes(cbActivateCode2LocalApps, "ActivateCode2LocalApp");   
        }


public void SaveLocalPathConfigSettings()
        {
            var settings = Settings.Default;

            settings.SetControlTexts(tbLocalAppNames, "LocalAppName");
            settings.SetControlTexts(tbSourceFilePathLocalApps, "LocalAppFilePath");
            settings.SetControlTexts(tbSetPointFileLocalApps, "SetPointLocalApp");

            settings.SetCheckBoxes(cbActivateLocalApps, "LocalAppAktiv");
            settings.SetCheckBoxes(cbActivateCode2LocalApps, "ActivateCode2LocalApp");           

            Settings.Default.Save();
        }

von TH69
Die Methodenparameter für die ausgelagerten Methoden dann als Control[] bzw. CheckBox[] deklarieren

Muss das Control[] sein oder Textbox[]?

Wäre es richtig, wenn ich den folgenden Code aus der Mainform in die Settings.cs übernehme?


        private TextBox[] tbLocalAppNames;
        private TextBox[] tbSourceFilePathLocalApps;
        private TextBox[] tbSetPointFileLocalApps;
        private CheckBox[] cbActivateLocalApps;
        private CheckBox[] cbActivateCode2LocalApps;

Vielen Dank nochmal für Deine Hilfe.

Grüße Tommylik

4.931 Beiträge seit 2008
vor 2 Jahren

Die Methode GetLocalPathConfigSettings() sieht so korrekt aus. Nur mußt du andere Methoden (Save...) bei SaveLocalPathConfigSettings() verwenden, denn du willst dort ja die Settingswerte speichern (den Code dafür hatte ich dir doch schon gezeigt).

Ich habe extra Control[] genommen, da die Eigenschaft Text dort definiert ist (so ist diese Methode auch für andere Controls außer TextBox nutzbar) - dies ist ja eines der Prinzipien der Objektorientierung.

Die Arrays kannst (bzw. solltest) du nicht auslagern, da du diese ja von der Form-Klasse aus benutzt.

T
Tommylik Themenstarter:in
51 Beiträge seit 2019
vor 2 Jahren

Hallo Th69,

Vielen Dank für deine Antwort.

Die Methode GetLocalPathConfigSettings() sieht so korrekt aus.

Ok wenigstens etwas obwohl es ja dein Verdienst ist.

Ich kapier es nicht wie ich diese 5 Methodenaufrufe aus der Methode GetLocalPathConfigSettings()

MainForm


public void GetLocalPathConfigSettings()
        {
            var settings = Settings.Default;

            settings.SetControlTexts(tbLocalAppNames, "LocalAppName");
            settings.SetControlTexts(tbSourceFilePathLocalApps, "LocalAppFilePath");
            settings.SetControlTexts(tbSetPointFileLocalApps, "SetPointLocalApp");

            settings.SetCheckBoxes(cbActivateLocalApps, "LocalAppAktiv");
            settings.SetCheckBoxes(cbActivateCode2LocalApps, "ActivateCode2LocalApp");
        }

hier in die 2 Methoden reinbringe.

Settings.cs


using System.Windows.Forms;

namespace PDE.Properties
{   
    internal sealed partial class Settings
    {   

#######################################################################  
        Ich denke hier brauche ich noch neue arrays aber ich weiß es nicht und das ist das was mich ärgert.
#######################################################################


        internal void SetControlTexts(Control[] tbLocalAppNames, string v) <-- //tbLocalAppNames muss falsch sein
        {
            ?
        }

        internal void SetCheckBoxes(CheckBox[] ?, string v)
        {
               for (int i = 0; i < ?.Length; i++)
              {
               ?[i].Checked = (bool)Settings.Default[v + (i + 1)];
               }
        }

Es tut mir Leid aber das überfordert mich doch, alles in der Mainform zu machen ist eine Sache
aber mit einer Klasse in Zusammenspiel ist zu viel für mich.

Das ihr Euch das alles merken könnt wo Ihr was braucht (referenzieren, instanziieren, ....usw.)

Da muss ich mir noch viele Beispiele im Internet anschauen. Kennst du ein Beispiel was mir helfen könnte?
Das ich es vielleicht dadurch erarbeiten kann.

Vielen Dank für Deine Hilfe und Geduld.

Grüße Tommylik

5.657 Beiträge seit 2006
vor 2 Jahren

Beschäftige dich mal mit objektorientierter Programmierung. Steuerelement in der UI und Anwendungs-Einstellungen sind auch nur spezielle Fälle davon. Damit kannst du dich dann beschäftigen, wenn du die Basics beherrschst.

Arbeite am besten mal ein Buch durch, z.B. das kostenlose C#-Buch vom Rheinwerk-Verlag: Visual C# 2012 - Das umfassende Handbuch von Andreas Kühnel.

Das Forum ist besser geeignet, wenn du konkrete Fragen hast. Lernen und üben können wir dir hier nicht abnehmen.

Siehe dazu auch [Hinweis] Wie poste ich richtig?

Weeks of programming can save you hours of planning

4.931 Beiträge seit 2008
vor 2 Jahren

Die Methode SetCheckBoxes sieht doch schon richtig aus, nur daß du statt ? dort eben einen (allgemeinen) Variablennamen benutzt, z.B. checkboxes. Das konkrete Array wird ja beim Aufruf aus der MainForm-Methode mitgegeben. Und daher benötigst du auch keine weiteren Arrays in der Klasse Settings.

Aber einen kleinen Fehler habe ich jetzt noch (in deinem geänderten Code) entdeckt - innerhalb der Settings-Klasse kann man einfach mit this auf das eigene Objekt zugreifen.
So sollte also die Methode dann aussehen:


internal void SetCheckBoxes(CheckBox[] checkboxes, string settingsName)
{
    for (int i = 0; i < checkboxes.Length; i++)
    {
        checkboxes[i].Checked = (bool)this[settingsName + (i + 1)];
    }
}

(den 2. Parameter habe ich auch mal entsprechend umbenannt)
Und genauso sollte die andere Methode SetControlTexts aussehen, nur daß du dann auf Text zugreifst.

Ich stimme MrSparkle zu, du solltest mal die passenden Abschnitte in einem C#-Buch (Methoden, Parameter, Referenzen, ...) durcharbeiten (mit kleinen Testprogrammen, um das selber nachzuvollziehen).