Autor |
Beitrag |
Talemantros
      
Beiträge: 444
Erhaltene Danke: 2
Win7 Proff 64bit
C# (VS2013)
|
Verfasst: Di 25.03.14 21:04
Hallo,
ich habe ein Form mit 3 TextBoxen und einen Button der nur gedrückt werden kann, wenn die Daten vorher gespeichert werden.
Daher möchte ich, wenn jemand eine TextBox ändert nur den Speicherbutton anzeigen lassen und erst wenn dieser gedrückt wurde den anderen.
Muss ich jetzt für jede TextBox ein TextChanged schreiben und eine boolVariable setzen?
Oder gibt es da einen anderen Ansatz?
Vielen Dank
Gruß
Daniel
|
|
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Di 25.03.14 23:46
Wo kommen die Daten her? Wenn ich eine Modelklasse hinter den Controls annehmen darf dann sollte die Klasse einfach ihren eigenen Änderungstatus nachhalten und als Property mit passendem PropertyChanged Event veröffentlichen so das man den Button einfach an die Modelklassen Property binden kann.
|
|
Talemantros 
      
Beiträge: 444
Erhaltene Danke: 2
Win7 Proff 64bit
C# (VS2013)
|
Verfasst: Mi 26.03.14 09:16
Hallo Ralf,
danke für deine Antwort, aber das war zu viel für mein Anfänger Hirn
Also in dem akuten Fall habe ich die TextBoxen durch Abfragen aus der MDF gefüllt mit dem SQL Statement?!
Wärst du bereit es mir verständlicher näher zu bringen?
Sorry!
Gruß
Daniel
Edit:
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| private void optionenSystem_Load(object sender, EventArgs e) { try { txtServer.Text = localDbQuery.getOptions(dbInfo.getConnStr(), 5, 2); txtBenutzer.Text = localDbQuery.getOptions(dbInfo.getConnStr(), 2, 2); txtPassword.Text = localDbQuery.getOptions(dbInfo.getConnStr(), 3, 2); txtDatenbank.Text = localDbQuery.getOptions(dbInfo.getConnStr(), 4, 2); lblPfadVorlagen.Text = localDbQuery.getOptions(dbInfo.getConnStr(), 6, 2); } catch (Exception ex) { msgAusgabe.showError(ex.Message); throw; }
} |
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
| public static string getOptions(string connStr, int idAbfrage, int idRueckgabe) { string rueckgabe = string.Empty; SqlConnection conn = new SqlConnection(connStr);
conn.Open(); using (SqlCommand command = new SqlCommand("SELECT * from tblOptionen WHERE id =@id", conn)) { command.Parameters.AddWithValue("@id", idAbfrage); SqlDataReader reader = command.ExecuteReader();
while (reader.Read()) { rueckgabe = reader.GetString(idRueckgabe); } } conn.Close();
return rueckgabe; } |
Aus der dbInfo kommt nur der ConnectionString
Gruß
Daniel
|
|
Th69
      

Beiträge: 4798
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: Mi 26.03.14 10:29
Hallo Talemantros,
du kannst auch alle 3 Textboxen mit derselben Ereignismethode verknüpfen (geht auch schon im VS-Designer), so daß du dann nur einen Code hast:
C#-Quelltext 1: 2: 3: 4:
| void TextBox_TextChanged(object sender, EventArgs e) { buttonSave.Enabled = true; } |
Besser wäre aber wirklich der Ansatz von Ralf, d.h. die Trennung von GUI und Logik (geschweige denn von Datenbank-Operationen).
Erstelle dir zuerst einmal eine eigene Modellklasse, so daß dein GUI-Code nur noch diese benutzt (d.h. alle DB-Operationen auch auslagern) und dann kannst du mittels der Control.DataBindings-Eigenschaft die Verknüpfung(en) herstellen.
|
|
Talemantros 
      
Beiträge: 444
Erhaltene Danke: 2
Win7 Proff 64bit
C# (VS2013)
|
Verfasst: Mi 26.03.14 14:19
Hallo zusammen,
vielen Dank
Da ich ja immer offen bin wie man es besser macht und richtiger würde mich den Ansatz gern versuchen, habe aber meine Schwierigkeiten damit.
Ich habe bisher in meinen Posts nicht danach gefragt, aber wäre es in dem Fall vielleicht möglich mir eine kleine Beispieldatei zu bauen?
Sorry, aber ich stehe auf dem Schlauch!? Es fehlt schon am Verständnis der Modellklasse.
Jetzt stehe ich noch am Anfang meines Tools und wäre sinnig es gleich richtig zu machen.
Vielen dank
Gruß
Daniel
|
|
Th69
      

