Autor |
Beitrag |
Talemantros
      
Beiträge: 444
Erhaltene Danke: 2
Win7 Proff 64bit
C# (VS2013)
|
Verfasst: Do 01.05.14 17:13
Hi,
es tut mir leid, dass ich schon wieder was fragen muss.
Eigentlich wollte ich das nächste Modul fertig stellen und dann den Code zur konstruktiven Kritik hier einstellen.
Leider habe ich jetzt einen Hänger und komme nicht weiter und bezweifele gerade, dass die grundlegende Überlegung, die ich angestellt habe, richtig ist.
Ich möchte nun die vorher angelegten Mitarbeiter auch bearbeiten können.
Die im System hinterlegten Mitarbeiter werden untereinander in einem TreeView angezeigt.
Beim Klick auf einen Namen sollen die Felder zum Bearbeiten gefüllt werden.
Meine grundsätzliche Überlegung war folgendes:
Beim Klick auf das TreeView, dachte ich eine Methode aufzurufen die eine Liste<Employee> zurückgibt, die ich dann an die BindingSource koppeln wollte.
Dann hätte ich die genutzen Daten beim Speichern wieder wegschreiben können.
Dies klang erstmal ganz gut. Leider komme ich nicht weiter beim Binden der Liste an die Bindingsource, weil ich die Daten nicht aus der Liste bekomme
Code, die die Liste zurückgeben soll mit den Daten eines Mitarbeiters, der vorher ausgewählt wurde.
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:
| public static List<Employee> GetEmployeeDetail(string benutzername) { List<Employee> employee = new List<Employee>();
strSQL = "Select * from mitarbeiter where benutzername = ?benutzername"; using (MySqlConnection conn = new MySqlConnection (connStr)) { using(MySqlCommand cmd = new MySqlCommand (strSQL, conn)) { conn.Open(); cmd.Parameters.AddWithValue("?benutzername", benutzername); MySqlDataReader dr = cmd.ExecuteReader();
while (dr.Read()) { Employee newEmployee = new Employee(); newEmployee.Vorname = dr["vorname"].ToString(); newEmployee.Nachname = dr["nachname"].ToString(); newEmployee.SecId = Convert.ToInt64(dr["secid"]); newEmployee.Gruppe = Convert.ToInt64(dr["berechtigunggruppeid"]);
employee.Add(newEmployee); }
conn.Close(); } }
return employee; } |
Aufgerufen, sollte dies werden über
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| private void treeMitarbeiter_AfterSelect(object sender, TreeViewEventArgs e) { List<Employee> neueListe = new List<Employee>(); neueListe = EmployeeMethods.GetEmployeeDetail(e.Node.Text);
bsEmployee.DataSource = new Employee(); } |
Und eigentlich dachte ich die Werte der Liste an den Konstruktor der Modellklasse zu übergeben und habe dazu 2 Konstrukor eingefügt in die Klasse Employee
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| public Employee() {
}
public Employee(string vorname, string nachname, long secid, long gruppe) { Vorname = vorname; Nachname = nachname; SecId = secid; Gruppe = gruppe; } |
Die TextBoxen etc sind über den Designer gebunden beim OnPropertyChange.
Warum auch immer bekomme ich aber nicht aus der aufrufenden Methode die Werte aus der Liste "neueListe" um sie der Instanz zu übergeben.
Vielleicht ist ja wenigstens der Denkansatz nicht ganz falsch?!
Freue mich wie immer über konstruktive Kritik und Hilfe.
Vielen Dank
Gruß
|
|
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Do 01.05.14 17:39
Zitat: | bsEmployee.DataSource = new Employee(); |
Die zurückgegebene Liste zu ignorieren und ein neues Object anlegen wolltest du sicher nicht. Du wolltest bestimmt
C#-Quelltext 1: 2: 3: 4:
| private void treeMitarbeiter_AfterSelect(object sender, TreeViewEventArgs e) { bsEmployee.DataSource = EmployeeMethods.GetEmployeeDetail(e.Node.Text); } |
schreiben.
|
|
Talemantros 
      
