Laden...

ListBox DataTempalte für Items mit ComboBox funktioniert nicht richtig

Erstellt von sth_Weird vor 8 Jahren Letzter Beitrag vor 8 Jahren 990 Views
S
sth_Weird Themenstarter:in
469 Beiträge seit 2007
vor 8 Jahren
ListBox DataTempalte für Items mit ComboBox funktioniert nicht richtig

Hallo,
ich habe in meinem Customer-ViewModel eine ObservableCollection<Product> Products. Außerdem gibt es noch eine Liste List<Product> AllProducts die alle Produkte liefert (hieraus kann sich der Customer quasi seine Products auswählen).
Die Producs Liste binde ich im View an eine ListBox. Die einzelnen ListBoxItems möchte ich als ComboBoxen anzeigen (hierfür habe ich ein DataTemplate definiert), ItemsSource der ComboBox soll immer AllProducts sein, SelectedItem dann halt der aktuelle Eintrag des ListBoxItems (also das Element aus Products das zu dem ListBoxItem gehört).
Leider bekomme ich das nicht zum Laufen...Ich bekomme es soweit hin, dass ich eine ComboBox sehe und diese AllItems anzeigt. Aber wie bekomme ich es jetzt hin, dass der ListBoxItem auch der SelectedItem der ComboBox ist, und noch wichtiger, wenn ich in der ComboBox einen anderen Eintrag auswähle, soll dieser sich ja auch in der ObservableCollection niederspiegeln. Das tut es zur Zeit nicht (ich habe mich an die CollectionChanged der ObservableCollection angehängt, dort fliegt kein Event, wenn ich einen anderen Eintrag in der ComboBox auswähle 😦 )
Hier ein Teil meiner XAML, hier habe ich versucht SelectedItems auf . zu setzen. da das ja das aktuelle Binding ist oder???


<ListBox Name="LbProducts" ItemsSource="{Binding Path=Products}" Margin="10,2,5,2" HorizontalAlignment="Left" VerticalAlignment="Center" Width="400">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <Grid Margin="0,2">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*" />
                                    <ColumnDefinition Width="50" />
                                </Grid.ColumnDefinitions>
                                <ComboBox Name="CboAllProducts"  Grid.Column="0" ItemsSource="{Binding Path=DataContext.AllProducts, ElementName=LbProducts}" SelectedItem="{Binding Path=., Mode=TwoWay}" />
                                ..hier soll noch ein Button zum Löschen hin                            </Grid>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>

hier ein Teil des Products-Properties