Beiträge: 4798
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: Mi 26.03.14 15:08
Hallo Talemantros,
du könntest dir mal mein PersonManagement-Projekt von Kommunikation von 2 Forms (Link ist ganz unten auf der Seite) anschauen. Dort habe ich zwar keine Datenbank-Anbindung, aber die Modell-Klassen sind unter "Logic" zu finden (obwohl es bisher nur reine Datenhaltungsklassen sind, aber zusätzliche Logikmethoden würde man dann dort implementieren).
Für das Einbinden der Datenbankfunktionalität (entsprechend der Drei-Schichten-Architektur) am besten auch einen eigenen Unterordner mit Klassen anlegen (für größere Projekte würde man diese Unterordner dann sogar in eigene Projekte, d.h. Assemblies, auslagern) - und dann entweder von den Modellklassen darauf zugreifen oder aber eine Service-Klasse erstellen, welche die Modellobjekte mit den Daten aus der DB füllt).
Das Verwenden der Modellklasse wird z.B. dann in der Controls/PersonUserControl gezeigt.
Ich hoffe, damit kommst du weiter und verstehst damit mehr den Sinn einer verteilten Anwendung.
P.S. Ein guter Anhaltspunkt für die Entscheidung, was in die GUI und was in die Logikschicht kommt, ist es, wenn man sich überlegt, für sein Projekt sowohl eine WinForms-GUI als auch eine Konsolenanwendung zu erstellen - alle gemeinsamen Funktionalitäten kommen dann in die Logikschicht (Modelle oder Services etc.).
|
|
Talemantros 
      
Beiträge: 444
Erhaltene Danke: 2
Win7 Proff 64bit
C# (VS2013)
|
Verfasst: Mi 26.03.14 15:26
Hallo TH69,
vielen dank für den Post.
Ich werde versuchen mich heute oder morgen damit zu beschäftigen und zu verstehen.
Vielen Dank
Gruß
Daniel
|
|
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Mi 26.03.14 15:32
Ein vollständiges Projekt wie von Th69 ist sicher am sinnvollsten da man das auch in Aktion sehen kann. Da ich anhand deines Codes was gebastelt habe poste ich das mal trotzdem.
a.) Ein Klasse die deine Optionen hält für DataBinding vorbereitet ist und den Änderungsstatus (Modified) nachhält.
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:
| public class ConnectionOptions : INotifyPropertyChanged { public ConnectionOptions(string server, string benutzer, string password) { this.server = server; this.benutzer = benutzer; this.password = password; this.modified = false; }
private bool modified; public bool Modified { get { return modified; } set { if (value != modified) { modified = value; OnModifiedChanged(); NotifyPropertyChanged(); } } }
private string server = String.Empty; public string Server { get { return server; }
set { if (value != server) { server = value; Modified = true; NotifyPropertyChanged(); } } }
private string benutzer = String.Empty; public string Benutzer { get { return benutzer; }
set { if (value != benutzer) { benutzer = value; Modified = true; NotifyPropertyChanged(); } } }
private string password = String.Empty; public string Password { get { return password; }
set { if (value != password) { password = value; Modified = true; NotifyPropertyChanged(); } } }
public event EventHandler ModifiedChanged; private void OnModifiedChanged() { var changed = ModifiedChanged; if (changed != null) changed(this, EventArgs.Empty); }
public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged([CallerMemberName] String propertyName = "") { var changed = PropertyChanged; if (changed != null) changed(this, new PropertyChangedEventArgs(propertyName)); } } |
b.) Ein neue Methode die die Optionen in der neuen Klasse verpackt
C#-Quelltext 1: 2: 3: 4: 5: 6:
| public static ConnectionOptions getConnectionOptions(string connStr) { return new ConnectionOptions(localDbQuery.getOptions(dbInfo.getConnStr(), 5, 2), localDbQuery.getOptions(dbInfo.getConnStr(), 2, 2), localDbQuery.getOptions(dbInfo.getConnStr(), 3, 2)); } |
c.) Ein Überarbeitung deines Load Events das nun diese Klasse verwendet
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| private ConnectionOptions options; private void optionenSystem_Load(object sender, EventArgs e) { if(options != null) options.ModifiedChanged -= options_ModifiedChanged; options = getConnectionOptions(dbInfo.getConnStr()); options.ModifiedChanged += options_ModifiedChanged;
txtServer.DataBindings.Add("Text", options, "Server"); txtBenutzer.DataBindings.Add("Text", options, "Benutzer"); txtPassword.DataBindings.Add("Text", options, "Password"); } |
d.) einen Eventhandler (oben in Load schon verdrahtet) um das andern von Modified der Klasse abzufangen und deinen Save Button passend zu setzen
C#-Quelltext 1: 2: 3: 4:
| void options_ModifiedChanged(object sender, EventArgs e) { meinLieberButtonForSaving.Enabled = options.Modified; } |
|
|
Talemantros 
      
