Laden...
T
TreaxP
myCSharp.de - Member
3
Themen
11
Beiträge
Letzte Aktivität
vor 11 Tagen
Dabei seit
05.04.2023
Erstellt vor einem Jahr

Die ListeUnderlyingListwird in der View tatsächlich nicht verwendet (diese benötige ich im ViewModel).

Underlying1 und Underlying2 haben in der View beide eine Bindung (im geposteten Code aber nur Underlying1 ersichtlich). Das diese beiden nicht aktualisiert werden ist genau mein Problem, dass ich nicht zu lösen weiss. Für Terminerinnerungen habe ich in einem anderen Teil des Programms nahezu denselben (nachfolgenden) Code, er funktioniert und die View aktualisiert sich nach dem Aufruf der "Load"-Methode. Der einzige Unterschied ist, dass die dort verwendeten Controls nicht in einem UserControl eingebettet sind.

ObservableCollection<Reminder> loadedReminderList = PersistenceInstance.Load();
if (loadedReminderList != null)
{
   ReminderList.Clear();
   foreach (Reminder reminder in loadedReminderList)
   {
       ReminderList.Add(reminder);
   }
}
Erstellt vor einem Jahr

Deklaration der UnderlyingList

UnderlyingList = new ObservableCollection<Underlying>()
{
   Underlying1, Underlying2
};

Deklaration der 2 Underlying-Objekte

Underlying1 = new Underlying() 
{                
   DivDate = DivDate,
   DivAmount = DivAmount,
   VrfList = { new DateTime(2023, 01, 20), new DateTime(2025, 11, 28) },
   
   RowNumber = 1,
   RowIsVisible = true,
   BS = 'K',               
   Hdt = new DateTime(2022, 05, 21),
   Prjd = new DateTime(2022, 05, 21),
   Vrf = new DateTime(2025, 11, 28),
   KUl = 68.63f,                
   Ka = 1,
   BzvhO = 100.00f,               
   Vortrag = 0f,
   Colorgroup = IAsset.Group.B
};
Underlying2 = new Underlying() 
{
   DivDate = DivDate,
   DivAmount = DivAmount,
   VrfList = { new DateTime(2023, 01, 20), new DateTime(2025, 11, 28) },
   RowNumber = 2,
   RowIsVisible = true,
   BS = 'K',
   Hdt = new DateTime(2022, 05, 21),
   Prjd = new DateTime(2022, 05, 21),
   Vrf = new DateTime(2025, 11, 28),
   KUl = 68.63f,
   Ka = 1,
   BzvhO = 100.00f,
   Vortrag = 0f,
   Colorgroup = IAsset.Group.C
};

Hoffe das hilft für das Verständnis vom Gesamten weiter.

Besten Dank auch für deine Randbemerkung, die ich gerne noch studieren werden.

Erstellt vor einem Jahr

Hallo zusammen

Ich habe das Problem, dass ich bei der Deserialisierung mit Json sowohl die Bindings als auch die Commands verliere (vorher funktionieren diese einwandfrei). Das Objekt bekommt die richtigen Daten zwar wieder, diese werden in den einzelnen Controls die sich in einem UserControl befinden aber nicht mehr aktualisiert (Bild im Anhang: Mit "1" und "2" können die UserControls in grün und gelb ein-/ausgeschaltet werden. Die Daten in den Controls kommen von den "Underlying"-Objekten.).

MainWindow.xaml

<uc:UnderlyingsRowHidden Grid.Row="0" Visibility="{Binding Underlying1.RowIsVisible, Converter={StaticResource invertedBooleanToVisibilityConverter}}" />
<uc:UnderlyingsRow Grid.Row="0" 
                              Visibility="{Binding Underlying1.RowIsVisible, Converter={StaticResource booleanToVisibilityConverter}}"
                              Underlying="{Binding Underlying1}"
                              ButtonBuySellUnderlyingCommand="{Binding ButtonBuySellUnderlyingCommand}"
                              CalculateUnderlyingCommand="{Binding CalculateUnderlyingCommand}"
                              ComboBoxColorgroupUnderlyingCommand="{Binding ComboBoxColorgroupUnderlyingCommand}"
                              CalculateCommandParameter="{Binding Underlying1.RowNumber}"/>

MainViewModel.cs

