Autor Beitrag
Määx
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 123



BeitragVerfasst: Di 28.05.13 09:43 
Hallo zusammen,

ich verzweifel gerade bei der Funktionalität der BackgroundWorker:
ich habe eine recht komplexe TabControl-GUI, die jede Menge Datenbankanfragen benötigt. Hierzu habe ich auf jeder TabPage in jedem UserControl einen BackgroundWorker, den ich im Konstuktor starte. In der doWork lade ich die Daten aus der DB, konvertiere sie in mein Zielformat und übergebe das meist entstandene Dictionary A an die ProgressChanged. Dort überschreibe ich dann das leere private Dictionary B mit dem übergebenen A und setze -falls nicht bereits im Konstruktor geschehen- die comboBoxen, Autocomplete usw auf die überschriebenen Dictionarys B.

Das funktioniert auch wunderbar, nur wird die GUI beim ersten laden ewig nicht angezeigt und wenn ich einen neuen Tab hinzufüge wird die gesamte GUI eingefroren bis alle Datenbankabfragen fertig sind und erst dann erscheint die neue TabPage.

Jetzt habe ich zwei Fragen:
1. Wieso funktioniert das mit den BackgroundWorkern nicht? Der sollte doch asynchron die DB-Abfragen ausführen und die GUI erst bei erfolgreichem Ergebnis ansprechen. Besonders wenn ich meine Datenbank deaktiviere um jeweils die Timeouts abzuwarten friert die GUI für bis zu 30sek ein!
2. Ich habe mir jetzt überlegt vll doch "globale Variablen" für die ganzen Inhalte zu nutzen, da die Datenbankanfrage dann ja zumindestens nur beim Programmstart erfolgen muss. Ich habe aber immer wieder gelesen, dass man darauf wenn möglich verzichten sollte - besonders wenn man mit Threads arbeitet. Da ich genau das machen werde frage ich mich, ist es an dieser Stelle sinnvoll oder sollte ich lieber versuchen das Problem der BackgroundWorker zu lösen?

Vielen Dank für eure Hilfe
Määx

Achso vll. ist auch das noch Wichtig:
Meine TabPages bestehen jeweils aus einem UserControl, dass wiederum aus vielen weiteren Controls besteht. Fast jeder der Controls auf der untersten Ebene hat einen BackgroundWorker.
Wenn in einem Thread der im Hintergrund eine MessageQueue abhöhrt eine entsprechende Nachricht erkannt wird, meldet er ein Event an meine MainClass. Hier wird dann in einem Invoke meine neue TabPage erstellt und dem TabController übergeben
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4803
Erhaltene Danke: 1060

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Di 28.05.13 10:10 
Hallo Määx,

auf die Schnelle kann ich dich nur auf myCSharp.de - [FAQ] Warum blockiert mein GUI? (besonders die Abschnitte "Achtung: Die Falle" ff.) verweisen (gerade das von dir erwähnte Invoke hat mich stutzig gemacht!).
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Di 28.05.13 10:28 
Ohne Code können wir eigentlich nur raten. Und dabei kommt vermutlich nichts hilfreiches bei rum.
Määx Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 123



BeitragVerfasst: Di 28.05.13 13:31 
Hey zusammen,

erstmal vielen Dank für die Links. Ich habe das alles durchgelesen und bin noch einmal durch meinen Code gegangen. Ich glaube aber eigentlich alles richtig gemacht zu haben. Ich versuche jetzt aber mal die wichtigsten Codestellen hier aufzulisten. Falls was fehlt tut es mir leid. Will euch hir nicht mit Code überfluten!
In meiner MainApp starte ich zunächst meinen Thread, der die AMQP-Messages abruft. Dieser soll dann via Invoke auf meinen TabControl zugreifen. Hier übergebe ich recht viel an die Invoke, da ja GUI-Elemente erstellt werden müssen und dies im GUI-Thread geschehen soll.
Beim ersten Aufruf wird newTabAdded() direkt aufgerufen um einen ersten Tab zu erzeugen:
ausblenden 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:
AMQPManagement amqpManager;
public MainApp()
{
  InitializeComponent();
  amqpManager = new AMQPManagement();
  amqpManager.newTabArrived += new AMQPManagement.NewTabArrivedEventHandler(newTabAdded);
  [...]
}
private void newTabAdded(MyTabObject tabObject)
{
  [...]
  if (InvokeRequired){
    BeginInvoke(new Action(() =>
                {
                    TabPage newTab = new TabPage();
                    UserControl_TabHauptmaske tm = new UserControl_TabHauptmaske (tabObject);   // <- dieser hier benötigt ewig viel Zeit
                    newTab .Controls.Add(tm);
                    tabControl.TabPages.Add(newTab );
                }), null);
            }
  }else{
    //hier ist nocheinmal das aufgelistet, was oben im Invoke steht
  }
}


