| Autor |
Beitrag |
Määx
      
Beiträge: 123
|
Verfasst: Di 12.02.13 17:32
Hey zusammen,
ich habe folgendes vor:
Ich habe im Hintergrund einen MSMQ-Thread laufen, der auf eintreffende neue Nachrichten reagiert. Diese Nachrichten enthalten Objekte (zur vereinfachung im Folgenden Personen). Für jede ankommende Person wird nun via Invoke im Hauptfenster (Form mainApp) ein Tab in dem TabControl angelegt. Gleichzeitig starte ich einen weiteren Thread, der via SOAP beim Server weitere Informationen zu der Person abruft. Nun will ich bei SOAP-Antwort den zugehörigen Tab aktualisieren.
Jetzt bin ich mir nicht sicher wie ich das ganze am besten lösen soll. Bisher habe ich MSMQ, SOAP usw. in der Program.cs und rufe von dort Funktionen wie addNewTab in der mainApp auf. Funktionieren tut nun das hinzufügen von Tabs bei MSMQ-Nachrichten(dank www.entwickler-ecke....grammcs_111101.html). Jetzt frage ich mich aber, wie ich am geschicktesten die SOAP-Nachrichten den Tabs zuordne.
Meine erste Überlegung war es, ein globales static Dictonary (PersonenIds als Schlüssel) mit sämtlichen Personen in der Program.cs anzulegen und dem Objekt Person noch ein TabPage hinzuzufügen, das dann auf den entsprechenden Tab in mainApp.tabControl verweist.
Zunächst meine Frage: ist das so sinnvoll oder gibt es elegantere Methoden dies zu realisieren? Würdet ihr das ganze in der Program.cs lassen oder alles in die mainApp verlagern? Bisher sind sämtliche Methoden ja als static deklariert. Bin mir bei den ganzen Threads aber nicht mehr so sicher, ob das nicht zu Problemen führt?
Vielen Dank schon mal für eure Tipps!
Määx
|
|
Christoph1972
      
Beiträge: 690
Erhaltene Danke: 16
VS2015 Pro / C# & VB.Net
|
Verfasst: Mi 13.02.13 22:29
Hört sich machbar an! Ich sehe gerade nicht warum du das nicht so machen könntest
Määx hat folgendes geschrieben : | | Meine erste Überlegung war es, ein globales static Dictonary (PersonenIds als Schlüssel) mit sämtlichen Personen in der Program.cs anzulegen und dem Objekt Person noch ein TabPage hinzuzufügen, das dann auf den entsprechenden Tab in mainApp.tabControl verweist. |
Du könntest Person auch an das Tag Property der TabPages übergeben. Dann ist auch immer klar zu wem das TabPage gehört. Ein UserControl, das von TabPage erbt, könnte ich mir auch gut vorstellen. Mit einer Property Person und DataBinding zu den Controls innerhalb der Page and so on.......
_________________ Gruß
Christoph
|
|
Määx 
      