public MainViewModel()
{
ShowUnderlyingsRowCommand = new RelayCommand(ShowUnderlyingsRowExecute, ShowUnderlyingsRowCanExecute);
...
}
private void ButtonLoadExecute(object obj)
{
   ObservableCollection<Underlying> loadedUnderlyingList = PersistenceInstance.Load();
   if (loadedUnderlyingList != null)
   {
       UnderlyingList.Clear();
       foreach (Underlying underlying in loadedUnderlyingList)
       {
           UnderlyingList.Add(underlying);
       }
   }
}

Persistence.cs

public ObservableCollection<Underlying>? Load()
{
   FileStream fs;
   StreamReader sr;
   OpenFileDialog ofd = new OpenFileDialog()
   {
       InitialDirectory = @"C:\Temp",
       Filter = "Json (*.json)|*.json",
       Title = "Datei zum Öffnen auswählen : "
   };
   if (ofd.ShowDialog() == true)
   {
       if (!File.Exists(ofd.FileName))
       {
           MessageBox.Show("Datei " + ofd.FileName + " existiert nicht");
           return null;
       }
       fs = new FileStream(ofd.FileName, FileMode.Open);
       sr = new StreamReader(fs);
       jsonUnderlyingList = sr.ReadLine();
       sr.Close();
   }
   //Liste muss noch auf null geprüft werden, sonst Absturz, wenn zwar geöffnet aber nichts ausgewählt wird.
   ObservableCollection<Underlying>? newUnderlyingList = JsonConvert.DeserializeObject<ObservableCollection<Underlying>>(jsonUnderlyingList);
   return newUnderlyingList;
}

RelayCommand.cs

public class RelayCommand : ICommand
{
   //Private Fields
   private readonly Action<object> _executedHandler;
   private readonly Predicate<object> _canExecuteHandler;
   private Action onButtonPressed;
   //Constructors
   public RelayCommand(Action<object> execute) : this(execute, null)
   {
   }
   public RelayCommand(Action<object> execute, Predicate<object> canExecute)
   {
       if (execute == null)
           throw new ArgumentNullException("Execute kann nicht null sein");
       _executedHandler = execute;
       _canExecuteHandler = canExecute;
   }
   public RelayCommand(Action onButtonPressed)
   {
       this.onButtonPressed = onButtonPressed;
   }              
   //Methods
   public void Execute(object parameter)
   {
       _executedHandler(parameter);
   }
   public bool CanExecute(object parameter)
   {
       if (_canExecuteHandler == null)
           return true;
       return _canExecuteHandler(parameter);
   }
   //Events
   public event EventHandler? CanExecuteChanged
   {
       add { CommandManager.RequerySuggested += value; }
       remove { CommandManager.RequerySuggested -= value; }
   }
}

Vielen Dank für eure Hilfe!

Erstellt vor einem Jahr

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);                    
           }
       });
   }        
}

Erstellt vor einem Jahr

Nun habe ich den DecimalUpDown genommen und einfach die SpinnerButtons entfernt. Das Resulat: Es funktioniert und sieht wie eine normale TextBox aus. Fantastisch.

Erstellt vor einem Jahr

Ja das ist auch wieder wahr. Daher habe ich ein passendes Control von Extended.WPF.Toolkit implementiert, wobei ich nun aber wieder beim alten Problem angelangt bin, dass "LostFocus" zwar funktioniert aber das Drücken der "Return-Taste" nicht.

<xctk:AutoSelectTextBox Style="{StaticResource styleRowTextBox}" Grid.Column="3" Margin="2 4" Foreground="Black" Text="{Binding ElementName=userControlOptionsRow, Path=Option.KUl, Mode=TwoWay}" AutoSelectBehavior="OnFocus">
           <i:Interaction.Triggers>
              <i:EventTrigger EventName="LostFocus">
                  <i:InvokeCommandAction Command="{Binding ElementName=userControlOptionsRow, Path=CalculateCommand}"
                                         CommandParameter="{Binding ElementName=userControlOptionsRow, Path=CalculateCommandParameter}"/>
              </i:EventTrigger>
          </i:Interaction.Triggers>  
</xctk:AutoSelectTextBox>