In der UserControl_TabHauptmaske habe ich im Designer diverse UserControls integriert, die jeweils über eine init(MyTabObject tabObject) verfügen und dann -wie ich dachte- den intensiven Rechenaufwand an den Backgroundworker schieben:

ausblenden volle Höhe 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:
private Dictionary<intstring> modelleListDictonary = new Dictionary<int,string>();
private KeyedAutoCompleteStringCollection fahrerListAutocomplete = new KeyedAutoCompleteStringCollection();
public void init(MyTabObject tabObject)
{
  this.Cursor = Cursors.AppStarting;
  backgroundWorkerLoadingData.RunWorkerAsync(); //diese beiden Zeilen stehen teilweise auch im Konstruktor, da ich nicht immer das tabObject dafür benötige
}
private void backgroundWorkerLoadingData_DoWork(object sender, DoWorkEventArgs e)
{
  Communication communication = new Communication();
  Dictionary<intstring> _modelleListDictonary;
  if (communication.getListModelle(out _modelleListDictonary))
    backgroundWorkerLoadingData.ReportProgress(50, _modelleListDictonary);      
  
  Dictionary<intstring> _fahrerListDictonary;
  KeyedAutoCompleteStringCollection _fahrerListAutocomplete = new KeyedAutoCompleteStringCollection();
  if (communication.getListFahrer(out _fahrerListDictonary)){
    foreach( KeyValuePair<int,string>kvp in _fahrerListDictonary ){
      _fahrerListAutocomplete.Add(kvp.Key, kvp.Value);
    }
    backgroundWorkerLoadingData.ReportProgress(100, _fahrerListAutocomplete ); 
  }       
}
private void backgroundWorkerLoadingData_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
  switch(e.ProgressPercentage){
    case 50:
      modelleListDictonary = (Dictionary<intstring>)e.UserState;
      break;
   case 100:
      fahrerListAutocomplete = (KeyedAutoCompleteStringCollection)e.UserState;
      break;
  }     
}
private void backgroundWorkerLoadingData_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
  comboBoxModelle.DataSource = new BindingSource(modelleListDictonary , null);
  comboBoxModelle.DisplayMember = "Value";
  comboBoxModelle.ValueMember = "Key";

  textBoxFahrer.AutoCompleteCustomSource = fahrerListAutocomplete ;
  this.Cursor = Cursors.Default;
}

Ich starte also direkt am Anfang den Backgroundworker, lade sämtliche Daten in ein Dictionary, das dem Worker zugeordnet ist und übergebe dann das fertige Ergebnis jeweils an den GUI-Thread. Am ende ordne ich dann die den GUI-Elemente die Inhalte zu.
Aber trotzdem ist das sehr sehr langsam. Dabei ist es egal, ob das ganze im Invoke passiert oder beim Anwendungsstart manuell aufgerufen wird. In beiden Fällen warte ich ewig auf die Anzeige.

Vielen Dank für eure Hilfe
Määx
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Di 28.05.13 14:03 
Was ist Communication für ein Ding? Kapselt das einen RPC Channel der die Windows Messageschleife braucht?
Määx Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 123



BeitragVerfasst: Di 28.05.13 14:15 
In der Communication greife ich via EntityFramework auf die Datenbank zu -sollte also eher DBCommunciation heissen. Wenn dies geklappt hat wird das übergebene Dictionary gefüllt und true zurückgegeben, sonst false.
Dort werden keine weiteren Threads gestartet. Aber egal was die Communciation-Klasse macht, sie sollte doch unabhängig von der GUI laufen oder?

Määx
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Di 28.05.13 19:13 
Zitat:
Aber egal was die Communciation-Klasse macht, sie sollte doch unabhängig von der GUI laufen oder?


Nein. Drum meine Frage zur Windows Messageschleife. Der UI Thread hat eine Messageschleife (durch Application.Run() gestartet). Sollten andere Threads auch irgendwas aus der Windowsmessage Ecke brauchen und für irgendeine Aufgabe diese Messageschleife blockieren hängt halt die UI weil keine Key-, Mouse-, Paint- und so weiter Messages mehr dran kommen bis die Messageschleife wieder frei ist. Eine reine DB Verbindung ist da aber eigentlich unverfänglich.