Beiträge: 444
Erhaltene Danke: 2
Win7 Proff 64bit
C# (VS2013)
|
Verfasst: Fr 02.05.14 10:51
Hi,
ich bin gerade leider an keinem Rechner wo ich das von dir korrigierte testen kann
Anscheinend war die Grundüberlegung schonmal gar nicht so falsch.
Im Moment verstehe ich noch nicht ganz, wenn ich die Liste an die BindingSource binde, wie bzw wo ich dann die Propertys fülle damit die TextBoxen diese anzeigen?!
Brauche ich dann den Extra Konstruktor gar nicht?
Jetzt würde ich gern heim fahren und dies testen
Gruß
EDIT: Habe mir diesnochmal über die Textdateien ohne Entwicklungsumgebung angeschaut.
Würde es nicht reichen, die Konstruktoren raus zu nehmen und statt einer List<Employee> die Klasse Employee zurückliefern zu lassen?
Dann würde ich noch nachvollziehen können, dass das DataBindung funktioniert, wenn die Klasse dem DataSource der BindingSource zugewiesen wird.
Das probiere ich mal zu Hause, wenn keine weiteren Einwände kommen sollten
Und schaue dann mal wie ich die Änderungen wieder wegschreibe 
|
|
Th69
      

Beiträge: 4798
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: Fr 02.05.14 12:59
Hallo,
benötigst du denn wirklich eine Liste, denn soweit ich deinen Code überblicke, holst du dir die Datensätze anhand des Benutzernamens (und dieser ist doch wohl eindeutig in der Datenbank, oder)?
Und wenn du dann die Details bearbeiten willst, dann brauchst du nur den Employee zu binden und kannst dann mit den Controls (z.B. TextBoxen) auf die einzelnen Eigenschaften (z.B. Vorname oder Nachname) binden.
PS: Ralf hat zwar schon deine Methode korrigiert, mir erscheint aber, dir ist nicht klar, warum dein vorheriger Code so nicht sinnvoll war.
Zum einen hast du mittels new List<Employee>() eine neue leere Liste erzeugt, aber in der folgenden Zeile gleich wieder mittels der Zuweisung überschrieben (so daß der GC die leere Liste wieder entfernen würde) und zum anderen sollte klar sein, daß die neueListe natürlich auch benutzt werden muß, damit das DataBinding aktiv werden kann.
Ich habe die Erzeugung eines leeren Objekts und der anschließenden Zuweisung mittels eines anderen Objekts allerdings schon öfters bei Anfängern gesehen und frage mich daher, welcher Gedankengang dahinter steckt!?
|
|
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Fr 02.05.14 13:36
Zitat: | Im Moment verstehe ich noch nicht ganz, wenn ich die Liste an die BindingSource binde, wie bzw wo ich dann die Propertys fülle damit die TextBoxen diese anzeigen?! |
Es macht keinen Unterschied ob man eine Liste bindet oder ein einzelnes Objekt. Siehe das einzelne Objekt am besten einfach als Sonderfall einer Liste an. Die BindingSource wird mit der Liste nichts machen sondern veröffentlich die Struktur des Objects in der Liste oder eben das Object selbst wenn es keine Liste ist (genauer wenn es kein IEnumerable ist). Wen du die BindingSource an eine TextBox gebunden hast wird das Objekt angezeigt das gerade als das aktuelle der Liste ausgewählt ist bzw. das einzelne Object das du gebunden hast. (in beiden Fällen das was in BindingSource.Current steht).
Aber wieso willst du die Properties füllen? Deine EmployeeMethods.GetEmployeeDetail holt die schon passend gefüllt aus der Datenbank (ob das eine Liste ist oder nur ein einzelnes Objekt kann ich gerade nicht beurteilen das solltest du wissen). Wenn es dir um einen neuen Employee geht dann kannst du den selbst befüllen wie du willst. Über den Konstruktor, die Properties, einen Initializer oder was auch immer. Es macht keinen Unterschied. Nichts im Subsystem des Databinding erwartet da einen bestimmten Konstruktor.
|
|
Talemantros 
      
