2016-09-06 5 views
0

Ich versuche, eine ComboBox zu erstellen, die zwischen Objekten wechselt. Die allgemeine Aussage besteht darin, dass ein Objekt einen Schlüssel hat, der in der ComboBox erscheint, und eine Datenkomponente, die theoretisch alles sein kann. Die Datenkomponente ist komplex, während der Schlüssel nur eine Zeichenfolge ist. Für das folgende Beispiel ist Data nur ein Uri, es stellt sich heraus, dass der Typ der Daten keine Rolle spielt.Combobox SelectedItem wird Null

Die grundlegende Absicht besteht darin, das SelectedItem der ComboBox an das Model zu binden, damit die Daten des SelectedItem durch andere Interaktionen geändert werden können.

Der Code ist so eingerichtet, dass einige Elemente zur ComboBox hinzugefügt werden und dann SelectedItem als erstes Element ausgewählt wird. Das funktioniert gut. Wenn ich dann auf die Schaltfläche klicke, wird SelectedItem null zugewiesen, wo ich die Ausnahme auslöst.

Warum wird SelectedItem null zugewiesen?

Hier ist vollständiger Arbeitscode; Mein Ziel ist .NET 4.0, aber ich denke, es spielt keine Rolle. XAML folgt:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Data; 
using System.Windows.Documents; 
using System.Windows.Input; 
using System.Windows.Media; 
using System.Windows.Media.Imaging; 
using System.Windows.Navigation; 
using System.Windows.Shapes; 
using System.ComponentModel; 
using System.Collections.ObjectModel; 
using System.Collections.Specialized; 

namespace Sandbox 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window 
    { 
     public Model Model { get; set; } 

     public MainWindow() 
     { 
      InitializeComponent(); 
      this.DataContext = this; 

      Model = new Model(); 

      this.Model.Items.Add(
       new ObservableKeyValuePair<string, Uri>() 
       { 
        Key = "Apple", 
        Value = new Uri("http://apple.com") 
       }); 

      this.Model.Items.Add(
       new ObservableKeyValuePair<string, Uri>() 
       { 
        Key = "Banana", 
        Value = new Uri("http://Banana.net") 
       }); 

      this.Model.SelectedItem = this.Model.Items.First(); 
     } 

     private void Button_Click(object sender, RoutedEventArgs e) 
     { 
      this.Model.SelectedItem.Value = new Uri("http://cranberry.com"); 
     } 
    } 

    public class TrulyObservableCollection<T> : ObservableCollection<T> where T : INotifyPropertyChanged 
    { 
     public TrulyObservableCollection() 
     { 
      CollectionChanged += FullObservableCollectionCollectionChanged; 
     } 

     public TrulyObservableCollection(IEnumerable<T> pItems) 
      : this() 
     { 
      foreach (var item in pItems) 
      { 
       this.Add(item); 
      } 
     } 

     private void FullObservableCollectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 
     { 
      if (e.NewItems != null) 
      { 
       foreach (Object item in e.NewItems) 
       { 
        ((INotifyPropertyChanged)item).PropertyChanged += ItemPropertyChanged; 
       } 
      } 
      if (e.OldItems != null) 
      { 
       foreach (Object item in e.OldItems) 
       { 
        ((INotifyPropertyChanged)item).PropertyChanged -= ItemPropertyChanged; 
       } 
      } 
     } 

     private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e) 
     { 
      NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, sender, sender, IndexOf((T)sender)); 
      OnCollectionChanged(args); 
     } 
    } 

    public class ObservableKeyValuePair<TKey, TValue> : 
     INotifyPropertyChanged, 
     IEquatable<ObservableKeyValuePair<TKey, TValue>> 
    { 
     public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; 

     protected virtual void OnPropertyChanged(string propertyName) 
     { 
      if (PropertyChanged != null) 
      { 
       PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); 
      } 
     } 

     public override bool Equals(object rhs) 
     { 
      var obj = rhs as ObservableKeyValuePair<TKey, TValue>; 

      if (obj != null) 
      { 
       return this.Key.Equals(obj.Key); 
      } 

      return false; 
     } 

     public bool Equals(ObservableKeyValuePair<TKey, TValue> other) 
     { 
      return this.Key.Equals(other.Key); 
     } 

     public override int GetHashCode() 
     { 
      return this.Key.GetHashCode(); 
     } 

     protected TKey _Key; 
     public TKey Key 
     { 
      get 
      { 
       return _Key; 
      } 
      set 
      { 
       if (value is INotifyPropertyChanged) 
       { 
        (value as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(KeyChanged); 
       } 

       _Key = value; 

       OnPropertyChanged("Key"); 
      } 
     } 
     void KeyChanged(object sender, PropertyChangedEventArgs e) 
     { 
      OnPropertyChanged("Key"); 
     } 

     protected TValue _Value; 
     public TValue Value 
     { 
      get 
      { 
       return _Value; 
      } 
      set 
      { 
       if (value is INotifyPropertyChanged) 
       { 
        (value as INotifyPropertyChanged).PropertyChanged += new PropertyChangedEventHandler(ValueChanged); 
       } 

       _Value = value; 

       OnPropertyChanged("Value"); 
      } 
     } 
     void ValueChanged(object sender, PropertyChangedEventArgs e) 
     { 
      OnPropertyChanged("Value"); 
     } 
    } 

    public class Model : INotifyPropertyChanged 
    { 
     public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; 

     public Model() 
     { 
      Items = new TrulyObservableCollection<ObservableKeyValuePair<string, Uri>>(); 
     } 

     protected virtual void OnPropertyChanged(string propertyName) 
     { 
      if (PropertyChanged != null) 
      { 
       PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); 
      } 
     } 

     public TrulyObservableCollection<ObservableKeyValuePair<string, Uri>> Items { get; set; } 
     public ObservableKeyValuePair<string, Uri> _SelectedItem = null; 
     public ObservableKeyValuePair<string, Uri> SelectedItem 
     { 
      get 
      { 
       return Items.FirstOrDefault(x => _SelectedItem != null && x.Key == _SelectedItem.Key); 
      } 
      set 
      { 
       if (value == null) 
       { 
        throw new Exception("This is the problem"); 
       } 

       if (_SelectedItem != value) 
       { 
        _SelectedItem = value; 
        OnPropertyChanged("SelectedItem"); 
       } 
      } 
     } 
    } 
} 

