Autor |
Beitrag |
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Di 01.04.14 16:14
Man würde beim speichern ähnlich vorgehen wie wir das schon beim Laden der Optionen gemacht haben (getConnectionOptions). Databinding funktioniert ja Bidirectional also stehen die Änderungen aus deiner UI schon in der options Instanz drin. Also äquivalent eine setConnectionOptions Methode schreiben der du die option Instanz übergibst und die wegschreiben läßt. Die Methode gehört dann genauso wie getConnectionOptions nicht zur Form Klasse sondern solltest du in eine eigene Klasse auslagern. localDbQuery und mysqlDbQuery hören sich auch sehr DB nah an und gehören nicht auf die Form.
Die Form sollte vermutlich wenn du es richtig machst nichts mehr von localDbQuery und mysqlDbQuery Wissen sondern nur die Klasse kennen die get/setConnectionOptions hält. Diese Klasse wiederum darf dann localDbQuery und mysqlDbQuery kennen und benutzen. So bist du dann auch schon halbwegs auf dem Weg zu einem schichtenartig aufgebauten System getrennt nach UI-Logik-Persistenz.
|
|
Talemantros 
      
Beiträge: 444
Erhaltene Danke: 2
Win7 Proff 64bit
C# (VS2013)
|
Verfasst: Mi 02.04.14 19:40
Hallo,
leider hatte ich gestern beruflich keine Zeit Dinge zu machen, die mir Spaß machen.
Habe heute aber mal versucht die letzten Anregungen umzusetzen.
Folgendes ist dabei raus gekommen:
Code der Form (komplett ohne DBZugriffe)
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: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65:
| using System; using System.Windows.Forms; using regPosition; using msgbox; using rEcycle.Logics;
namespace rEcycle { public partial class optionenSystem : Form { private connectionOptions options;
public optionenSystem() { InitializeComponent(); }
private void optionenSystem_Load(object sender, EventArgs e) { if (options != null) options.ModifiedChanged -= options_ModifiedChanged; options = connectionOptions.getConnectionOptions(); options.ModifiedChanged += options_ModifiedChanged;
connectionOptionsBindingSource.DataSource = options; }
private void options_ModifiedChanged(object sender, EventArgs e) { btnVerbTest.Enabled = !options.Modified; }
private void btnVerbTest_Click(object sender, EventArgs e) { connectionOptions.mySqlConnTest(); }
private void btnSpeichern_Click(object sender, EventArgs e) { try { connectionOptions.setConnectionOptions(2, options.Benutzer); connectionOptions.setConnectionOptions(3, options.Password); connectionOptions.setConnectionOptions(4, options.Datenbank); connectionOptions.setConnectionOptions(5, options.Server);
msgAusgabe.showInformation("Die Daten wurden erfolgreich gespeichert"); } catch (Exception ex) { msgAusgabe.showError(ex.Message); throw; }
options.Modified = false; } private void btnBeenden_Click(object sender, EventArgs e) { Close(); } } } |
Code der neuen Klasse
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: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176:
| using localDbConnect; using mysqlDbConnect; 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 connectionOptions : INotifyPropertyChanged { #region Felder private bool modified; private string server = string.Empty; private string benutzer = string.Empty; private string password = string.Empty; private string datenbank = string.Empty; public event EventHandler ModifiedChanged; public event PropertyChangedEventHandler PropertyChanged; #endregion
public connectionOptions(string server, string benutzer, string password, string datenbank) { this.server = server; this.benutzer = benutzer; this.password = password; this.datenbank = datenbank; this.modified = false; }
#region Events private void OnModifiedChanged() { var changed = ModifiedChanged; if (changed != null) { changed(this, EventArgs.Empty); } }
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") { var changed = PropertyChanged; if (changed != null) { changed(this, new PropertyChangedEventArgs(propertyName)); } } #endregion
#region Properties
public bool Modified { get { return modified; } set { if (value != modified) { modified = value; OnModifiedChanged(); NotifyPropertyChanged(); } } }
public string Server { get { return server; } set { if (value != server) { server = value; Modified = true; NotifyPropertyChanged(); } } }
public string Benutzer { get { return benutzer; } set { if (value != benutzer) { benutzer = value; Modified = true; NotifyPropertyChanged(); } } }
public string Password { get { return password; } set { if (value != password) { password = value; Modified = true; NotifyPropertyChanged(); } } }
public string Datenbank { get { return datenbank; } set { if (value != datenbank) { datenbank = value; Modified = true; NotifyPropertyChanged(); } } }
#endregion
#region Methoden public static connectionOptions getConnectionOptions() { string connStr = dbInfo.getConnStr(); return new connectionOptions(localDbQuery.getOptions(connStr, 5, 2), localDbQuery.getOptions(connStr, 2, 2), localDbQuery.getOptions(connStr, 3, 2), localDbQuery.getOptions(connStr, 4, 2)); }
public static void mySqlConnTest() { mysqlDbQuery.connectTest(dbInfo.getMysqlConnStr()); }
public static void setConnectionOptions(int id, string wert) { localDbQuery.setOptions(dbInfo.getConnStr(), id, wert); } #endregion } } |
Jetzt würde ich im nächsten Step noch die TextBoxen in ein UserControl auslagern und dann denke ich könnte ich mich dem Rest des Tools widmen.
Solltet ihr noch grobe Fehler in meinem Code finden, dürft ihr euch gern dazu äußern
Auch wenn ich es schon einige Male gesagt habe, möchte ich mich gern an dieser Stelle noch mal bedanken.
Das alles war sehr ergiebig für mich.
Danke
Viele Grüße
|
|
Th69
      

Beiträge: 4798
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: Mi 02.04.14 19:56
Hallo,
das sieht doch jetzt schon richtig gut aus.
Einzig die Benennung der Namensbereiche, Klassen und Methoden solltest du einheitlich nach den C#-Coding Styleguides machen, d.h. mit einem Großbuchstaben am Anfang, s. z.B. C# Programming/Naming.
|
|
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Mi 02.04.14 20:02
a.)
Zitat: | C#-Quelltext 1:
| connectionOptions.setConnectionOptions(2, options.Benutzer); | |
Das vier mal aufzurufen ist natürlich immer noch unschön. Die ~magischen~ Nummern 2,3,4,5 müsste so auch plötzlich jeder Nutzer kennen.
Das Detail gehört versteckt in die Methode. Also besser nur options als Parameter an die setConnectionOption Methode und die 4 notwendigen Einzelaufrufe dann intern abhandeln.
b.)
Die Daten und die Methoden Richtung Datenbank sollten nicht in der selben Klasse stecken. Stell dir zum Beispiel vor zwischen dem Teil mit dem Datenbankzugriff und dem Teil mit der UI wäre noch eine Netzwerkverbindung. Also das die beiden Teile in verschiedenen Prozessen stattfinden. Die connectionOptions Klasse kann,wenn sie nur aus Daten besteht, einfach serialisiert werden und zwischen den Prozessen bewegt werden. Ein simpler Client/Server Aufbau also einfach möglich. Der Datenbank Zugriff ist aber nicht transportierbar. Der andere Prozess/Rechner muss ja keinen Zugriff auf den DatenbankServer haben. Du solltest hier also mindestens 3 unterscheidbare Klassen haben. Die Form, die Klasse mit den Daten und der Klasse die den Zugriff Richtung Datenbank behandelt. dbInfo und localDbQuery betrachte ich dann mal als Interna der Datenbank Klasse die kein anderer kennen muss.
|
|
Talemantros 
      