Da ich ansonsten nichts sehe kann ich dir nur raten in deinem vorhanden Code in den Threads langsam verdächtige Bestandteile zu entfernen und z.B. durch Thread.Sleep() Aufrufe zu ersetzen. Damit solltest du dich dem Schuldigen näher kommen. Wenn wir denn haben können wir uns dann fragen warum und das korrigieren.

Scheint mir auch nicht sonderlich geschickt das Daten heranholen aus dem Control zu machen. Sauberer wäre es wohl das völlig unabhängig von der UI zu machen. Also einen 3.ten zu haben der den Backgroundworker anwirft und sobald die Daten vom Backgroundworker geholt wurden ein Tab instanziiert und dieser Instanz dann die Daten unterjubelt.
Määx Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 123



BeitragVerfasst: Mi 29.05.13 10:24 
ah, ok! Vielen Dank - das mit der Messageschleife ist gut zu wissen!

Habe deinen Tipp befolgt und bin noch einmal alle einzelnen threads, UserControl usw durchgegangen und siehe da, ich hab an einer Stelle tatsächlich noch etwas gefunden, was sich in den GUI-Thread eingeshclichen hat. Konnte die worst-case Reaktionszeit jetzt damit auf 3Sekunden reduzieren. Werde mal weiter suchen - da ist bestimmt noch ein kleiner Fehler!

Zitat:
Sauberer wäre es wohl das völlig unabhängig von der UI zu machen. Also einen 3.ten zu haben der den Backgroundworker anwirft und sobald die Daten vom Backgroundworker geholt wurden ein Tab instanziiert und dieser Instanz dann die Daten unterjubelt.

Wie meinst du das? Klingt vernünftig :) Wie muss ich das angehen? Würde die Fehlersuche beim nächstenmal auch sicherlich deutlich vereinfachen...

Vielen Dank für eure ambitionierte Hilfe!
Määx
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 29.05.13 11:31 
Zitat:
Wie muss ich das angehen? Würde die Fehlersuche beim nächstenmal auch sicherlich deutlich vereinfachen...


Das mit dem angehen ist 'ne gute Frage. Mit Erfahrung macht man gewisse Dinge irgendwann einfach richtig besser ohne immer genau zu wissen warum. Ich könnte jetzt irgendwelche Entwicklungsmuster abspulen (MVP, MVVM etc.) aber das wäre vermutlich nur bedingt hilfreich bei der Frage wie man sowas angeht ;) Die ganzen Muster versteht man erst richtig wenn man weiß wie man sowas angeht ;)

Ein hilfreicher Ansatz ist denke ich immer zu versuchen Techniken/Aufgaben voneinander zu trennen. Du hast einen BackgroundWorker der Daten von irgendwoher holt und eine UI die Daten anzeigen kann. Kannst du im Moment deinen Backgroundworker nehmen und eine kleinen Wrapperanwendung (oder nennen wir das einen Test) aus ein paar Zeilen Code schreiben die den erzeugt und nur die Daten abgreift? Oder umgekehrt könntest du im Moment den Backgroundworker weglassen und dir kurz mal Daten im Code zusammenlügen um genauso eine kleine Testanwendung zu schreiben die nur deine UI ausprobiert? Wenn ja hast du es vermutlich richtig gemacht. Wenn nein und so sah dein Code für mich aus hast du deine Tabpages unnötiger Weise direkt mit dem Backgroundworker verdrahtet. Ein Test der Einzelaufgaben ist so nur schwer oder gar nicht mehr möglich. Das finden von Problemen ,wie du auch wohl gerade erkennst, wird schwerer weil du es nicht mehr einfach einer bestimmten Einzelkomponente zuordnen kannst.

Insofern der Rat den Backgroundworker unabhängig von anderen Dingen zu machen (wie zum Beispiel der UI) außer dem Ding mit dem es sich tatsächlich beschäftigen soll hier also der Datenbank. Alles andere sei es UI, Konfiguration oder was auch immer hat darin nicht direkt etwas zu suchen sondern sollte maximal von außen an diese Komponente übergebbar sein. Somit sind die Einzelteile austauschbar bzw. sind die Einzelteile auch meist einzeln verwendbar (und testbar).

