Laden...

Forenbeiträge von TreaxP Ingesamt 11 Beiträge

26.12.2023 - 22:34 Uhr

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);
   }
}
26.12.2023 - 16:27 Uhr

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.

26.12.2023 - 13:30 Uhr

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!

01.11.2023 - 18:09 Uhr

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

16.04.2023 - 14:54 Uhr

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

16.04.2023 - 13:25 Uhr

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....

16.04.2023 - 11:05 Uhr

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>   
15.04.2023 - 21:55 Uhr

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.

15.04.2023 - 16:50 Uhr

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); }
       } 
15.04.2023 - 13:14 Uhr

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?

10.04.2023 - 16:50 Uhr

Hallo zusammen!
Wäre jemand so freundlich und würde mir zu nachfolgenden zwei Fragestellungen weiterhelfen:

Ich habe ein UserControl (OptionsRow) das verschiedene Elemente wie TextBoxen, ComboBoxen, Labels usw. beinhaltet und im MainWindow.xaml 10x verwendet wird. Nun soll sich z.B. die TextBox des UserControl1 an die Eigenschaft Option1.Eigenschaft im ViewModel (MVVM) binden, das UserControl2 an die Eigenschaft Option2.Eigenschaft usw.
Frage 1: Gelöst habe ich das Problem zwar mit nachfolgendem Code, frage mich aber ob das nicht einfacher ginge, als im MainWindow.xaml nochmals sämtliche Eigenschaften vom ViewModel an die DependencyProperties vom UserControl zu binden. Für die bisherigen Eigenschaften ist das ja kein Problem, aber es kommen noch einige weitere hinzu und ich frage mich ob das der richtige Weg ist?

MainWindow.xaml:

<uc:OptionsRow Grid.Row="0" Visibility="{Binding Option1.OptionsRowIsVisible, Converter={StaticResource booleanToVisibilityConverter}}" 
RowNumber="{Binding Option1.RowNumber}" BS="{Binding Option1.BS}" PC="  {Binding Option1.PC}" Pr="{Binding Option1.Pr}" KUl="{Binding Option1.KUl}"/>

UserControl.xaml (z.B. eine TextBox):

<TextBox Style="{StaticResource styleRowTextBox}" Grid.Column="3" Foreground="Black" Text="{Binding KUl, RelativeSource={RelativeSource 		AncestorType=UserControl}, UpdateSourceTrigger=LostFocus}">
           <!--<i:Interaction.Triggers>
               <i:EventTrigger EventName="KeyDown">                    
                   <i:InvokeCommandAction Command="{Binding CalculateOptionCommand}"
                                          CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=RowNumber}"/>
               </i:EventTrigger>
           </i:Interaction.Triggers>-->
           <TextBox.InputBindings>
               <KeyBinding Key="Return" Command="{Binding CalculateOptionCommand}"
                               CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=RowNumber, Mode=OneWay}"/>
           </TextBox.InputBindings>         
       </TextBox>

UserControl.xaml.cs (z.B. die Property KUl die an die TextBox bindet)

private static readonly DependencyProperty KUlProperty = DependencyProperty.Register("KUl", typeof(float), typeof(OptionsRow), 
           new FrameworkPropertyMetadata(0.0f) { BindsTwoWayByDefault = true });
           
public float KUl
       {
           get { return (float)GetValue(KUlProperty); }
           set { SetValue(KUlProperty, value); }
       }

Frage 2: Wie bringe ich es hin, dass der Command in der TextBox mit Drücken der Enter-Taste ausgeführt wird? Mit "KeyBinding" funktioniert das zwar, aber nur wenn ich zuerst den Fokus wechsle ("LostFocus") und dann zur ursprünglichen TextBox zurückkehre und Enter drücke. "PropertyChanged" als UpdateSourceTrigger habe ich auch schon versucht, aber dann kann ich keinen Punkt oder Komma in der TextBox eingeben (die TextBox bindet KUl die ein float ist). Mit "Interaction.Triggers" von Xaml Behaviors habe ich das auch schon versucht, aber dann bringe ich es nicht hin, dass "KeyDown" nur auf Return reagiert.

Vielen Dank für eure Hilfe.