Beiträge: 444
Erhaltene Danke: 2
Win7 Proff 64bit
C# (VS2013)
|
Verfasst: Fr 02.05.14 13:40
Th69 hat folgendes geschrieben : |
Ich habe die Erzeugung eines leeren Objekts und der anschließenden Zuweisung mittels eines anderen Objekts allerdings schon öfters bei Anfängern gesehen und frage mich daher, welcher Gedankengang dahinter steckt!? |
Hey Th69,
dass ich ein Objekt erstelle, welches ich dann wieder überschreibe ist mir tatsächlich nicht klar gewesen.
Ich habe das vor Jahren mal VB (nicht .Net) "gelernt" eine Variable zu deklarieren und dieser dann einen Wert zuzuweisen.
Irgendwie habe ich mir dabei gedacht, dass ich quasi auf der empfangenden Seite ein "Container" des gleichen Formats benötige, wie das was die Methode zurück liefert.
Ich hatte jetzt mittlerweile einen Rechner mit Visual Studio zur Hand und habe noch mal ein wenig dran gebastelt. Und da mir ja wichtig ist es "richtig" zu machen stelle ich ihn hier mal ein zur konstruktiven Kritik. Auch wenn es ein wenig dauert finde ich dass ich mit jedem Eintrag hier weiter voran komme. An der Stelle nochmals vielen Dank.
Die Modellklasse habe ich gelassen wie sie vorher war und habe den Konstruktor entfernt.
Weiterhin habe ich die Methode zum zurückliefern wie folgt umgebaut
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:
| public static Employee GetEmployeeDetail(string benutzername) { Employee newEmployee = new Employee();
strSQL = "Select * from mitarbeiter where benutzername = ?benutzername"; using (MySqlConnection conn = new MySqlConnection(connStr)) { using (MySqlCommand cmd = new MySqlCommand(strSQL, conn)) { conn.Open(); cmd.Parameters.AddWithValue("?benutzername", benutzername); MySqlDataReader dr = cmd.ExecuteReader();
while (dr.Read()) { newEmployee.Vorname = dr["vorname"].ToString(); newEmployee.Nachname = dr["nachname"].ToString(); newEmployee.SecId = Convert.ToInt64(dr["secid"]); newEmployee.Gruppe = Convert.ToInt64(dr["berechtigunggruppeid"]); newEmployee.Id = Convert.ToInt64(dr["mitarbeiterid"]); }
conn.Close(); } } return newEmployee; } |
Aufgerufen wird sie so! LokalEmployee habe ich fürs UserControl angelegt, damit ich sie in der SpeicherMethode wieder weiter geben kann. Ok so?!
C#-Quelltext 1: 2: 3: 4: 5:
| private void treeMitarbeiter_AfterSelect(object sender, TreeViewEventArgs e) { lokalEmployee = EmployeeMethods.GetEmployeeDetail(e.Node.Text); bsEmployee.DataSource = lokalEmployee; } |
Die SpeichernMethode habe ich so gebaut:
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
| public static void UpdateEmployeeDetail(Employee employee) { strSQL = "Update mitarbeiter SET benutzername=?benutzername, vorname=?vorname, nachname=?nachname, berechtigunggruppeid=?berechtigunggruppeid where mitarbeiterid=?mitarbeiterid";
using (MySqlConnection conn = new MySqlConnection(connStr)) { using (MySqlCommand cmd = new MySqlCommand (strSQL, conn)) { conn.Open();
cmd.Parameters.AddWithValue("?vorname", employee.Vorname); cmd.Parameters.AddWithValue("?nachname", employee.Nachname); cmd.Parameters.AddWithValue("?berechtigunggruppeid", employee.Gruppe); cmd.Parameters.AddWithValue("?mitarbeiterid", employee.Id); cmd.Parameters.AddWithValue("?benutzername", employee.Nachname + "_" + employee.Vorname);
cmd.ExecuteNonQuery();
conn.Close(); } } } |
Aufgerufen wird sie so:
C#-Quelltext 1: 2: 3: 4:
| private void btnSpeichern_Click(object sender, EventArgs e) { EmployeeMethods.UpdateEmployeeDetail(lokalEmployee); } |
Hiernochmal der ganze Code des UserControls:
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:
| namespace Recycle.Controls { public partial class MitarbeiterBeaUserControl : UserControl { Employee lokalEmployee = new Employee();
public MitarbeiterBeaUserControl() { InitializeComponent(); }
private void MitarbeiterBeaUserControl_Load(object sender, EventArgs e) { List<string> mitarbeiter = new List<string>();
mitarbeiter = EmployeeMethods.GetAllEmployees(); treeMitarbeiter.BeginUpdate(); foreach (string eintrag in mitarbeiter) { treeMitarbeiter.Nodes.Add(eintrag); }
treeMitarbeiter.EndUpdate(); treeMitarbeiter.FullRowSelect = true;
cmbGruppe.DataSource = PermissionMethods.GetGroups(); cmbGruppe.DisplayMember = "name"; cmbGruppe.ValueMember = "berechtigunggruppeid"; }
private void treeMitarbeiter_AfterSelect(object sender, TreeViewEventArgs e) { lokalEmployee = EmployeeMethods.GetEmployeeDetail(e.Node.Text); bsEmployee.DataSource = lokalEmployee; }
private void btnSpeichern_Click(object sender, EventArgs e) { EmployeeMethods.UpdateEmployeeDetail(lokalEmployee); } } } |
Würde man das so bauen mit der Übergabe der Instanz der Klasse oder kann man auch irgendwie die BindinSource etc übergeben?!
EDIT: Der Eintrag von Ralf hatte sich mit meinem Tippen überschnitten. Vielen Dank schon mal für Erklärung
|
|
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Fr 02.05.14 14:09
Ich seh da nur Kleinigkeiten. Gefällt mir.
Zitat: | Employee lokalEmployee = new Employee(); |
Sollte private sein. Wenn du die auch außerhalb des Controls brauchst veröffentliche die Variable als Property. Und ich vermute das du die initiale leere Employee Instanz nicht brauchst.
Am besten prüfst du dann auch in btnSpeichern_Click ob lokalEmployee null ist. Im Moment wenn man einfach mal sofort speichern klickt und noch kein Employee explizit ausgewählt ist sieht es so aus als versuchst du ein leeres Employee Objekt upzudaten (ich glaube zwar das das nicht knallt aber es wird ein Roundtrip zu Datenbank gemacht ohne dann auch irgendwas zu tun ist also unnötig und kostet Zeit).
Zitat: | EmployeeMethods.GetAllEmployees(); |
Da würde ich jetzt erwarten das da Employee Objekte zurückkommen tun sie aber nicht. Nenn die Methode eher danach was sie wirklich tut. Also z.B. GetAllEmployeeNames.
Ach ja und entscheide dich Dinge englisch oder deutsch zu benennen und dann konsequent  Die Sprache wechseln machen zwar viele wenn ihnen die Kreativität bei der Benennung von Variablen ausgeht ich finds aber furchtbar. Insbesondere wenn mehrere Leute beteiligt sind und jeder eine andere Vorstellung der Übersetzung hat. Also nicht nur Employee -> Mitarbeiter sondern auch noch Angestellter, Personal, Arbeiter, Beschäftiger etc. und dann die Spezialisten die das dann wieder noch anders zurück ins englische übersetzen 
|
|
Talemantros 
      