Beiträge: 444
Erhaltene Danke: 2
Win7 Proff 64bit
C# (VS2013)
|
Verfasst: Do 03.04.14 10:02
Guten Morgen,
Th69 hat folgendes geschrieben : | Hallo,
das sieht doch jetzt schon richtig gut aus.
|
wenn man bedenkt wie lange ich jetzt an dem einen Form sitze bin ich auch echt froh, dass es langsam wird
Ich habe mir nun den C# Coding Styleguide angeschaut und alles innerhalb meines Tools umbenannt was umzubennen wäre.
Danke für den Quide.
Weiterhin habe ich den Aufruf auf anraten von Ralf umgebaut
C#-Quelltext 1:
| ConnectionOptionsMethods.SetConnectionOptions(options); |
Und die Methoden aus der Datenklasse entfernt und eine neue Klasse ConnectionOptionsMethod erzeugt.
Soweit so gut.
Nun sitze ich seit gestern Abend an dem UserControl und habe zum Test eins erstellt in das ich alle 4 TextBoxen und den Verbidung Testen und Speichern Button integriert habe. Muss da aber noch dran basteln, da zum einen die Optik nicht stimmt. Obwohl die Buttons im UserControl richtig angezeigt werden, werden Sie in der Form nur teilweise dargstellt.
Und wenn ich die Buttons aus der UserControl raus lasse und nur die TextBoxen integriere, kennen diese die Buttons auf der Form nicht mehr
Muss ich mich jetzt mal dran versuchen.
Gruß
EDIT:
Hi, ich schon wieder
Also, dass mit dem UserControl gehe ich glaube auch falsch an und bei Youtube etc. finde ich nur welche die irgendwie immer anderes damit umsetzen, wie ich das vorhabe oder in dem Personmanagement Projekt drin sind.
Ich habe nun ein UserControl gebaut, welches die 4 TextBoxen enthält und die 2 Buttons (Verbindung testen, Speichern)
Optik siehe Bild! Wenn ich dies nun so laufen lasse funktioniert es auch, aber der Designer meckert den in der Anlage gezeigten Fehler an, den ich nicht zuordnen kann.
Wenn ich die Buttons aus dem UserControl entferne ist der Fehler weg.
Allerdings müsse ich dann in meiner Form, die Buttons haben, die ich aber nicht von der UserControl aus angesprochen bekomme
Im Moment ist in der UserControl alles drin:
1: Databinding
2: Das abonnieren des Ereignisses
3: Und der Code zu der Klasse für Test und Speichern.
Vielen Dank
Gruß
Einloggen, um Attachments anzusehen!
|
|
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Do 03.04.14 13:38
Aus dem Fehler kann man nur ersehen das du Code ausführst im Umfeld des UserControls (im Konstruktor, Load bzw. Shown Event etc.) den man nur einmal ausführen darf und auch vom WinformsEditor ausgeführt wird. Klick doch mal auf einen der "Aufrufliste anzeigen" Links in der Fehleranzeige. Im angezeigten Stacktrace sollten Methoden von dir auftauchen die die Auslöser des Problems sind. Das sollte dir genügend Information liefern wo das Problem liegt udn warum es knallt. Voraussichtlich ist es Code der eigentlich nicht vom Editor ausgeführt werden sollte.
Zitat: | Allerdings müsse ich dann in meiner Form, die Buttons haben, die ich aber nicht von der UserControl aus angesprochen bekomme |
Bis wir diese Form des Denkfehlers aus dir raus haben liegen wohl noch ein paar Runden mit Fragen vor uns
Wenn das hier gerade helfen würde, was ich nicht glaube, die Buttons vom UserControl zu trennen gibt es keinen Grund das das UserControl auf die Buttons zugreift. Warum auch. Dann hättest du einen UserControl mit den 4 TextBoxen das die connectionOptions Klasse ändern kann. Also würdest du dem UserControl eine API verpassen um diese Klasse mit dem UserControl auszutauschen.
Dafür wäre eine Property mit getter und setter geeignet. Der Setter würde die Klasse an die Textboxen binden und der getter einfach die reingeschobene und gebundene Instanz wieder zurückliefern.
Die Buttons würden also in ihrem ClickEvent nur das UserControl befragen und sich die connectionOptions Klasse holen und dann das tun was sie halt tun. Die einzige Beziehung die es da gibt ist also das der Code der Buttons das UserControl kennen. Sonst kennt keiner irgendwas;)
Das haben wir im großen vorher übrigens auch so gemacht. Sicherstellen das alle Beteiligten möglichst wenig über den jeweiligen anderen wissen. Die UI kennt nur die Oberflächen der Datenklassen und der Datenbanklogik. Die DatenbankLogik kennt nur die Datenklassen. Und die Datenklasse kennt niemanden.
Wichtig ist hier das Wort Oberfläche der Klasse. Hier im kleinen sollte auch nur den anderen Beteiligten die Oberfläche der jeweilig andern Klassn bekannt sein. Wenn du von irgendwo direkt auf die Textboxen im UserControl zugreifst (oder auf die Buttons) dann läuft da was falsch. Auf die innereinen des UserControls darf nur das UserControl zugreifen. Andere nur über die von dir bereitgestellte Programmieroberfläche wie zum Beispiel die von mir angedeutete Property für die connectionOptions Klasse. Alles andere ist privat.
|
|
Talemantros 
      