Beiträge: 123
|
Verfasst: Do 14.02.13 11:09
Super, danke für die Antwort!
Dass man der TabPage ein Objekt als Tag übergeben kann wusste ich garnicht! Das ja praktisch! Eine Referenz aus dem Objekt Person macht aber trotzdem Sinn um aus beiden Richtungen eine schnelle zuordnung zu haben, oder?
Da ich je nach Personengruppe die Tabs unterschiedlich gestalten möchte, wollte ich normale Tabs nehmen und dort nur die UserControls drauf ablegen. Wenn sich dann die Gruppe ändert, kann ich einfach die entsprechenden Controlls wechseln. Oder ist es speicher/geschwindigkeitstechnisch schlauer den gesamten Tab auszutauschen?.
Christoph1972 hat folgendes geschrieben : | | Mit einer Property Person und DataBinding zu den Controls innerhalb der Page and so on....... |
Das habe ich nicht ganz verstanden. Kannst du mir das noch einmal erklären oder kennst du ein Tutorial was mir dies näher bringt?
Vielen Dank für die Hilfe!
Määx
|
|
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Do 14.02.13 13:05
| Zitat: | | Dass man der TabPage ein Objekt als Tag übergeben kann wusste ich garnicht! Das ja praktisch! |
Nein ist nicht praktisch sondern untypisierter Mist womit man sich herrlich ins Knie schießen kann.
| Zitat: | | Da ich je nach Personengruppe die Tabs unterschiedlich gestalten möchte, wollte ich normale Tabs nehmen und dort nur die UserControls drauf ablegen. Wenn sich dann die Gruppe ändert, kann ich einfach die entsprechenden Controlls wechseln. Oder ist es speicher/geschwindigkeitstechnisch schlauer den gesamten Tab auszutauschen?. |
Das ist der richtigere Weg. Bei einer TabPage Ableitung würdest du erhebliche Problem mit dem Winforms Designer bekommen bzw. der wäre gar nicht nutzbar und du müßtest entweder zum Designen dein Code so manipulieren das der Designer glaubt es wäre ein UserControl oder das TabPage Design ohne Designer Unterstützung selbst im Code zusammenbasteln. Da ist ein Satz UserControls das du dann halt selbst im Code auf einem TabPage platzierst, entsprechend deiner Personengruppe, deutlich einfacher zu beherrschen.
Wenn du einmal ein UserControl hast ist dessen interner Aufbau, also wie du die Daten aus dem Person Object in die Controls auf dem UserControl bekommst, ein sekundäres Problem.
| Zitat: | | Meine erste Überlegung war es, ein globales static Dictonary (PersonenIds als Schlüssel) mit sämtlichen Personen in der Program.cs anzulegen und dem Objekt Person noch ein TabPage hinzuzufügen, das dann auf den entsprechenden Tab in mainApp.tabControl verweist. |
Egal was du tust. Einer Datenklassen wie Person Kenntnis über konkrete UI Elemente zu geben ist ein nogo. UI Elemente kennen vielleicht Datenklassen aber nie niemals nicht umgekehrt. Wüßte auch nicht wofür du das brauchst. Eine eingehende Nachricht must du dem richtigen Person Object zuordnen. Wenn dadurch sich das Person Object ändert sollte das Person Object selbst indirekt das an seine Umgebung melden. Zum Beispiel per Event das dein UserControl registriert hat um sich selber anzupassen oder eben per Databinding. Das Person Object sollte aber auf keinen Fall aktiv den Inhalt des UserControls ändern weil es den Typ oder den Aufbau des UserControls kennt. Sondern das UserControl solte sich das notwendige aus dem Person Object holen. Es muss halt nur mitbekommen das es aktiv werden muss.
Für diesen Beitrag haben gedankt: Määx
|
|
Määx 
      
Beiträge: 123
|
Verfasst: Do 14.02.13 13:46
ok, danke für die Rückmeldung. Ich hatte mich bereits gegen die Variante mit dem Tag entschieden und eine eigene TabPagePerson geschrieben, die von TabPage erbt und auf der ich mein UserControl hinzufüge und das Objekt der Klasse Person speichere.
Wenn ich deinen letzten Absatz richtig verstanden habe ist dies aber ebenfalls keine kluge Idee? Ich sollte also normale TabPages nutzen, darauf die UserControls legen und diese dann über DataBinding (bzw. Event) mit dem zugehörigen Personen-Objekt verbinden?
Zusätzlich müsste ich dann noch die TabPage an das Personen-Objekt binden um den Titel entsprechend anzupassen und auf eine Personengruppenänderung zu reagieren (also das UserControl auszutauschen)?
Habe ich das so richtig verstanden oder bin ich noch auf dem falschen Dampfer?
Ich werde mir jetzt erstmal ein paar Tutorials zu dem DataBinding / Eventing anschauen. Das scheint in meinem Konzept ja der Knackpunkt zu sein! Kannst du mir hier eins empfehlen? Sonst frag eich einfach mal google.
Vielen Dank für die Unterstützung!
Määx
|
|
Th69
      

Beiträge: 4807
Erhaltene Danke: 1061
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: Do 14.02.13 14:09
Hallo Määx,
ich hatte gerade gestern erst DataBinding-Tutorials verlinkt: Datensätze ins Label schreiben
|
|
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Do 14.02.13 14:11
| Zitat: | | Wenn ich deinen letzten Absatz richtig verstanden habe ist dies aber ebenfalls keine kluge Idee? Ich sollte also normale TabPages nutzen, darauf die UserControls legen und diese dann über DataBinding (bzw. Event) mit dem zugehörigen Personen-Objekt verbinden? |
Ein TabPage Ableitung wäre nur unklug wenn du darauf irgendwas designen willst weil der Designer halt nur mit UserControls und Forms umgehen kann und einen bei TabPages allein läßt. Du scheinst dafür aber ja ein UserControl zu haben. Wenn das TabPages nur dazu da ist ein UserControl entgegen zunehmen und auf dem TabPage anzuzeigen ist das absolut ok. Da drängt sich eine simple generisches TabPage die einfach ein UserControl entgegen nimmt und sauber anzeigt (sizen, TabText setzen, vielleicht Border setzen) ja auch gerade zu an.
Wie du das im UserControl dann genau machst ob per Event oder DataBinding ist Geschmackssache. Habe da selber keine Präferenz.
|
|
Määx 
      