Um dann die Einzelaufgaben, wie hier Backgroundworker und UI, zusammenzubekommen brauch man dann eben eine spezialisierte 3.te Komponente die die beiden zusammenbringt. Also hier z.B. Die UI anzeigt, den Backgroundworker startet, und wenn der durch ist die Daten in die UI schreibt. In den klassischen Entwicklungsmustern wirst du üblicherweise Klassen finden die Controller, Presenter, Strategie oder irgendsowas heißen. Das sind immer diese ominösen 3.ten die Aufgaben zusammenführen.
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 918
Erhaltene Danke: 158

Win 10
VS 2013, VS2015
BeitragVerfasst: Mi 29.05.13 11:36 
Du kannst dir auch mal die TPL anschauen, die ist etwas neuer. Vielleicht haben Sie da auch noch etwas an der Performance gedreht. Beide nutzen jedoch einen Threadpool, sollten also nicht besonders langsam bei der Erstellung sein.

Damit kannst du dann auch bequem Fortsetzungen festlegen (also was passieren soll, wenn die Arbeit fertig ist) und brauchst nicht das Progress-Event missbrauchen ;-)
(wie das ungefähr aussehen kann siehst du hier: www.entwickler-ecke.....php?p=676643#676643 )

Und weil ich oben das hier gesehen habe:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
  if (InvokeRequired){
    BeginInvoke(new Action(() =>
                {
                    // Code
                }), null);
            }
  }else{
    //hier ist nocheinmal das aufgelistet, was oben im Invoke steht
  }

Um Codewiederholung zu vermeiden mache ich das gerne so:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
private void UpdateProgress()
{
  if (ProgressBar.InvokeRequired)
    ProgressBar.BeginInvoke((Action)delegate { UpdateProgress(); });
  else
  {
    // Fortschrittsleiste updaten - findet im richtigen Thread statt :-)
  }
}
Määx Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 123



BeitragVerfasst: Mi 29.05.13 12:02 
Zitat:
Du kannst dir auch mal die TPL anschauen, die ist etwas neue

Ok, werde ich mir mal ansehen, danke!

Zitat:
Um Codewiederholung zu vermeiden mache ich das gerne so:

:) Gute Idee, hätte man auch selber drauf kommen können...

Zitat:
[...]Ein hilfreicher Ansatz ist denke ich immer zu versuchen Techniken/Aufgaben voneinander zu trennen[...]

Ok, macht sinn! Deine Beschreibung kann ich zwar weitestgehend nachvollziehen, habe jedoch absolut keine Idee wie ich soetwas jetzt umsetzten könnte. Denn wie du schon richtig festgestellt hast, kann ich nicht mal eben so einen Test schreiben.
Kannst du mir neben Test, Controller/Presenter vll. noch ein Stichwort für die Gesamtarchitektur nennen? Oder vll. gar ein Tutorial/Beispielcode empfehlen wo man sich das mal ansehen könnte?

Vielen vielen Dank für eure Hilfe!
Määx
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 29.05.13 13:35 
Zitat:
Kannst du mir neben Test, Controller/Presenter vll. noch ein Stichwort für die Gesamtarchitektur nennen? Oder vll. gar ein Tutorial/Beispielcode empfehlen wo man sich das mal ansehen könnte?

Das hier wäre ein kleines MVP Tutorial unter anderem mit Winforms. Zeigt zumindest ganz gut wie man eine Anwendung strukturieren kann.

Im Sprech des Tutorials wäre die Logik in deinem Backgroundworker ein Service. Der aus dem Presenter heraus in einem Thread abgefragt werden würde und die vom Service erhaltenen Daten dann in den View, also deine Form, schiebt.
Määx Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 123



BeitragVerfasst: Mi 29.05.13 16:07 
komplex, aber sehr cool :) Vielen Dank! Habe dazu aber noch eine kurze Frage. Ich habe mir jetzt das MVP Tutorial angesehen und auch -hoffentlich- weitstgehend verstanden wie ich es selber bauen kann. Bei den Forms wird da ja in der Load der Presenter initialisiert. Im UserControl müsste ich das ja in den Konstruktor schreiben, da diese ja keine Load-Funktion besitzen.
Wenn ich nun in die Service-Klassen aber zeitintensive Routinen schreibe würden die die GUI ja ebenfalls hängen lassen. Müsste ich dann im Presenter die Zeile
ausblenden C#-Quelltext
1:
_view.EmployeeTypes = _service.GetEmployeeTypes();					