Beiträge: 444
Erhaltene Danke: 2
Win7 Proff 64bit
C# (VS2013)
|
Verfasst: Fr 04.04.14 12:13
Hi zusammen,
also ich habe mir eben den Fehler noch mal genauer anschauen können.
Da ich keine Methoden oder ähnliches gefunden habe, die ich nicht aufrufen hätte dürfen oder so habe ich mal ein wenig Tante Google bemüht.
Diese hat mir dann mitgeteilt, dass es wohl Fehler im connectionString gibt.
In meinem Stand "AttachDBFilename=|DataDirectory|optionen.mdf;"
Ich habe diesen nun geändert und habe statt des |DataDirectory| den richtigen Pfad auf meinem Laufwerk angegeben.
Jetzt ist der Fehler weg. Mir ist zwar noch ein wenig unklar warum das jetzt mit dem Bau des UserControls aufgetreten ist, da der ConnString vom Visual Studio da abgelegt wurde und vorher auch funktioniert hat, aber Hauptsache es geht.
Ralf Jansen hat folgendes geschrieben : |
Zitat: | Allerdings müsse ich dann in meiner Form, die Buttons haben, die ich aber nicht von der UserControl aus angesprochen bekomme |
Bis wir diese Form des Denkfehlers aus dir raus haben liegen wohl noch ein paar Runden mit Fragen vor uns
|
Ja das glaube ich auch, versuche aber und gelobe Besserung
Zur meiner Verteidigung würde ich sagen, dass ich Beamter im öffentlichen Dienst bin und hier ist auch alles ein wenig langsamer  (Einstellungsvoraussetzung  )
Ich hoffe das zieht, dass ich mich jetzt mal an diesem Klischee bediene.
(Mein Kollege und ich wollten auch schon mal ein Aquarium ins Büro stellen, aber wir haben entschieden das bringt zuviel Unruhe rein  )
Naja, es funktioniert jetzt soweit so ich mir das vorstelle und ich mache mich die Tage mal dran weiter zu machen.
Bin gespannt, wie lange ich ohne euch auskomme.
Vielen Dank und sonniges Wochenende
Gruß
|
|
|