Beiträge: 444
Erhaltene Danke: 2
Win7 Proff 64bit
C# (VS2013)
|
Verfasst: Mi 26.03.14 16:29
Wow, vielen Dank euch beiden.
Bevor ich weiter baue, werde ich mir beides ausführlich anschauen und versuchen zu verstehen.
Vielen Dank
Gruß
Daniel
P.S.: Immer wenn ich denke ich habe was verstanden und bin ein Stück weiter, bekomme ich (positiv gesehen) eins zwischen die Hörner 
|
|
Talemantros 
      
Beiträge: 444
Erhaltene Danke: 2
Win7 Proff 64bit
C# (VS2013)
|
Verfasst: Do 27.03.14 11:18
Guten Morgen,
ich habe nun mal den Code von Ralf durchgearbeitet, hänge allerdings bei vielen Ecken und bin ein wenig frustriert.
Das Projekt von Th69 konnte ich mir nur grob anschauen, da dort einige Fehler bei mir gezeigt werden. Aber die Übersichtlichkeit der Modellklassen etc sind mir erst mal bewusst geworden. Dies werde ich versuchen bei mir auch so umzusetzen.
Es ergeben sich noch einige Fragen für mich:
1. Die Klasse public class ConnectionOptions erbt von INotifyPropertyChanged. Gehe ich recht in der Annahme, dass dies eine C# eigene Klasse ist, die speziell solche Dinge abfängt?
2.) Die Initialisierung des Konstruktors mit den Parametern und die Propertys verstehe ich soweit. Leider komme ich mit den Eventhandlern gar nicht zurecht.
Der erste Eventhandler setzt True oder False bei Änderungen der Propertys. Was genau macht dabei changed(this, EventArgs.Empty);
3.) Die Aufgabe des 2 EventHandlers und deren Übergabewerte ([CallerMemberName] String propertyName = "") verstehe ich leider gar nicht.
Ich werde mal versuchen dies in mein Projekt zu basteln, ein wenig mit rumspielen in der Hoffnung das Verständnis wird mir klar.
Danke schon mal...
frustrierte Grüße
Daniel
EDIT: Bin gerade auf www.mycsharp.de/wbb2...d.php?threadid=26116 gestoßen, welches ich mir dann auch parallel noch mal anschauen werde
Zuletzt bearbeitet von Talemantros am Do 27.03.14 11:46, insgesamt 1-mal bearbeitet
|
|
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Do 27.03.14 11:45
Zitat: | 1. Die Klasse public class ConnectionOptions erbt von INotifyPropertyChanged. Gehe ich recht in der Annahme, dass dies eine C# eigene Klasse ist, die speziell solche Dinge abfängt? |
Wie das I am Anfang andeutet ist es ein Interface keine Klasse. Es stellt im Prinzip nur das PropertyChanged Event bereit das zum automatischen Daten synchronisiern zwischen Controls und Klassen benutzt wird. Wie du im Code siehst wird das PropertyChanged in den Settern der Properties verwendet. Sobald sich also eine Property ändert wird dieses Event geworfen. Die Textboxen sind per Databinding an diese Klasse gebunden und horchen auf das PropertyChanged Event.Heißt also sobald sich die Klasse ändert ändert sich auch die Anzeige in den Textboxen ohne das man da manuell eingreifen muß.
Zitat: | 2.) Die Initialisierung des Konstruktors mit den Parametern und die Propertys verstehe ich soweit. Leider komme ich mit den Eventhandlern gar nicht zurecht.
Der erste Eventhandler setzt True oder False bei Änderungen der Propertys. Was genau macht dabei changed(this, EventArgs.Empty); |
Es wirft den PropertyChanged Event. So das jemand anderes den fangen kann. Du kennst das mit den Events vermutlich nur vom anderen Ende her also das du Events fängst und behandelst wie zum Beispiel in deinem Code das Load Event der Form. Aber die Form muss denn ja auch irgendwie ausführen und das macht es mit Load ähnlich wie ich hier mit Modified oder dem PropertyChanged Event.
Zitat: | Die Aufgabe des 2 EventHandlers und deren Übergabewerte ([CallerMemberName] String propertyName = "") verstehe ich leider gar nicht. |
Das CallerMemberName Attribut ersetz den Inhalt des propertyName Parameters durch den Methoden/Propertynamen der Methode der diese Methode aufgerufen hat.
Im Setter der Password Property hätte ich die Methode z.B auch so NotifyPropertyChanged("Password"); aufrufen können das wäre aber identisch zu dem was jetzt passiert da das Attribut automatisch den string "Password" in propertyName schreibt. Wenn ich nicht das Attribut verwenden würde besteht das Risiko das bei Ändern des Propertynamen vergessen wird diesen String mit anzupassen. Wenn wir z.B. Password in Kennwort umbennnen aber vergessen den String mit zu ändern würde beim Ändern der dann Kennwort genannten Property ein PropertyChanged Event ausgelöst werden der behauptet die (nicht existente) Passwort Property wurde geändert. Für einen Programmierer wäre das Unfug und das autmatische Databinding würde nicht mehr funktionieren. Wenn ich eine Kennwort Property an ein Control binde horcht das Control natürlich auf einen Event der besagt das Kennwort geändert wurde und nicht Password.
Deine 3 Fragen drehe sich alle um die Implementierung der Klasse damit sie im Context von Databinding funktioniert. Wen du kein Databinding möchtest kannst du das auch ausbauen dann bleibt recht wenig von der Klasse übrig 
Für diesen Beitrag haben gedankt: Talemantros
|
|
Talemantros 
      