durch einen Worker ähnlich wie ich es oben gemacht habe ersetzen, oder wäre es besser wenn ich dann dem _service nur sage, dass er sich die Daten holen soll und dann ein Event auslösen lasse wo ich dann die _view.EmployeeTypes setze? Oder gibt es da einen ganz anderen Weg?

Und noch einmal kurz zum Verständnis:
Ich hatte es in meinem Projekt so, dass es ein zentrales Objekt pro TabPage gab. Wurden einige Eigenschaften hier geändert wurde in verschiedenen UserControls via Eventing die GUI angepasst. Diese Events müsste man dann im Presenter abfangen und an die GUI weiterreichen?

Nochmals vielen Dank!
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 29.05.13 16:40 
Zitat:
durch einen Worker ähnlich wie ich es oben gemacht habe ersetzen, oder wäre es besser wenn ich dann dem _service nur sage, dass er sich die Daten holen soll und dann ein Event auslösen lasse wo ich dann die _view.EmployeeTypes setze? Oder gibt es da einen ganz anderen Weg?

Das würde funktionieren. Da gibt es aber viele Optionen. Einmal würde sich auch anbieten den Service das IAsyncResult Muster für diese Methode implementieren zu lassen (Du hast entsprechendes eigentlich schon benutzt den BeginInvoke Aufruf auf wenn du dort den IAsyncResult return ignoriert hast). Genauso könntest du aber auch weiter einen Backgroundworker verwenden, die angesprochene TPL einsetzen oder irgendein anderes Threading verfahren und dann eben auf den passenden Event reagieren. Ich habe da keine expliziten Präferenzen.
Zitat:
Ich hatte es in meinem Projekt so, dass es ein zentrales Objekt pro TabPage gab. Wurden einige Eigenschaften hier geändert wurde in verschiedenen UserControls via Eventing die GUI angepasst. Diese Events müsste man dann im Presenter abfangen und an die GUI weiterreichen?

Klares ja. Umso dümmer dein Viewteil ist umso einfacher hast du es üblicherweise später beim testen. Und um den ~Sprech~ zu üben ;) mit "Wurden einige Eigenschaften hier geändert wurde" meinst du wahrscheinlich das du dein Model geändert hast. Interessierte Presenter haben dann entsprechende Events des Models registriert um zu bemerken das sich was für sie relevantes geändert hat. Die werden dann die entsprechenden Modeldaten abgreifen eventuell aufbereiten und an den von Ihnen betreuten View (Form oder UserControl) weiterreichen.
Määx Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 123



BeitragVerfasst: Mi 29.05.13 17:02 
ok,Danke! Dann werde ich mir mal die Mühe machen meine Anwendung "neu" zu schreiben. Denke das lohnt!
Dabei habe ich aber direkt noch eine Frage. Ich habe einige UserControls, die ähnliche Funktionalitäten erfüllen. Dabei unterscheiden sie sich eigentlich nur in der Art der Anzeige und den Werten. Alle Werte kommen jedoch als Objekt Parameter an und können auf der ParameterID basierend abgefragt werden. Macht es hier Sinn einfach einen IView für diese Controls zu machen und in den gettern/settern usw. jeweils die ID zu übergeben und dann anhand von switch/case-Anweisungen den GUI-Elementen zuzuordnen (wären im schlimmsten Falle 15stk,im Schnitt eher 4)? Oder wäre dies zu GUI-lastig?
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 29.05.13 17:05 
Zitat:
Dabei unterscheiden sie sich eigentlich nur in der Art der Anzeige und den Werten.


Das bräuchten wir genauer. Jeder Dialog unterscheidet sich von einem anderen darin das er etwas anderes anders darstellt. Ist irgendwie normal oder?
Määx Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 123



BeitragVerfasst: Mi 29.05.13 17:18 
:) stimmt natürlich.
Ich habe ein Objekt Parameter, das die Attribute ID, ErstellungsDateTime und Wert enthält. Ein UserControl UC ist dann für mehrere dieser Parameter verantwortlich. Jenachdem für welchen Parameter das UC verantworlich ist, sollen am Anfang ComboBoxen/ListBoxes gefüllt werden. Ändern sich die Werte eines beliebigen Parameters soll dieser der Datenbank hinzugefügt werden. Entsprechend wäre das speichern/laden von den diversen Parametern im prinzip identisch - der UC muss eben nur entscheiden welche Parameter in TextBoxen oder ComboBoxen geladen werden.
Hoffe das macht meine Frage verständlicher!