Beiträge: 444
Erhaltene Danke: 2
Win7 Proff 64bit
C# (VS2013)
|
Verfasst: Fr 02.05.14 14:30
Ralf Jansen hat folgendes geschrieben : | Ich seh da nur Kleinigkeiten. Gefällt mir.
Zitat: | Employee lokalEmployee = new Employee(); |
Sollte private sein. Wenn du die auch außerhalb des Controls brauchst veröffentliche die Variable als Property. Und ich vermute das du die initiale leere Employee Instanz nicht brauchst.
|
Hi Ralf,
das mit dem englisch und deutsch werde ich noch abändern, sowie die Bezeichnungen der Methoden noch mal durchgehen.
Ist gar nicht so einfach sich was passendes einfallen zu lassen.
Ich dachte bisher, dass wenn ich kein privat oder public davorschreibe, dass es dann immer private ist. Da muss ich dann in Zukunft dran denken.
Was ich noch nicht verstehe, ist dass du sagt ich bräuchte die Initiale leere Employee Instanz vermutlich nicht. Wie könnte ich dann die Werte sowohl in der AfterSelect Methode des TreeView und im Speichern nutzen?!
Dachte, dass ich dazu immer dies etwas gloaber Vorhalten muss.
Merci
|
|
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Fr 02.05.14 15:00
Bei den Sichtbarkeiten würde ich empfehlen die immer anzugeben auch wenn der Default passen würde. Die Defaults sind in ähnlich aussehenden Sprachen schon mal unterschiedlich und es gibt genug Grenzgänger die darüber stolpern. Und die Default für Sichtbarkeiten sind nicht immer so eindeutig wie man denkt. Klassen sind per Default internal können aber auch anders sein wenn die Klasse in einer anderen Klasse eingebettet ist. Member sind erstmal private wenn die aber zum Beispiel von einem Interface stammen sind haben die die Interface Sichtbarkeit. Interfaces sind erstmal public und wenn wir zu special Klasses kommen wie z.B. Enums sind die irgendwie. Bei Enums ists glaube ich public (wenn nicht in einer Klasse definiert). Du siehst besser einfach immer dranschreiben als jedesmal genau ~nachrechnen~ zu müssen wie die Regeln denn waren.
Zitat: | Was ich noch nicht verstehe, ist dass du sagt ich bräuchte die Initiale leere Employee Instanz vermutlich nicht. Wie könnte ich dann die Werte sowohl in der AfterSelect Methode des TreeView und im Speichern nutzen?! |
Du erzeugst einfach keine Instanz sondern nur die Variable.
C#-Quelltext 1:
| Employee lokalEmployee = null; |
Und beim speichern prüfst du dann halt ob eine Instanz an der Variablen existiert und ob die gültig ist.
C#-Quelltext 1: 2: 3: 4: 5:
| private void btnSpeichern_Click(object sender, EventArgs e) { if ((lokalEmployee != null) && (lokalEmployee.IsValid)) EmployeeMethods.UpdateEmployeeDetail(lokalEmployee); } |
Für diesen Beitrag haben gedankt: Talemantros
|
|
Talemantros 
      