Beiträge: 444
Erhaltene Danke: 2
Win7 Proff 64bit
C# (VS2013)
|
Verfasst: Do 27.03.14 11:53
Hallo Ralf,
Vielen Dank für die ausführliche Antwort.
Werde mich damit gleich weiter beschäftigen. Zum Glück ist heute nicht so viel an der Arbeit los
Das mit dem Databindings würde ich gern so nutzen, aber auch da muss ich noch mal schauen was du mit dem Kommentar über den Designer meintest.
Werde ich dann aber versuchen wenn der Rest verständlich im Kopf ist. Leider fühlt es ich so an, als ob ich zu blöd bin und zu langsam verstehe
Aber wenn man gefühlt ganz unten ist geht es ja nur noch nach oben
Viele liebe Grüße
Daniel
P.S.: Habe wie oben im Edit erwähnt noch etwas zu Events gefunden, was ich mir heute Abend auf der Couch noch zu Gemüte führen werden.
|
|
Talemantros 
      
Beiträge: 444
Erhaltene Danke: 2
Win7 Proff 64bit
C# (VS2013)
|
Verfasst: Sa 29.03.14 23:00
Hallo Ralf,
habe mich jetzt einige Zeit damit beschäftigt und alles Schritt für Schritt auseinander der genommen und denke ich komme so weit klar.
Was ich mich jetzt noch frage ist:
1.) in den Tutorials und Büchern werden Events (immer) mit einem delegate erklärt!
Wieso ist das bei dir nicht so? Wann macht man es mit delegate.
2.) Für was genau ist das Databinding. Wenn ich nur ein Form gebe in dem die Dazen geladen und geändert werden brauch ich es dann? Da es ja von außen nicht geändert werden kann? Oder verstehe ich das ganz falsch?!
3.) Im Projekt von Th69 sind die forms mit UserControls bestückt. Haben diese noch andere Vorteile außer der Wiederverwendbarkeit in anderen Projekten?
Vielen Dank und sonniges Wochenende
Gruß
|
|
Th69
      