public ObservableCollection<Product> Products
    {
      get
      {
        if (this.products == null)
        {
          this.products= new ObservableCollection<Products>(myModel.Products);
          this.products.CollectionChanged +=
            delegate(object sender, NotifyCollectionChangedEventArgs e)
            {
              switch (e.Action)
              {
                 // hier ist Code drin, der bei Aendungen der ObservableCollection das Model anpassen soll...wie geschrieben der Debugger springt hier nie rein, wenn ich in der Combo ein anderes Element auswähle...

Die AllProducts Liste wird vom Konstruktur erzeugt und bleibt immer gleich.

Kann mir jemand sagen, wo der Fehler liegt???

grüße & danke
sth_Weird

++++++++++++++++++++~+
Fluchen ist die einzige Sprache, die jeder Programmierer perfekt beherrscht


Linux is for free...if your time is worth nothing
++++++++++++++++++++~+

1.378 Beiträge seit 2006
vor 8 Jahren

Also um es zum Verständnis zu vervollständigen:

  1. Du hast eine ListBox in der die "ausgewählten Produkte" dargestellt werden
  2. Irgendwo (extern) fügst du dann ein neues "Produkt" deiner Liste hinzu, welches vermutlich am Anfang leer sein wird.
  3. Jeder Eintrag in der ListBox wird durch eine ComboBox dargestellt, welche selbst widerum alle Produkte zur Auswahl hat.
  4. Dadurch kann man dann in der Liste der ausgewählten Produkte diese noch verändern, was sich klarerweise auch in der Liste der ausgewählten Produkte widerspiegeln soll.
  5. (später soll neben jedem Item in der Liste noch ein Button zum Entfernen hinzukommen)

Ich vermute mal das das Problem bei der Art des Bindings des SelectedItems zu suchen ist.

  • Mein erster Versuch wäre, die ListBox-Produkte in eine Wrapper-Klasse "ObservableList<SelectedProduct>" zu stecken. SelectedProduct ist in dem Fall ein ViewModel, welches das Binding erleichtern soll und selbst nur ein Property "Product" (inkl. NotifyPropertyChanged) hat.

  • Das Binding auf SelectedItem würde dann nicht auf "Path=." sondern "Path=Product" in SelectedProduct schauen und jede Manipulation würde dann wieder ordentlich in alle Richtungen durchschlagen.

Lg, XXX

S
sth_Weird Themenstarter:in
469 Beiträge seit 2007
vor 8 Jahren

da ich nicht weiß, ob ich deine Interpretation richtig verstanden habe, hier vielleicht noch mal anders:
Datenbanktechnisch habe ich eine Tabelle Customers und eine Tabelle Products (n:m Beziehung).
Mein ViewModel bildet einen Customer ab. In der ListBox möchte ich nun quasi die Verbinding herstellen zischen diesem Customer und der Products Tabelle, allerdings kann ein Product mehrfach verknüpft werden. Aber in seinen Eigenschaften nicht verändert, d.h. das Product-Objekt muss immer gleich bleiben!
D.h. für mein Programm, in der ComboBox stelle ich alle Products zur Verfügung die in der Products Tabelle stehen, ich wähle davon eines aus und genau dieses Product muss dann in die Products-ObservableCollection rein, die an die ListBox gebunden ist. Ändere ich die Auswahl in der ComboBox, dann muss in der ObservableCollection eigentlich genau dieses Objekt ausgetauscht werden.
Und genau hier hänge ich, also dass meine Änderungen an der ComboBox nicht in die ObservableCollection übergehen...
Ich habe auch ein Command, das einen neuen Eintrag (erst mal null) in der ObservableCollection erstellt. Dieser wird dann auch gleich als neues Item mit ComboBox (ohne Auswahl) in meiner ListBox angezeigt. Aber wenn ich dann was in der ComboBox auswähle, dann wandert diese Auswahl nicht in die ObservableCollection zurück, dort bleibt das vorher eingefügte null stehen...aber auch wenn ich statt null ein (existierendes) Dummy-Product in die ObservableCollection eintrage und im View ein anderes Product in der Combo auswähle, ich bekomme im ViewModel rein garnichts davon mit, die ObservableCollection wirf keine Events (ich hätte hier ein Replace erwartet).
Warum weiß ich nicht, und ich finde auch beim googlen keine Lösung, obwohl ich denke, dass das was ich suche doch ein gängiges Anwengsszenario sein sollte.
Wie mir da ein ViewModel helfen kann, das verstehe ich nicht. "." sollte doch schon der DataContext des ListBoxItems sein, oder?
Das mit dem Delete-Button wird recht trivial, wenn der Rest mal geht.

gruß
sth_Weird

++++++++++++++++++++~+
Fluchen ist die einzige Sprache, die jeder Programmierer perfekt beherrscht


Linux is for free...if your time is worth nothing
++++++++++++++++++++~+

1.378 Beiträge seit 2006
vor 8 Jahren

Ich kanns nicht erklären warum "Path=." den Wert nicht mehr zurückschreibt aber es ist nunmal so: Man kann nur die Eigenschaften des Context verändern, nicht aber den Context selbst.

Hier ein Beispiel zum Veranschaulichen:

<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
   <StackPanel Orientation="Vertical">

      <TextBox Text="{Binding TextProp1, UpdateSourceTrigger=PropertyChanged}"></TextBox>
      <TextBox DataContext="{Binding TextProp2, UpdateSourceTrigger=PropertyChanged}"
               Text="{Binding Path=., UpdateSourceTrigger=PropertyChanged}"></TextBox>

   </StackPanel>
</Window>


   public partial class MainWindow : Window
   {
      private string _textProp1;
      private string _textProp2;

      public MainWindow()
      {
         InitializeComponent();

         DataContext = this;

         TextProp1 = "Text1";
         TextProp2 = "Text2";
      }

      public string TextProp1
      {
         get { return _textProp1; }
         set { _textProp1 = value; }
      }

      public string TextProp2
      {
         get { return _textProp2; }
         set { _textProp2 = value; }
      }
   }

TextBox1.Text ist an DataContext.TextProp1 gebunden
TextBox2.Text ist an DataContext gebunden, welcher selbst auf DataContext.TextProp2 geändert wurde.

Wenn du nun einen Breakpoint bei beiden Settern setzt, siehst du das (1) verändert wird aber (2) nicht.

Lg, XXX

//Edit: Um meinen vorigen Vorschlag nochmal zu verdeutlichen: Deine Items in der Listbox müssen in einem Wrapper-Objekt sein, damit du eine Property hast die du verändern kannst. Du kannst nicht den Kontext selbst ändern aber die Eigenschaften sehrwohl.

5.299 Beiträge seit 2008
vor 8 Jahren

Man kann nur die Eigenschaften des Context verändern, nicht aber den Context selbst. Ich bin mir nicht sicher ob der genauen Begrifflichkeiten, aber ich glaube, ich darf widersprechen: In https://www.vb-paradise.de/index.php/Thread/65927-die-vier-Views-in-Wpf/ zeige ich u.a. einen Joinin-View, und einen m:n-View, wo man sehr wohl den Fremdschlüssel verändert, indem man in der Combobox-Column eine Auswahl trifft.

Es ist trickreich einzustellen (und glaub ein Wpf-Bug ist zu umgehen), aber es ist möglich.

Der frühe Apfel fängt den Wurm.