Ist denn das normal, dass alles so zusammengebastelt werden muss? Ich möchte ja nur eine einfach TextBox in einem UserControl, dessen dahinterliegende Eigenschaft angepasst wird, wenn ich die Return-Taste drücke. Habe ich (noch) komplett etwas nicht verstanden oder ist das wirklich so mühsam? Bzw. woher weiss ich für was ich was einsetzen muss und wo die Grenzen von den einzelnen Elementen/Controls liegen usw....

Erstellt vor einem Jahr

Das ganze kann noch viel einfacher gelöst werden und funktioniert (StringFormat und UpdateSourceTrigger):

<TextBox Style="{StaticResource styleRowTextBox}" Grid.Column="3" Foreground="Black" Text="{Binding ElementName=userControlOptionsRow, Path=Option.KUl, Mode=TwoWay,StringFormat={}{##.##},UpdateSourceTrigger=PropertyChanged}">
           <TextBox.InputBindings>
               <KeyBinding Key="Return"
                           Command="{Binding ElementName=userControlOptionsRow, Path=CalculateCommand}" 
                           CommandParameter="{Binding ElementName=userControlOptionsRow, Path=CalculateCommandParameter}"/>
           </TextBox.InputBindings>
</TextBox>   
Erstellt vor einem Jahr

Freude herrscht! 
Das funktioniert mit deiner Variante bestens. Anscheinend hat sich dein weiterer Beitrag mit meinem überschnitten, sonst wäre es schon vorher klar gewesen. Vielen Dank für deine geduldige und sehr wertvolle Hilfe.

Wenn du mir noch eine letzte Frage erlaubst: Die TextBox aktualisiert den Wert erst (wie standardmässig vorgesehen) bei LostFocus. Also wenn ich den Focus wechsle, zur TextBox zurückkehre und dann "Return" drücke. Mit "PropertyChanged" geht das ohne den Fokuswechsel. Dann kann ich in die TextBox aber lediglich noch ganze Zahlen eingeben wie z.B. 45 und nicht etwa 45.15.

Erstellt vor einem Jahr

Da ich Anfänger bin, ist leider noch gar nichts klar 😉

Was du meinst habe ich verstanden, bei der Umsetzung happert es aber leider schon den ganzen Nachmittag. Liegt es an der Bindung im MainWindow.xaml an CalculateOptionCommand?

MainWindow.xaml:

<uc:OptionsRow Grid.Row="0" Visibility="{Binding Option1.OptionsRowIsVisible, Converter={StaticResource booleanToVisibilityConverter}}" DataContext="{Binding Option1}"
                                                                              CalculateOptionCommand="{Binding RelativeSource={RelativeSource AncestorType=vm:MainViewModel}, Path=CalculateOptionCommand}"/>

OptionsRow.xaml (TextBox im UserControl):

<TextBox Style="{StaticResource styleRowTextBox}" Grid.Column="3" Foreground="Black" Text="{Binding KUl, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
           <TextBox.InputBindings>
               <KeyBinding Key="Return" Command="{Binding CalculateOptionCommand}" CommandParameter="{Binding RowNumber, Mode=OneWay}"/>
           </TextBox.InputBindings>         
       </TextBox>

OptionsRow.xaml.cs (Codebehind vom UserControl):


       private static readonly DependencyProperty RowNumberProperty = DependencyProperty.Register("RowNumber", typeof(int), typeof(OptionsRow), new PropertyMetadata(0));
       private static readonly DependencyProperty CmdProperty = DependencyProperty.Register("CalculateOptionCommand", typeof(RelayCommand), typeof(OptionsRow));   

       public int RowNumber
       {
           get { return (int)GetValue(RowNumberProperty); }
           set { SetValue(RowNumberProperty, value); }
       }
       public RelayCommand CalculateOptionCommand
       {
           get { return (RelayCommand)GetValue(CmdProperty); }
           set { SetValue(CmdProperty, value); }
       } 
Erstellt vor einem Jahr

Vielen Dank für deine Hilfestellung!

Ursprünglich habe ich das mit dem DataContext ebenfalls so gelöst wie von dir vorgeschlagen. Da der Zugriff auf CalculateOptionCommand dann aber nicht funktionierte, habe ich das Ganze auf die von mir beschriebene Variante gelöst. Hast du mir einen Vorschlag wie ich das Problem lösen kann, damit der Zurgriff auf CalculateOptionCommand auch mit geändertem DataContext wieder funktioniert?