Beiträge: 4798
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: So 30.03.14 11:38
Hallo (bin zwar nicht Ralf, aber ich hoffe, meine Antworten nimmst du auch ;-)
zu 1)
Ein Event ist im Grunde nur ein Delegate, jedoch mit speziellen Einschränkungen.
Ein eigenes delegate wird (bzw. sollte) für Events nicht mehr verwendet (werden), da es im Framework die Klasse EventHandler bzw. die generische Version EventHandler<TEventArgs> davon gibt. Der Vorteil dabei ist, daß dann Events immer nach dem gleichen Schema (object sender, EventArgs e) aufgebaut sind.
zu 2)
Databinding ist einfach die bequeme Art, Daten mit Controls zu verbinden, ohne daß der Entwickler jeweils von Hand diesen Code (Daten zuweisen, auslesen und evtl. konvertieren) schreiben muß (in WPF ist dies halt der Standard und auch in WinForms ist dies zu empfehlen).
Ob das DataBinding nur intern oder über eine öffentliche Schnittstelle passiert, spielt dabei keine Rolle.
zu 3)
UserControls bzw. auch selbstentwickelte Controls dienen dem Kapselungsprinzip (und damit auch der Wiederverwendbarkeit). Gerade Anfänger neigen dazu eine Form mit Dutzenden von Einzelelementen zu bestücken (im Sinne von labelX, textBoxX, buttonX) - der bessere Weg ist dann die Entwicklung eines (User)Controls, welches diese Elemente aufnimmt und man dann eine explizite Schnittstelle für den Zugriff bereitstellt!
Ich verwende fast immer ein UserControl (bzw. bei grafischen Controls ein Panel) für die Hauptanzeige innerhalb eines Forms, denn dann kann man diese Form noch sehr bequem um weitere Elemente ergänzen (z.B. eine Menü- oder Toolbar), ohne die vorhandenen Elemente (negativ) zu beeinflussen.
Dies reduziert den Code innerhalb einer Form und macht daher den gesamten Code viel übersichtlicher (in der Form sind dann nur noch Initialisierung sowie die nötigen Event-Methoden enthalten).
|
|
Talemantros 
      
Beiträge: 444
Erhaltene Danke: 2
Win7 Proff 64bit
C# (VS2013)
|
Verfasst: So 30.03.14 17:44
Hey Th69,
Klar nehme ich auch gern deine Antwort entgegen.
Wollte keinen ausschließen. Sorry
Ich hätte es auch wie Anfänger gemacht und viele Steuerelemente genutzt.
Gut dass vieles wie die Modellklassen und UserControls jetzt am Anfang aufkamen. So kann ich es gleich anständig lernen/ bauen.
Werde mich noch weiter mit den Databindings und Controls beschäftigen und dann mal weiter basteln. Bin gespannt.
Vielen Dank
Viele Grüße
|
|
Talemantros 
      