Beiträge: 123
|
Verfasst: Do 14.02.13 14:23
ok, vielen Dank für eure schnelle & kompetente Hilfe. Super Forum! So wird das hoffentlich ein vernünftiges Projekt!
Ich werde mir die Tutorials von Th69 einmal anschauen und denke, dass ich jetzt klar kommen werde und markiere das Thema mal als gelöst...
|
|
Määx 
      
Beiträge: 123
|
Verfasst: Do 14.02.13 15:18
So, ich habe mir das jetzt mal alles angeschaut und mit dem DataBinding gelöst (Code weiter unten für andere Suchende). Das funktionierte jetzt bei dem ersten Testen ganz gut. Jetzt habe ich allerdings das Problem, dass mir auf meiner TabPagePerson die entsprechenden UserControlls nicht angezeigt werden.
Das ganze sieht wie folgt aus:
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| public class TabPagePerson : TabPage { public Person person; public UserControl control;
public TabPagePerson (Person p) { person = p;
UserControlPersonGrp1 ucp1 = new UserControlPersonGrp1(); control = ucp1;
Controls.Add(ucp1); } } |
Wenn ich mir eine solche TabPage hinzufüge funktioniert das zwar, aber das UserControl wird nicht angezeigt. Ich vermute dass es daran liegt, dass ein Aufruf wie InitializeComponent(); fehlt. Diesen kann ich aber weder direkt in dem Konstruktor noch über die base machen?
Hat jemand eine Idee wo mein Fehler liegt?
DataBinding-Lösung:
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 class Person: INotifyPropertyChanged { private string _Vorname = ""; private string _Nachname = ""; [...]
public string Vorname { get { return _Vorname; } set { if (_Vorname!= value) { _Vorname= value; OnPropertyChanged("Vorname"); } } } [...]
public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } |
Hinzufügen tue ich das ganze dann mit (Edit: nicht sinnvoll - siehe nächster Post von Ralf Jansen)
C#-Quelltext 1: 2: 3:
| Person p = new Person(); p.Vorname = "Max"; textBoxVorname.DataBindings.Add("Text", p, "Vorname"); |
Das mit den gettern&settern kann man wegen des OnPropertyChanged-Aufrufes nicht vereinfachen um sich Schreibaufwand zu sparen oder?
Vielen Dank an alle!
Edit: Da ich zum Beispiel meinen TabPage-Titel ("Nachname, Vorname (Gruppe)") aus mehreren Werten zusammensetze, kann ich hier das DataBinding ja nicht nutzen, oder? Hier müsste ich also auf das Eventing zurückgreifen? Kann mir hier jemand einen Lösungsansatz geben?
Zuletzt bearbeitet von Määx am Do 14.02.13 15:58, insgesamt 2-mal bearbeitet
|
|
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Do 14.02.13 15:40
| Zitat: | Wenn ich mir eine solche TabPage hinzufüge funktioniert das zwar, aber das UserControl wird nicht angezeigt. Ich vermute dass es daran liegt, dass ein Aufruf wie InitializeComponent(); fehlt. Diesen kann ich aber weder direkt in dem Konstruktor noch über die base machen?
Hat jemand eine Idee wo mein Fehler liegt? |
Sehe da keinen Fehler. InituializeComponent kanst du ignorieren. Braucht nur der generierte Code den du in deinem TabPage nicht hast. Vielleicht solltest du noch das UserControl auf die Größe des TabPages sizen bevor du es der Controls Collection hinzufügst.
C#-Quelltext 1:
| ucp1.Dock = DockStyle.Fill; |
Wieso merkst du dir UserControl und Person im TabPage? Sollte es nicht reichen Person direkt an das UserControl weiterzureichen. Das TabPage muss Person nicht kennen.
| Zitat: | | Das mit den gettern&settern kann man wegen des OnPropertyChanged-Aufrufes nicht vereinfachen um sich Schreibaufwand zu sparen oder? |
Programmiertechnisch nicht wirklich. Es gibt ein paar Methoden per Reflection oder AOP Erweiterungen die ich aber erstmal nicht empfehlen würde.
Visual Studio ermöglicht dir aber eigene CodeSnippets zu schreiben (solche wie zum Beispiel das prop -Snippet). Erstell dir ein Snippet für Properties mit PropertyChanged Event und gut.
| Zitat: | | Hinzufügen tue ich das ganze dann mit |
Databinding ist nur schön wenn man es auch im Designer verdrahtet dann ist das ganz recht nützlich und einfach. Wenn du das von Hand im Code verdrahten willst würde ich das eher ohne Databinding machen.
| Zitat: | | Edit: Da ich zum Beispiel meinen TabPage-Titel ("Nachname, Vorname (Gruppe)") aus mehreren Werten zusammensetze, kann ich hier das DataBinding ja nicht nutzen, oder? Hier müsste ich also auf das Eventing zurückgreifen? Kann mir hier jemand einen Lösungsansatz geben? |
Soll sich das sofort ändern wenn du im Tab auf dem UserControl was änderst (z.B. den Nachnamen)? Ansonsten sehe ich da keinen Grund da irgendwas zu basteln. Einfach beim initialisieren den Text zuweisen.
|
|
Määx 
      
