Entwickler-Ecke

WinForms - TextBox mit Namen 'ansprechen'


RalphHoffmann - So 08.11.15 14:55
Titel: TextBox mit Namen 'ansprechen'
Hallo,
um Text aus einer TextBox auszulesen wird ja


C#-Quelltext
1:
2:
3:
4:
5:
Void auslesen()
{
   String a;
   a = this.tb1.Text;
}


genutzt.

Wie kann ich das variabel gestalten? Also in etwa so


C#-Quelltext
1:
2:
3:
4:
5:
Void auslesen()
{
   String a;
   a = this.tb("1").Text;
}


Wobei ich den Ausdruck "1" durch eine Variable ersetzen möchte. Ich habe solch eine Möglichkeit/Methode bei keinem Control gefunden. Gibt es das bei c# nicht?

Gruß
Ralph


Ralf Jansen - So 08.11.15 15:23

Variablennamen sind ein Designzeit Ding die haben keine wirkliche Bedeutung zur Ausführungszeit. Zu einem angezeigten Control muss es nicht mal eine Variable geben.
Controls können aber einen Namen haben (einfach eine Name Property an der Klasse) über die kannst du suchen.

C#-Quelltext
1:
2:
3:
4:
5:
6:
var ctrl = this.Controls["tb1"]; // oder this.Controls.Find(...
TextBox tb1 = ctrl as TextBox;
if (tb1 != null)
{
    // tu was  
}

Theoretisch musst du aber berücksichtigen das es mehrere Controls mit diesem Namen geben kann.

Letztlich hat dein Problem aber ein Geschmäckle. Wenn man dynamisch den Namen eines Ctrl braucht hat man höchstwahrscheinlich was falsch gemacht und Problemfelder nicht sauber getrennt (Z.B. Trennung von Logik und UI).


RalphHoffmann - So 08.11.15 16:59

Hi Ralf,

aber wie soll ich sonst eine 'Matrix' mit 10 x 7 Eingabefeldern (RTFBoxen, Listenfelder) in einer Schleife auslesen? Jeweils 7 Controls habe ich in einem Panel gruppiert. Wenn ein Panel den Fokus verliert sollen die Daten aus den Eingabefeldern gespeichert werden.

Ich mache es jetzt so und es klappt sehr gut (von Eingabefehlern erst einmal abgesehen, die noch nicht abgefangen werden):

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
    public partial class formAuftrag : Form
    {
        private void ArtikelSpeichern(object sender, EventArgs e)
        {   
            if (sender.GetType() == typeof(Panel))
            {
                //Felder
                Panel pnl = (Panel)sender;
                string zeile = string.Empty;
                //Artikelzeile extrahieren
                zeile = Zeile(pnl.Name, "_");
                //Eine Instanz der Klasse Artikel erzeugen
                Artikel data = new Artikel();
                //Die Daten für diesen Artikel eintragen       
                data.Menge = Convert.ToSingle(Inhalt("Menge" + zeile))
                data.Preis = Convert.ToSingle(Inhalt("Preis" + zeile))
                data.Rabatt = (Convert.ToSingle(Inhalt("Rabatt" + zeile)) / 100);
                data.Pos = Inhalt("Pos" + zeile);
                data.Einheit = Inhalt("Einheit" + zeile);
                data.Bestelltext = InhaltRTF("Bestelltext" + zeile);
                data.Etat = Inhalt("Etat" + zeile);
                if (data.Einheit == "Stk." || data.Einheit == "VPE")
                    data.Summe = data.Menge * data.Preis * data.Rabatt;
                else if (data.Einheit == "%")
                    data.Summe = data.Menge * (data.Preis / 100) * data.Rabatt;
                else if (data.Einheit == "Versand" && this.cbRabattVersand.Checked==true )
                    data.Summe = data.Menge * data.Preis * NeuerAuftrag.AuftragsdatenRabatt;
                //data.SetLaengeInMM(rtf);
                //Instanz der Liste der Artikel hinzufügen
                artikel.Add(data);
            }                                 
        }
    }