XAML:

<Window x:Class="Sandbox.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> 
     <Button Click="Button_Click" Content="Clsick Me"/> 
     <ComboBox IsEditable="False" ItemsSource="{Binding Model.Items}" SelectedItem="{Binding Model.SelectedItem, Mode=TwoWay}"> 
      <ComboBox.ItemTemplate> 
       <DataTemplate> 
        <TextBlock Text="{Binding Key}"> 
        </TextBlock> 
       </DataTemplate> 
      </ComboBox.ItemTemplate> 
     </ComboBox> 
    </StackPanel> 
</Window> 

ich an insgesamt bin verlieren versuchen zu erklären, warum "Wert" null ist.

+0

Was würden Sie gerne auf Knopfdruck machen? Möchten Sie das 'SelectedItem' in ein anderes ändern oder einfach die' value' Eigenschaft des aktuellen 'SelectedItem' ändern – Gopichandar

+0

@Gopichandar Ich möchte eine Eigenschaft von SelectedItem ändern; Im Beispiel ist diese Eigenschaft Value. Ich ändere keine von Equals verwendete Eigenschaft. –

Antwort

0

Das zugrunde liegende Problem ist bei der Umsetzung der Selector.OnItemsChanged. Gegen Ende der Methode wird die SelectedItem nulled.

ich um dieses arbeitete durch eine neue ComboBox Klasse ableiten, die den aktuellen SelectedItemOnItemsChanged, spart überschreibt, ruft base.OnItemsChanged und setzt dann den SelectedItem. Dies kann die Weiterleitung eines "InhibitEvents" -Flags in das Modell erfordern, wenn der SelectedItem-Übergang von gültig => null => gültig nicht erwünscht ist.

0

Der Wert Null wird, weil Sie ein ausgewähltes Element Wert zuzuweisen versuchen, die nicht in der Sammlung ist

+0

Das SelectedItem ist bereits in der Sammlung. Ich versuche, eine Eigenschaft eines vorhandenen SelectedItem zu ändern. Der Wert wird in der Equals() - Logik ebenfalls nicht verwendet. –

Verwandte Themen