Beiträge: 123
|
Verfasst: Do 14.02.13 15:56
mh, das mit dem DockStyle.Fill hat leider nicht geholfen. Meine TabPage bleibt leer!
Noch eine Idee?
Die Sache mit den Snippet werde ich mal ausprobieren. Ist sicherlich eine gute Arbeitserleichterung!
| Zitat: | | Sollte es nicht reichen Person direkt an das UserControl weiterzureichen. |
Nein hast recht, wenn ich -wie es auch deutlich sinnvoller ist- die Person an den UserControl weiterreiche benötige ich Person nicht. Ich hatte die bindings auch im Konstruktor des TabPages stehen. Macht natürlich mehr sinn es im UserControl zu machen damit man dieses auch woanders verwenden kann und ide Binding über den Designer machen kann. Danke für den Hinweis!
| Zitat: | | Soll sich das sofort ändern wenn du im Tab auf dem UserControl was änderst (z.B. den Nachnamen)? Ansonsten sehe ich da keinen Grund da irgendwas zu basteln. Einfach beim initialisieren den Text zuweisen. |
Wenn man das ganze manuell im UserControl ändert muss das nicht zwangsweise direkt auch im Titel geändert werden. Aber wenn sich bei einem nicht akiven Tab durch einkommende MSMQ-Nachrichten etwas ändert, würde ich das gerne direkt im zugehörigen Tab-Titel sichtbar machen. Hier kann ich alternativ aber auch bei einer ankommenden Nachricht das direkt zuweisen.
|
|
Määx 
      
Beiträge: 123
|
Verfasst: Do 14.02.13 16:27
ok, ich hatte einen tipfehler drinnen. Mir ist zwar nicht klar wieso VisualStudio nicht gemeckert hat, aber es funktioniert jetzt auch mit dem UserControl!
Was ich jedoch noch nicht ganz verstanden habe ist wie ich die Zuordnung mit dem Designer mache. Ich habe jetzt in meinem UserControl eine globale Variable Person erzeugt und übergebe diese im Konstruktor. Wenn ich im Designer jetzt aber die DataBinding hinzufügen will zeigt er mir das Objekt Person nicht als Quelle an?
|
|
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Do 14.02.13 16:55
| Zitat: | | Was ich jedoch noch nicht ganz verstanden habe ist wie ich die Zuordnung mit dem Designer mache. Ich habe jetzt in meinem UserControl eine globale Variable Person erzeugt und übergebe diese im Konstruktor. Wenn ich im Designer jetzt aber die DataBinding hinzufügen will zeigt er mir das Objekt Person nicht als Quelle an? |
Weil zur Designzeit kein Objekt existiert sondern nur eine da noch leere Klassenvariable von der der Designer auch nix weiss. Vermutlich wirst du die Klassenvariable Person auch nicht brauchen. geh folgendermassen vor
1.) Wirf eine BindingSource auf das UserControl
2.) Öffne in den Properties der BindingSource die DataSoure Property und öffne dort über 'Add Project Data Source..' den Configuration Wizard
3.) Wähle im Wizard 'Object' aus und suche im angezeigten Klassenbaum der verfügbaren Klassen deine Person Klasse aus.
4.) Im Winforms Designer selber kannst du jetzt allen Controls die BindingSource zuweisen bzw. eine Property deiner Klasse (über die BindingSource).
5.) Im Konstruktor des UserControls noch die konkret übergebene Person Instanz der BindingSource.DataSource zuweisen.
6.) Freuen
Für diesen Beitrag haben gedankt: Määx
|
|
Määx 
      
