Entwickler-Ecke
WinForms - Alle Textboxen durchsuchen, ob sie ausgefüllt sind
Talemantros - Di 29.04.14 11:47
Titel: Alle Textboxen durchsuchen, ob sie ausgefüllt sind
Hallo,
ich möchte gern alle TextBoxen durchsuchen, ob der Benutzer sie ausgefüllt hat.
Wenn nicht, soll die TextBox markiert werden.
Nach einigen Verscuhen im Netz kam ich zu :
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| foreach (Control C in grpDaten.Controls) { if (C.GetType() == typeof(TextBox)) { if (((TextBox)C).Text == string.Empty) { MsgAusgabe.ShowError("Bitte füllen Sie alle Felder aus!"); ((TextBox)C).Focus(); return; } } } |
Leider scheint er den Code nicht anständig zu durchlaufen.
Obwohl eine TextBox leer war läuft er nicht zur MessageBox
Danke
Palladin007 - Di 29.04.14 13:20
Versuch mal so:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| foreach (var control in grpDaten.Controls) { var textBox = control as TextBox;
if (textBox != null && string.IsNullOrWhiteSpace(textBox.Text)) { MsgAusgabe.ShowError("Bitte füllen Sie alle Felder aus!"); textBox .Focus(); break; } } |
Hab allgemein was umgebaut.
So ist es besser, den Typ entweder mit
is oder
as zu prüfen.
as castet die Variable in den angegebenen Typ, wenn das nicht geht, wird null zurück gegeben.
is gibt an, ob die Variable als der angegebene Typ darstellbar ist.
string.IsNullOrWhiteSpace(string) gibt an, ob der übergebene String null, leer ist, oder nur aus Leerzeichen besteht.
Das dürfte alleine schon den Fehler behoben haben, alles ANdere habe ich nur gemacht, weil ich es für besser halte.
Außerdem ist es vielleicht besser, wenn du alle betroffenen TextBoxen in einer Liste bereit hältst.
Je nachdem, wo die erstellt werden, kannst du die dann im Konstruktor oder direkt beim Erstell-Vorgang in dieser Liste ablegen und dann von dort aus ohne extra Überprüfung entsprechend prüfen.
Entsprechende Links:
is [
http://msdn.microsoft.com/de-de/library/scekt9xw.aspx]
as [
http://msdn.microsoft.com/de-de/library/cscsdfbt.aspx]
string.IsNullOrWhiteSpace(string) [
http://msdn.microsoft.com/de-de/library/system.string.isnullorwhitespace%28v=vs.110%29.aspx]
Ich hab dir hier jetzt alles mundgerecht auf bereitet, beachte aber auch, dass für ein Programmierer die Fähigkeit, diverse Suchfunktionen richtig zu verwenden, beinahe schon existenziell wichtig ist.
Keiner kann alles wissen, es ist daher notwendig, sich fehlende Informationen zu suchen und das Internet bietet dafür hervorragende Möglichkeiten.
Ralf Jansen - Di 29.04.14 13:25
Benutzt du DataBinding? Ich würde dich gerne weg von den UI Controls lotsen hin zu einer Modelklasse die ihre Konsistenz selbst prüfen könnte unabhängig von den Controls die letzlich benutzt werden um die Daten anzuzeigen.
Talemantros - Di 29.04.14 17:10
Hallo Palladin007,
vielen dann für den aufbereiteten Code, den werde ich gleich mal testen.
Hatte ja schon einiges gesucht und nachgelesen um den geposteten Code zu erreichen.
@Ralf
In diesem Fall ist es ein Forumlar, welches nur einige Daten abfragt.
Mir fehlt es da noch an Vorstellungskraft wie ich Databindigs verwenden könnte.
Wenn du die Zeit investieren möchtest würde ich mich gerne lotsen lassen.
Im konkreten Beispiel wollte ich ein paar Mitarbeiterdaten abfragen um einen Account auf der MySQL Datenbank anzulegen
Dazu hatte ich eine Modellklasse gemacht.
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:
| using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks;
namespace Recycle.Logics { public class Employee { private string vorname = string.Empty; private string nachname = string.Empty; private long secId = 0; private long gruppe = 0;
public string Vorname { get; set; } public string Nachname { get; set; } public long SecId { get; set; } public long Gruppe { get; set; }
} } |
Mein SpeichernButton sieht momentan so aus
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: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47:
| private void btnSpeichern_Click(object sender, EventArgs e) { private Employee newEmployee;
if (cmbGruppe.SelectedIndex == -1) { MsgAusgabe.ShowError("Bitte wählen Sie eine Berechtigungsgruppe aus!"); return; }
foreach (Control C in grpDaten.Controls) { if (C.GetType() == typeof(TextBox)) { if (((TextBox)C).Text == string.Empty) { MsgAusgabe.ShowError("Bitte füllen Sie alle Felder aus!"); ((TextBox)C).Focus(); return; } } }
newEmployee.Vorname = txtVorname.Text; newEmployee.Nachname = txtNachname.Text; newEmployee.SecID = Convert.ToInt64(txtSecId.Text); newEmployee.Gruppe = Convert.ToInt64(cmbGruppe.SelectedValue);
if (EmployeeMethods.CheckEmployee(newEmployee) == 1) { MsgAusgabe.ShowError("Dieser Mitarbeiter ist bereits im System angelegt wurden!"); return; }
try { EmployeeMethods.SetEmployee(newEmployee); MySqlHistorie.SetHistorie(DbInfo.GetMySqlConnStr(), "Mitarbeiterverwaltung", "Mitarbeiter", newEmployee.Nachname + "_" + newEmployee.Vorname + " angelegt", "Ich"); MsgAusgabe.ShowInformation("Mitarbeiter erfolgreich gespeichert!"); ClearAttributes(); } catch (Exception ex) { MsgAusgabe.ShowError(ex.Message); throw; } } |
@Palladin007
Also der Code wird auch ignoriert und prüft die TextBoxen nicht durch. Denke ich habe irgendwo nen Dreher :-(
Th69 - Di 29.04.14 17:18
Hallo Talemantros,
liegen die Textboxen direkt in der GroupBox 'grpDaten'? Oder doch in weiteren Subcontainern, denn dann müßtest du rekursiv diese Container durchsuchen.
Aber wie Ralf schon schrieb, schöner wäre ein Validierungsansatz mittels DataBinding. Unschön an deinem Code bisher ist z.B. die Verwendung der Convert.ToInt64-Methode, da diese eine Exception wirft, wenn keine Zahl in der TextBox drin steht.
Talemantros - Di 29.04.14 19:00
Hi Th69,
also von mir wurde kein anderer SubContainer angelegt! Wie könnte ich dies sehen?
Die txtsecid kann nicht leer sein, da sie beim Load des UserControls per Zufallsgenerator gefüllt wird. Wäre sonst die tryParse besser?
Würde mir das gern ansehen wie man mit der Validierung mittels Databindung arbeitet.
Das wär für viele weitere Module die ich noch bauen will sehr hilfreich.
In der Zwischenzeit versuche ich gerade dein Beispielprojekt besser zu verstehen.
Danke
Palladin007 - Di 29.04.14 20:17
C#-Quelltext
1: 2: 3: 4: 5:
| if (cmbGruppe.SelectedIndex == -1) { MsgAusgabe.ShowError("Bitte wählen Sie eine Berechtigungsgruppe aus!"); return; } |
Kann es sein, dass die Methode hier raus springt?
Aus dem Grund setze ich returns nach Möglichkeit nur am Ende, damit ich kein return irgendwo übersehe und sicher bin, dass die Methode garantiert und in jedem Fall bis zum Ende durch läuft.
Das kannst du aber auch testen, indem du einfach mal einen Breakpoint an den Anfang der foreach-Schleife setzt und dann mit F10 Schritt für Schritt die einzelnen Durchläufe mit verfolgst und dir anschaust, was dein Programm im Detail tut.
Dazu kommt natürlich noch das, was Th69 gesagt hat.
Neben dem DataBinding-Ansatz (um es schneller und ohne große Änderungen zu lösen), kannst du doch einfach mal meinen Tipp von meinem letzten Post ausprobieren, nämlich indem du die betroffenen Controls in eine Liste legst. Dann musst du nicht auf den Typ prüfen und musst nicht rekusiv alle Controls ab suchen. Die betroffenen Controls kennst du ja und kannst sie im Konstruktor in die Liste legen.
Dennoch wäre DataBinding der saubere Weg.
Zum Verständnis, wie und wo man das verwendet:
Es gibt ein Pattern namens MVVM (Model-View-ViewModel). Das wurde (wenn ich mich nicht irre) besonders im Hinblick auf WPF entworfen, kann aber auch bei WindowsForms angewendet werden.
Einfach zusammen gefasst:
Model:
Das reine Daten-Objekt. Es enthält die Daten aus der Datenbank. Dort werden die abgerufenen Daten rein gelegt und von dort werden neue Daten in die Datenbank gespeichert.
Hier liegt bis auf die reine Validierungs-Logik keine Programm-Logik.
View:
Das ist eine Oberfläche, sie macht nichts außer anzeigen und Daten via DataBinding mit dem ViewModel auszutauschen.
ViewModel:
Wie der Name schon sagt, ist es eine Art Mischung aus View und Model.
Konkret betrachtet ist ein ViewModel das Objekt, das die Logik für die View bereit hält.
Es bekommt seine Daten von dem Model und bereitet sie so auf, dass die View vollständig ohne weitere Logik Diese korrekt anzeigen kann.
Das DataBinding ist dabei die Schnittstelle zwischen View und ViewModel. Die View braucht Daten und sucht sie über diese Schnittstelle bei dem ViewModel und das ViewModel bekommt neu eingegebene Daten über diese Schnittstelle von der View.
Ob das Konzept so sauber mit Windows Forms umsetzbar ist, weiß ich nicht, bei WPF ist es aber möglich, eine View komplett ohne C# zu bauen und ohne irgendeinen Teil des Programms zu kennen. Die einzige Ausnahme hier bildet das ViewModel.
Der Vorteil ist, dass die View unabhängig vom Programm von z.B. einem Designer, der nur geringe C#-Kenntnisse braucht, entwickelt werden kann.
Außerdem kannst du Validierung von eingegebenen Daten im ViewModel machen, da hast du nicht das ganze Chaos drum herum, was die View dar stellt und das ist bei Forms ja sowieso recht umfangreich.
Ich schau grad nach einem Beispiel, da ich selber mich nie wirklich mit Windows Forms anfreunden konnte und DataBinding noch nie dort genutzt habe :D
Ansonsten google du doch mal, auf msdn gibts sowas wie ein riesiges Tutorial.
PS: Schau
hier [
http://www.akadia.com/services/dotnet_databinding.html] mal. Scheint ganz gut zu sein.
Talemantros - Di 29.04.14 20:38
Danke für die ausführliche Antwort.
Mir ist noch nicht ganz klar wie man das baut, werde aber morgen versuchen mir das zu Gemüte zu führen, damit ich direkt so weiter arbeiten kann.
Ich bin gespannt wie weit ich komme.
Gruß
EDIT: Also habe mir das schon einige Male jetzt angeschaut finde aber nicht den Dreh wie ich die Modellklasse bauen müsste damit sie sich selber validiert.
Würde mich über einige Hinweise oder Code freuen.
Würde dann morgen gleich das erste Form dieser Art umbauen und hätte einen guten Start in die nächsten kommenden Module
Danke
Ralf Jansen - Di 29.04.14 22:06
Palladin007's Link beschreibt das Verfahren aus der Sicht des Jahres 2002 (ungefähr). Das würde immer noch gehen aber es macht so keiner mehr.
Ich versuche mal frei die Steps zu beschreiben die nötig sind.
a.) Wirf im Designer eine BindingSource auf die Form (und benenn die vernünftig).
b.) In den Eigenschaften der BindingSource suchst du die DataSource Property raus und öffnest in dessen DropDownList den Punkt 'Add Project DataSource aus'.
c.) Im folgenden Dialog wählst du dann deine Employee Klasse in den angebotenen Klassen aus (wenn die fehlt nochmal kompilieren).
c.) Jetzt gehst du auf irgendeine deiner TextBoxen öffnest die ~Pseudo~ Property (DataBindings) und siehst eine Liste der üblichen bindbaren Properties dieses Controls.
Unter anderem sollte dir die Text Property angeboten werden. In dessen DropDown wählst du die gewünschte Property deiner Klasse über die BindingSource aus die du zum binden verwendet hast.
d.) Wenn das für alle Controls erledigt ist kannst du nun über die DataSource Property der BindingSource eine Employee Instanz zuweisen und am Ende wieder entnehmen.
Es sollte
kein direkter auf irgendein UI Control mehr notwendig sein. Die Daten der Controls kommen aus der Klasse und landen beim verlassen einen Controls direkt wieder in der Klasse.
Auch die Convertierung nach long passiert automatisch (durch das Databinding wird verhindert das du Unsinn eingeben kannst).
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| bsEmployee.DataSource = new Employee();
Employee newEmployee = bsEmployee.DataSource as Employee;
if (!newEmployee.IsValid) MsgAusgabe.ShowError("Bitte füllen Sie alle Felder aus!"); else if (EmployeeMethods.CheckEmployee(newEmployee) == 1) MsgAusgabe.ShowError("Dieser Mitarbeiter ist bereits im System angelegt wurden!"); else EmployeeMethods.SetEmployee(newEmployee); |
Deine Employee Klasse leicht angepasst. Deine privaten Felder sind unnütz die automatischen Properties benutzen die nicht. Und die Longs habe ich nullable gemacht sonst haben die ja schon initial den Wert 0 und eben nicht keinen. Zusatzlich die IsValid Methode mit dem Vollständigkeitstest. Kannst du auch Alternativ in deiner EmployeeMethods Klasse unterbringen.
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| public class Employee { public string Vorname { get; set; } public string Nachname { get; set; } public long? SecId { get; set; } public long? Gruppe { get; set; }
public bool IsValid { get { return !string.IsNullOrWhiteSpace(Vorname) && !string.IsNullOrWhiteSpace(Nachname) && SecId.HasValue && Gruppe.HasValue; } } } |
Talemantros - Mi 30.04.14 18:32
Hallo Ralf,
leider komme ich erst jetzt wieder ein wenig zum lernen/basteln.
Ich danke dir für die Anleitung. Habe dies so umgesetzt, aber leider läuft es noch nicht.
Code der Modellklasse
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:
| using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks;
namespace Recycle.Logics { public class Employee { public string Vorname { get; set; } public string Nachname { get; set; } public long? SecId { get; set; } public long? Gruppe { get; set; }
public bool IsValid { get { return !string.IsNullOrWhiteSpace(Vorname) && !string.IsNullOrWhiteSpace(Nachname) && SecId.HasValue && Gruppe.HasValue; } } } } |
Code hinter dem UserControl
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:
| private void MitarbeiterNeuUserControl_Load(object sender, EventArgs e) { txtVorname.Focus();
cmbGruppe.DataSource = PermissionMethods.GetGroups(); cmbGruppe.DisplayMember = "name"; cmbGruppe.ValueMember = "berechtigunggruppeid"; cmbGruppe.SelectedIndex = -1;
bsEmployee.DataSource = new Employee(); }
private void btnSpeichern_Click(object sender, EventArgs e) { Employee newEmployee = bsEmployee.DataSource as Employee;
if (!newEmployee.IsValid) MsgAusgabe.ShowError("Bitte füllen Sie alle Felder aus!"); else if (EmployeeMethods.CheckEmployee(newEmployee) == 1) MsgAusgabe.ShowError("Dieser Mitarbeiter ist bereits im System angelegt wurden!"); else EmployeeMethods.SetEmployee(newEmployee); } |
Er bemängelt immer, dass nicht alle Felder ausgefüllt wurden.
Nach einigem testen denke ich es liegt es daran, dass er keinen Wert aus der Combobox bekommt, die mit der Property Gruppe als Long definiert ist.
Ich lade die Daten dort aus einer Methode, die einen DataTable zurück gibt (siehe load)
Und irgendwie scheint er da Schwierigkeiten zu haben. In der Datenbank möchte ich nur eine Zahl speichern die ein Fremdschlüssel zu einer anderen Tabelle darstellt.
Daher ist Gruppe ein Long und nicht string.
Könntest du mir da weiterhelfen?
Gruß
Ralf Jansen - Do 01.05.14 10:31
Bei einer ComboBox solltest du eher SelectedItem binden und nicht Text Property so wie ich das bei TextBoxen beschrieben habe.
Talemantros - Do 01.05.14 14:27
Hi,
ich hatte jetzt die Property an SelecedItem gebunden.
Leider wird der Code dann gar nicht mehr ausgeführt, es kommt aber auch keine Fehlermeldung.
Wenn ich das an SelecedValue binde geht es und er schreibt auch den LongWert statt des Strings in die Datenbank
Soweit so gut.
Eine Frage hätte ich noch!
Wäre es so möglich, den Focus auf eine der leeren TextBoxen zu setzen?
Danke
Jetzt muss ich noch Probleme mit der per Zufallszahl generierten SecID lösen, die irgendwie Fehler produziert. Aber es geht voran :-)
Ralf Jansen - Do 01.05.14 16:22
Zitat: |
Wäre es so möglich, den Focus auf eine der leeren TextBoxen zu setzen? |
Dafür müßtest du tatsächlich näher an die UI ran und direkt die Controls prüfen. Man könnte zwar irgendwas an die Modelklasse basteln so das sie die betroffene Property zurückmeldet und der Aufrufer daraufhin das passende Control focusiert. Würde ich aber nicht tun da das kaum generisch lösbar ist und ich eine Trennung von sauberer Modelvalidierung (du könntest auf die Idee kommen Employees ohne UI anzulegen und die jetzige Validierung funktioniert auch dann) und UI Verhalten vorziehe. Wenn ein solches Verhalten wichtig ist würde ich das mit einem
ErrorProvider [
http://msdn.microsoft.com/de-de/library/system.windows.forms.errorprovider.aspx] machen der beim Validieren die passende Fehlermeldung generiert und den Focus passend setzt unabhängig von der Validierung in der Modelklasse.
Talemantros - Do 01.05.14 16:36
Super vielen Dank
Palladin007 - Do 01.05.14 19:05
Wie wäre es mit einer Art TrySet-Methode?
Die Methode bekommt den Namen der Eigenschaft und den Wert. Als Ergebnis gibt es entweder die Info, dass es geklappt hat, oder eine Fehlermeldung.
Die Info kann dann ja eine eigene Klasse namens ErrorInfo sein und wenn es keinen Fehler gibt, ist der Rückgabewert null.
Wenn es eine Möglichkeit gibt, über das Binding an den Namen der Eigenschaft zu kommen, an die ein Control gebunden ist, dann muss der Name auch nicht mehr so klar in den Code geschrieben werden.
Oder eine entsprechende Methode für jede Eigenschaft einzeln.
In der View läufst du dann jedes Control, das relevant ist, durch (ich würde immer noch die Liste nehmen), schickst den Wert an die entsprechende Try-Methode und wenn das Ergebnis nicht null ist, wird der ErrorProvider mit dem Control und der Fehlermeldung in dem Error-Info-Objekt gefüttert.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!