Laden...

Aktualisierung ObservableCollection aus anderem Thread heraus

Letzter Beitrag vor einem Jahr 2 Posts 589 Views
Aktualisierung ObservableCollection aus anderem Thread heraus

Hallo zusammen

Aus meinem MainViewModel rufe ich die folgende Methode auf:

private void TextBoxSymbolExecute(object obj)          
{     
   Option option1 = Option1;
   TWSInterface.DataForOptionsRow(Symbol, ref option1);
   MessageBox.Show("");            
   
   option1.Stk = option1.StkList[0];           
} 

Nun ist es so, dass option1.Stk nur einen Wert erhält, wenn die darüberliegende Zeile "Messagebox.Show(""); besteht. Ansonsten erhalte ich eine SystemOutOfRange-Exception, weil option1.Stk null ist, was es nicht sein soll.
Kann mir jemand helfen wie ich das (Thread)-Problem umgehen/lösen kann?

Nachfolgend noch den Code der TWSInterface-Klasse:

public class TWSInterface : EWrapperImpl
{
   //Eigenschaften
   int contractUnderlyingID;
   ObservableCollection<float> stkList;

   //Konstruktoren
   public TWSInterface()
   {   
       stkList = new ObservableCollection<float>();            
   }   

   //Methoden
   //Call to TWS-API
   public void Connect()
   {
       ClientSocket.eConnect("", 7496, 0);
       var reader = new EReader(ClientSocket, Signal);
       reader.Start();
       new Thread(() =>
       {
           while (ClientSocket.IsConnected())
           {
               Signal.waitForSignal();
               reader.processMsgs();
           }
       })
       { IsBackground = true }.Start();
       while (NextOrderId <= 0) { }
   }  
   public void DataForOptionsRow(string symbol, ref Option zo)
   {
       ClientSocket.reqContractDetails(100, contractUnderlying);
       zo.StkList = stkList;    
   }        
   //Recall from TWS-API 
   public override void contractDetails(int reqId, ContractDetails contractDetails)
   {         
       printContractMsg(contractDetails.Contract);      
   }
   public override void printContractMsg(Contract contract)
   {            
       contractUnderlyingID = contract.ConId;
       ClientSocket.reqSecDefOptParams(0, contract.Symbol, "", "STK", contractUnderlyingID);
   }
   public override void securityDefinitionOptionParameter(int reqId, string exchange, int underlyingConId, string tradingClass, string multiplier, HashSet<string> expirations, HashSet<double> strikes)
   {
       //Sicherstellung, dass der Zugriff auf die ObservableCollection auf dem UI-Thread erfolgt
       Application.Current.Dispatcher.BeginInvoke(() =>
       {
           stkList.Clear();         
           foreach (double strike in strikes)
           {
               float strikeFloat = (float)strike;
               stkList.Add(strikeFloat);                    
           }
       });
   }        
}

Hallo,

so wie ich deinen Code verstehe, hast du ein zeitliches Problem (was durch die MessageBox kaschiert wird).

Ohne den MessageBox-Aufruf ist die Liste option1.StkList leer (so daß der Zugriff auf Index 0 die Exception auslöst). Im Konstruktor von TWSInterface erzeugst du diese leere Liste, gefüllt wird sie aber nur in securityDefinitionOptionParameter(welche anscheinend in DataForOptionsRow durch ClientSocket.reqContractDetails(...)asynchron aufgerufen wird).

Du mußt (logisch / zeitlich) sicherstellen, daß die Daten erst komplett eingelesen sind, bevor die weitere Abarbeitung erfolgt. Am besten beschäftigst du dich mal mit der asynchronen Programmierung (Task<T>/async/ await).

PS: Die Schleife while (NextOrderId <= 0) { }, um auf das Setzen einer Variablen (aus einem anderen Thread) zu warten, ist ein No-Go (so erzeugst du 100% Kernlast)!

Und auch der Dispatcher-Aufruf ist m.E. dort unnötig (bzw. an der falschen Stelle): eine ObservableCollection<T>per se benötigt dies nicht, nur wenn sie an ein UI-Element gebunden ist.