C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
        //-------- Zeile(string, trenner) --------
        public static string Zeile(string text, string trenner)
        {
            return text.Substring(text.IndexOf(trenner));
        }

        //-------- Inhalt(string) --------
        public string Inhalt(string name)
        {
            Control[] c = this.Controls.Find(name, true);
            c[0].BackColor = SystemColors.Window;
            return c[0].Text;
        }

        //-------- InhaltRTF(string) --------
        public string InhaltRTF(string name)
        {
            Control[] c = this.Controls.Find(name, true);
            if (c[0].GetType() == typeof(RichTextBox))
            {
                RichTextBox rtf = (RichTextBox)c[0];
                return rtf.Rtf;
            }
            return string.Empty;
        }

        //-------- Tipp(string) --------
        public void Tipp(string name)
        {
            Control[] c = this.Controls.Find(name, true);
            c[0].Select();
            c[0].BackColor = Color.Red;
            MessageBox.Show("Fehlerhafte Eingabe!""Eingabefehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }


Mir ist nicht klar, wie das Auslesen ohne die Adressierung der Eingabefelder mittels deren NAmen funktionieren kann.
So geht es wohl nicht:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
            if (sender.GetType() == typeof(Panel))
            {
                Panel pnl = (Panel)sender;
                Artikel data = new Artikel();
                foreach (Control c in pnl.Controls)
                {
                    data.Pos = .... ;
                    data.Menge = .... ;
                    data.Einheit = .... ;
                    data.Bestelltext = .... ;
                    data.Preis = .... ;
                    data.Rabatt = .... ;
                    data.Etat = .... ;
                }
            }
            .....


Beste Grüße
Ralph


Ralf Jansen - So 08.11.15 18:43

Ich sonder mal ab was ich glaube zwischen den Zeilen und im Code zu sehen glaube.

Das hört sich nach eine Tabelle an in denen Zeilen von Artikeln(n) angezeigt werden. Das natürlichste wäre dann ein DataGridView an das ich eine Liste von Artikel(n) binde.
Das wäre dann einfach ein simple Zuweisung ala


C#-Quelltext
1:
2:
List<Artikel> artikel = GetListOfArtikel(); // woher auch immer bei dir die Artikel kommen
meinLiebesDataGridView.DataSource = artikel;


Nirgendwo wird da ein Controlname gebraucht.
Oder wenn die Darstellung im Grid zu simpel ist bzw. nicht hinnehmbare Einschränkungen mit sich bringt dann würde ich die besagten 7 Controls zu einem UserControl zusammenfassen dem ich ein get/set Property für einen Artikel verpasse. Die Zuordnung Artikel zu Controls auf diesem UsesControl würde ich dann über eine BindingSource machen. Da legt man dann die Zuordnung BindSource Property zu Control einfach im Designer fest im Code muss man dann auch nur wieder der BindingSource einen konkreten Artikel zuweisen. Dieses USerControl zeigst du dann halt 10 mal (oder so oft wie benötigt) auf der Form an (wenn es immer noch wie eine Liste angezeigt werden soll dann z.B. in einem TableLayoutPanel)


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
public Artikel Artikel
{
    get
    {
        return meineLiebeBindingSource.DataSource as Artikel;
    }
    set
    {
        meineLiebeBindingSource.DataSource = value;
    }
}


Wieder wird nirgendwo der Controlname gebraucht. Selbst wenn du eine Abneigung gegen DataBinding hast ist der Ansatz mit dem UserControl immer noch gangbar. Dann hast du halt 7 Zuweisungen im getter/setter der Property. Da hier die Zuweisung eindeutig ist. Controls für Pos/Menge etc. wird es auf dem UserControl genau einmal geben. Immer noch kein magischer Name nötig ;)

Zitat:
Mir ist nicht klar, wie das Auslesen ohne die Adressierung der Eingabefelder mittels deren Namen funktionieren kann.


Ich würde mit allen Mitteln verhindern wollen das die Zuordnung was in welchem Control angezeigt wird über irgendein Benamsungschema der Controls passiert.


RalphHoffmann - Mi 18.11.15 20:16

Hallo Ralf,

mir gefällt die Adressierung über die NAmen auch nicht sonderlich, weil sie so umständlich ist. Ich denke aber ich hebe eine Lösung gefunden. Eine Tabelle ist es nicht, deren Inhalte ich sichern möchte. Es ist eine Eingabemaske für Bestellungen.

Ich denke folgender Weg ist "besserer" Code:

Ich lese mit einer For-Each-Schleife die Controls im Panel aus und hinterlege sie in einem kleine Array mit entsprechnd vielen Einträgen. Dann kann ich die Daten aus dem Array einfach den entsprechenden Variablen zuordnen. Ich muss nur dafür Sorge tragen, dass die Controls immer in der gleichen Reihenfolge gelesen werden. I.d.R. fängt c# ja beim letzten Control an.

Bei diesem Weg muss ich zwar noch das entsprechende Panel herausfinden. Das stellt jedoch kein Problem dar.

Gruß
Ralph


Palladin007 - Mi 18.11.15 20:49

Die Variante, die Controls ist nicht besser, eigentlich noch schlechter, da fehleranfälliger.

Die optimale Variante wäre das MVVM-Pattern (Model View ViewModel)
Da hast Du ein ViewModel, was die Daten zum anzeigen bekommt und für die View so aufbereitet, wie sie die Daten braucht.
Die View bekommt das ViewModel dann als DataContext und bindet die Eingabe-Felder an die einzelnen Properties. Jede Änderung wird dann auf die Property übertragen.

Das funktioniert unter WPF ganz hervorragend, bei Windows Forms sollte das aber zumindest fast genauso gut gehen.