Beiträge: 123
|
Verfasst: Do 14.02.13 17:10
wie geil! Funktioniert einwandfrei und es nimmt einem ja so viel Arbeit ab!!
Vielen Dank!
|
|
Christoph1972
      
Beiträge: 690
Erhaltene Danke: 16
VS2015 Pro / C# & VB.Net
|
Verfasst: Do 14.02.13 17:13
Ralf Jansen
| Zitat: | | Nein ist nicht praktisch sondern untypisierter Mist womit man sich herrlich ins Knie schießen kann. |
Könntest du das mal näher erläutern was da schief laufen könnte?
Die Doku beschreibt das Tag Property jedenfalls exakt für solche Fälle. Ich habe das selbst schon mehrfach angewendet und finde es eigentlich sehr praktisch.
Moderiert von Th69: Beitragsformatierung überarbeitet.
_________________ Gruß
Christoph
|
|
Määx 
      
Beiträge: 123
|
Verfasst: Do 14.02.13 17:57
ok, ich habe doch noch eine Frage zu dem Databindings... Ich habe in meiner Klasse Person eine weitere Klasse Arbeitsplatz.
Ich hatte es jetzt so gelöst, dass ich eine zweite BindingSource hinzugefügt habe und diese dann wie folgt im Konstruktor setze:
C#-Quelltext 1: 2:
| bindingSourcePerson.DataSource = person; bindingSourceArbeit.DataSource = person.arbeitsplatz; |
Das ganze funktioniert zwar sehr gut, aber wenn ich jetzt den Arbeitsplatz wechsel und nicht alle Inhalte einzeln ändere, sondern den Arbeistplatz zB mit new Arbeitsplatz() überschreibe, dann hat die DataBinding ja noch die Referenz auf den alten Arbeitsplatz. Gibt es hier auch noch einen Trick oder muss ich beim Ändern des Arbeitsplatzes immer die Objektinhalte kopieren?
Vielen dank!
Määx
|
|
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Do 14.02.13 17:57
| Zitat: | Könntest du das mal näher erläutern was da schief laufen könnte?  |
a.) Tag ist einfach ein Object du musst casten wenn du das benutzen willst. Da könnte aber tatsächlich auch was anderes drinstehen also must du auch noch prüfen bevor du castest.
b.) Bei Entwicklermenge größer > 1 könnten mehrere auf die Idee kommen Tag zu benutzen. Tag sieht man nicht an wofür es verwendet wird.
c.) Es gibt (schlechte) Frameworks die Tag ihrerseits für irgendwas cleveres meinen verwenden zu müssen(ich habe da gerade ein Deja Vu aus alten Delphi-tagen mit Datasnap) das kann kollidieren.
Gegenfrage. Tag lässt sich immer durch saubere Ableitung mit dann etwas sprechendem und typsicheren ersetzen (Ausnahme man schreibt etwas generisches das sich an fremden Code klinken soll und ignoriert absichtlich Problem c.). Das kostest nur Sekunden. Warum also den nicht hilfreichen Shortcut verwenden und sich (oder anderen) ein potentielles Problem aufhalsen?
|
|
Ralf Jansen
      
Beiträge: 4708
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: Do 14.02.13 17:59
| Zitat: | | Gibt es hier auch noch einen Trick oder muss ich beim Ändern des Arbeitsplatzes immer die Objektinhalte kopieren? |
bindingSourceArbeit kann bindingSourcePerson als Quelle verwenden  Must dann DataMember von bindingSourceArbeit nur noch auf "arbeitsplatz" setzen.
C#-Quelltext 1: 2:
| bindingSourceArbeit.DataSource = bindingSourcePerson; bindingSourceArbeit.DataMember = "arbeitsplatz"; |
|
|
Christoph1972
      
Beiträge: 690
Erhaltene Danke: 16
VS2015 Pro / C# & VB.Net
|
Verfasst: Do 14.02.13 19:29
[OT]
b.) Bei Entwicklermenge größer > 1 könnten mehrere auf die Idee kommen Tag zu benutzen. Tag sieht man nicht an wofür es verwendet wird.
Das ist in der Tat ein gutes Argument. Die anderen beiden Punkte sind kontrollierbar, aber gut, weiter gehts 
_________________ Gruß
Christoph
|
|
Määx 
      
Beiträge: 123
|
Verfasst: Fr 15.02.13 10:11
funktioniert super! Einfach genial...
Vielen Dank!
|
|