Beiträge: 444
Erhaltene Danke: 2
Win7 Proff 64bit
C# (VS2013)
|
Verfasst: Mo 31.03.14 19:49
Guten Abend,
es tut mir sehr leid, dass ich jetzt noch mal fragen muss.
Nachdem ich nun einige Tutorials und Dokumentationen gelesen hatte und mit Hilfe eurer Antworten, dachte ich alles verstanden zu haben.
Nun wollte ich dies in mein Projekt einbauen:
Die Aufgabenstellung war ja, dass der Button "Verbindung testen" erst nach dem Speichern "enabled = true" ist und bei Änderung der Textfelder aus "false" wechselt.
Nun habe ich die Modellklasse noch um eine Property "Datenbank" erweitert (die Table in der MySQl Datenbank)
Die Databindung habe ich noch nicht anhand des Kommentars von Ralf umgebaut, weil ich erstmal wollte, dass es so funktioniert und ich es dann im nächsten Step umbaue.
Ich gehe halt lieber langsam ran und verstehe dann aber was ich da tue.
Mein Problem sehen nun wie folgt aus:
Wenn ich was in den Textfeldern ändere geht der Button "Verbindung testen" auf false, so wie gewünscht und wenn ich dann auf Speichern drücke wieder auf true. Leider nur einmal nach dem öffnen der Form. Bei jeder weiteren Änderung tut sich nichts und ich verstehe nicht wieso.
Wäre wie immer über Hilfe sehr dankbar!
Code der 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:
| using localDbConnect; 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 { 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;
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; }
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)); } }
#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 != null) { server = value; Modified = true; NotifyPropertyChanged(); } } }
public string Benutzer { get { return benutzer; } set { if (value != null) { benutzer = value; Modified = true; NotifyPropertyChanged(); } } }
public string Password { get { return password; } set { if (value != null) { password = value; Modified = true; NotifyPropertyChanged(); } } }
public string Datenbank { get { return datenbank; } set { if (value != null) { datenbank = value; Modified = true; NotifyPropertyChanged(); } } }
#endregion } } |
Code der Form (bisher noch ohne ausgelagerte UserControl // einfach nur TextBoxen und Buttons zum Test)
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:
| 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 = getConnectionOptions(dbInfo.getConnStr()); options.ModifiedChanged += options_ModifiedChanged;
txtServer.DataBindings.Add("Text", options, "Server"); txtBenutzer.DataBindings.Add("Text", options, "Benutzer"); txtPassword.DataBindings.Add("Text", options, "Password"); txtDatenbank.DataBindings.Add("Text", options, "Datenbank"); }
public static connectionOptions getConnectionOptions(string connStr) { return new connectionOptions(localDbQuery.getOptions(connStr, 5, 2), localDbQuery.getOptions(connStr, 2, 2), localDbQuery.getOptions(connStr, 3, 2), localDbQuery.getOptions(connStr, 4, 2)); }
private void options_ModifiedChanged(object sender, EventArgs e) { btnVerbTest.Enabled = false; }
private void btnVerbTest_Click(object sender, EventArgs e) { mysqlDbQuery.connectTest(dbInfo.getMysqlConnStr()); }
private void btnSpeichern_Click(object sender, EventArgs e) { try { localDbQuery.setOptions(dbInfo.getConnStr(), 2, txtBenutzer.Text); localDbQuery.setOptions(dbInfo.getConnStr(), 3, txtPassword.Text); localDbQuery.setOptions(dbInfo.getConnStr(), 4, txtDatenbank.Text); localDbQuery.setOptions(dbInfo.getConnStr(), 5, txtServer.Text);
msgAusgabe.showInformation("Die Daten wurden erfolgreich gespeichert"); } catch (Exception ex) { msgAusgabe.showError(ex.Message); throw; }
btnVerbTest.Enabled = true; } } } |
EDIT: Ausgehend von der Modelklasse, die ich nun im Hintergrund liegen habe, würde man den Speicher Button dann noch so lassen? Oder eher anders?
Hinter dem Aufruf liegt:
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| public static void setOptions(string connStr, int id, string inhalt) { using (SqlConnection conn = new SqlConnection(connStr)) { conn.Open(); using (SqlCommand cmd = new SqlCommand("UPDATE tblOptionen SET inhalt=@inhalt WHERE Id=@id", conn)) { cmd.Parameters.AddWithValue("@id", id); cmd.Parameters.AddWithValue("@inhalt", inhalt);
cmd.ExecuteNonQuery(); } } } |
EDIT:
Habe nun gesehen, dass ich in den Settern der Properties immer auf != null geprüft habe .
Dies habe ich nun auf != datenbank etc. geändert.
Ändert aber nichts an dem Problem, dass die Veränderung des Wertes nur bis zum ersten Speichern klappt.
|
|
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Mo 31.03.14 21:12
Zitat: | C#-Quelltext 1: 2: 3: 4:
| private void options_ModifiedChanged(object sender, EventArgs e) { btnVerbTest.Enabled = false; } | |
Vergleich das mal mit dem Code von mir. Habe ich auch Enabled immer auf false gesetzt? Ich verrate mal gerade nicht warum das falsch ist. Das wäre eigentlich einfach zu sehen wenn du den Code debuggst. Wenn du noch nicht so fit in debuggen von Code bist wäre das gerade genau die Gelegenheit das jetzt auszuprobieren. Setzt einen BreakPoint in den ModifiedChanged EventHandler und schau dir an wann der immer aufgerufen wird und leite davon ab auf was du Enabled wann setzten solltest.
|
|
Talemantros 
      