Beiträge: 444
Erhaltene Danke: 2
Win7 Proff 64bit
C# (VS2013)
|
Verfasst: Fr 02.05.14 17:36
|
|
Talemantros 
      
Beiträge: 444
Erhaltene Danke: 2
Win7 Proff 64bit
C# (VS2013)
|
Verfasst: So 04.05.14 16:58
Hi,
nachdem ich mich nun noch ein wenig rumhangel und auch schon ein wenig weiter bin, wollte ich alles noch ein wenig Userfreundlicher machen und habe den SpecherButton wie folgt ergänzt:
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16:
| private void btnSave_Click(object sender, EventArgs e) { if (localEmployee == null) MsgAusgabe.ShowError("Bitte wählen Sie einen Mitarbeiter zum Bearbeiten aus!"); else if (EmployeeMethods.CheckEmployee(localEmployee) == 1) MsgAusgabe.ShowError("Dieser Mitarbeiter existiert bereits im System!"); else if (!localEmployee.IsValid) MsgAusgabe.ShowError("Bitte erfassen Sie alle Felder!"); else if ((localEmployee != null) && (localEmployee.IsValid)) { EmployeeMethods.UpdateEmployeeDetail(localEmployee); MsgAusgabe.ShowInformation("Mitarbeiter erfolgreich gespeichert!"); localEmployee = null; LoadTreeView(); } } |
Ich würde nun gern, dass die TextBoxen, die über DataBinding gebunden sind nach dem Bearbeiten des Mitarbeiters leer sind und dachte es reicht, wenn ich localEmployee auf Null setze, da diese Instanz der Modellklasse ja an die DataSource gebunden ist.
Leider habe ich mich da geirrt.
Könnte mir da noch mal jemand auf die Sprünge helfen. Vielen Dank
Gruß
|
|
Th69
      

Beiträge: 4798
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: So 04.05.14 18:32
Hallo,
durch localEmployee = null setzt du nur diese Variable auf null, aber das Objekt, welches noch irgendwo anders gebunden ist, wird dadurch nicht automatisch geändert.
Du mußt schon explizit die DataBinding-Source verändern, wenn du diesen Effekt haben willst - in deinem Fall also z.B. eine neue leere Employee-Instanz anbinden.
Irgendwie kommt mir (und dir hoffentlich auch) dieser Code doch bekannt vor:
C#-Quelltext 1: 2:
| localEmployee = new Employee(); bsEmployee.DataSource = localEmployee; |

|
|
Talemantros 
      
Beiträge: 444
Erhaltene Danke: 2
Win7 Proff 64bit
C# (VS2013)
|
Verfasst: So 04.05.14 21:50
Danke
|
|
|