Beiträge: 444
Erhaltene Danke: 2
Win7 Proff 64bit
C# (VS2013)
|
Verfasst: Di 01.04.14 07:28
Guten Morgen,
anfangs hatte ich es wie du mit
C#-Quelltext 1:
| btnVerbTest.Enabled = options.Modified; |
Leider hatte dies nicht den gewünschten Erfolg.
Da ich davon ausging, dass "modified" true, oder false enthält und du in deinem Beispiel davon ausgegangen bist der Speicherbutton sollte auf Tue gesetzt werden, habe ich um den Verbindungsbutton auzugrauen es wie folge geändert:
C#-Quelltext 1:
| btnVerbTest.Enabled = !options.Modified; |
Auch hier geht es nur einmal.
Dann hatte ich in der Klasse modified mit true initialisiert und die Properties auf false setzen lassen bevor das Event ausgelöst wurde um in options.modified = false stehen zu haben.
Nachdem dies alles nicht funktioniert hatte und ich nicht gleich ins Forum schreiben wollte, habe ich dann mal pauschal beim Auslösen des Events auf false gesetzt.
Aber wie beschrieben funktioniert dies auch immer nur einmal.
Jetzt habe ich den Breakpoint wie du es empfohlen hast in den Eventhandler gesetzt und das ganze debuggt.
Beim ersten Ändern einer der Zeilen springt er in das Event und löst es aus. Bei jeder weiteren Änderungen nicht.
Leider weiß ich nicht wie ich es debuggen soll, wenn er nicht mal dahin springt wo ich den Breakpoint gesetzt habe.
Was kann ich tun, wenn das Event anscheinend gar nicht richtig ausgelöst wird?
Danke
Edit:
Was ich mich gerade noch frage. Habe mir den Code noch mal angeschaut. Nach instanzieren der Klasse wäre modified = false.
Nach dem ersten Aufruf würde der Setter feststellen, dass modified false war und jetzt true ist, weil zum Beispiel Password verändert wurde.
Beim nächsten Ändern hat modified ja noch true und der Setter würde feststellen, dass modified true ist und ihn ja nicht ändern, daher auch nicht die Methoden des Setters aufrufen oder? Vielleicht lieg ich jetzt auch ganz quer?!
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| set { if (value != modified) { modified = value; OnModifiedChanged(); NotifyPropertyChanged(); } } |
Gruß
|
|
Th69
      

Beiträge: 4798
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: Di 01.04.14 10:08
Hallo,
in der btnSpeichern_Click-Methode setzt du direkt btnVerbTest.Enabled = true; - überlege mal was das für die Logik bedeutet?
Du mußt schon ganzheitlich den DataBinding-Ansatz verwenden...
|
|
Talemantros 
      
Beiträge: 444
Erhaltene Danke: 2
Win7 Proff 64bit
C# (VS2013)
|
Verfasst: Di 01.04.14 14:12
Hi,
also das war jetzt echt eine schwere Geburt. Ich danke euch vielmals für die Hilfe.
Ich habe durch dieses eine Thema unheimlich viel an Informationen sammeln können.
Und natürlich hoffe ich das ich es jetzt richtig gemacht habe
Habe
C#-Quelltext 1:
| btnVerbTest.Enabled = True |
in
C#-Quelltext 1:
| options.Modified = false |
geändert.
Da es jetzt gut in das Thema rein passt mache ich mal kein neuen Eintrag auf. Hoffe das ist ok
Nachdem ich nun eine Modellklasse, Properties habe, würde mich interessieren, ob man in diesem Zuge den Speichern Button so lässt, oder ob der wie das Load auch völlig anders aussehen sollte
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
| private void btnSpeichern_Click(object sender, EventArgs e) { try { localDbQuery.setOptions(dbInfo.getConnStr(), 2, txtBenutzer.Text); localDbQuery.setOptions(dbInfo.getConnStr(), 3, txtPassword.Text); localDbQuery.setOptions(dbInfo.getConnStr(), 4, txtDatenbank.Text); localDbQuery.setOptions(dbInfo.getConnStr(), 5, txtServer.Text);
msgAusgabe.showInformation("Die Daten wurden erfolgreich gespeichert"); } catch (Exception ex) { msgAusgabe.showError(ex.Message); throw; }
options.Modified = false; } |
Hinter dem localDbQuery.setOptions steht
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| public static void setOptions(string connStr, int id, string inhalt) { using (SqlConnection conn = new SqlConnection(connStr)) { conn.Open(); using (SqlCommand cmd = new SqlCommand("UPDATE tblOptionen SET inhalt=@inhalt WHERE Id=@id", conn)) { cmd.Parameters.AddWithValue("@id", id); cmd.Parameters.AddWithValue("@inhalt", inhalt);
cmd.ExecuteNonQuery(); } } } |
Danach hoffe ich kann man ein paar Tage was basteln ohne euer Fachwissen.
Aber nochmal vielen Dank. Das hat vieles grundsätzliches für mich geklärt und ich wusste nach welchen Tutorials und Dokus ich gucken muss.
P.S.: Die Databinding habe ich mittlerweile auch über den Designer gelöst.
Gruß
|
|
|