web-dev-qa-db-ger.com

WPF MVVM ComboBox SelectedItem oder SelectedValue funktioniert nicht

Update

Nach ein bisschen Nachforschung. Das Problem scheint zu sein, dass SelectedValue/SelectedItem vor dem Laden der Elementquelle ausgeführt wird. Wenn ich in einem Haltepunkt sitze und ein paar Sekunden warte, funktioniert es wie erwartet. Ich weiß nicht, wie ich dieses Problem umgehen werde.

Update beenden

Ich habe eine Anwendung, die in WPF MVVM mit einer ComboBox verwendet. Unten sehen Sie das ViewModel-Beispiel. Das Problem, das ich habe, ist, wenn wir unsere Seite verlassen und zurückwandern. Die ComboBox wählt nicht den aktuell ausgewählten Wert aus.

Modell anzeigen

public class MyViewModel
{
     private MyObject _selectedObject;
     private Collection<Object2> _objects;
     private IModel _model;

     public MyViewModel(IModel model)
    {
         _model = model;
         _objects = _model.GetObjects();
    }

    public Collection<MyObject> Objects
    {
         get
         {
              return _objects;
         }
         private set
         {
              _objects = value;
         }
     }

     public MyObject SelectedObject
     {
          get
          {
              return _selectedObject;
          }
          set
          {
               _selectedObject = value;
          }
      }
 }

In diesem Beispiel soll MyObject über zwei Eigenschaften (Text und Id) verfügen. Meine XAML für die ComboBox sieht so aus.

XAML

<ComboBox Name="MyComboBox" Height="23"  Width="auto" 
    SelectedItem="{Binding Path=SelectedObject,Mode=TwoWay}" 
    ItemsSource="{Binding Objects}"
    DisplayMemberPath="Text"
    SelectedValuePath="Id">

Egal auf welche Weise ich dies konfiguriere, wenn ich zur Seite zurückkomme und das Objekt wieder zusammengesetzt wird, wählt die ComboBox den Wert nicht aus. Das Objekt gibt jedoch das korrekte Objekt über das get in der Eigenschaft zurück.

Ich bin nicht sicher, ob dies nur ein Problem mit der Funktionsweise von ComboBox und MVVM ist. Die Textfeldbindung, die wir ausführen, funktioniert ordnungsgemäß.

40
cjibo

Haben Sie versucht, INotifyPropertyChanged in Ihrem Viewmodel zu implementieren, und lösen Sie dann das PropertyChanged -Ereignis aus, wenn die SelectedItem festgelegt wird.

Wenn dies an sich nicht behoben wird, können Sie das PropertyChanged -Ereignis manuell auslösen, wenn Sie zurück zur Seite navigieren. Dies sollte ausreichen, damit WPF sich selbst neu zeichnet und das richtige ausgewählte Element anzeigt.

27
Orion Edwards

Einstellung IsSynchronizedWithCurrentItem="True" hat für mich funktioniert!

28
rp7799

Sie müssen die ItemsSource-Eigenschaft VOR der SelectedItem-Eigenschaft einfügen. Ich bin vor ein paar Tagen auf einen Blog gestoßen, in dem das Thema erwähnt wurde.

20
GuestPerson

Ich hatte ähnliche Probleme und es wurde gelöst, indem sichergestellt wurde, dass ich IEquatable richtig implementierte. Wenn die Bindung erfolgt, wird versucht zu sehen, ob die Objekte übereinstimmen. Stellen Sie daher sicher, dass Sie Ihre Gleichheitsprüfung ordnungsgemäß implementieren.

11

In diesem Fall funktioniert die Selecteditem-Bindung nicht, da sich die Hash-ID der Objekte unterscheidet.

Eine mögliche Lösung ist:

Stellen Sie anhand der ausgewählten Element-ID das Objekt in der itemsource-Sammlung wieder her und setzen Sie die ausgewählte Elementeigenschaft auf.

Beispiel:

<ctrls:ComboBoxControlBase SelectedItem="{Binding Path=SelectedProfile, Mode=TwoWay}" ItemsSource="{Binding Path=Profiles, Mode=OneWay}" IsEditable="False" DisplayMemberPath="Name" />

Die an ItemSource gebundene Eigenschaft lautet:

public ObservableCollection<Profile> Profiles
{
   get { return this.profiles; }
   private set { profiles = value; RaisePropertyChanged("Profiles"); }
}

Die an SelectedItem gebundene Eigenschaft lautet:

public Profile SelectedProfile 
{
    get { return selectedProfile; }
    set
    {
        if (this.SelectedUser != null)
        {
            this.SelectedUser.Profile = value; 
            RaisePropertyChanged("SelectedProfile");  
        } 
    } 
}

Der Wiederherstellungscode lautet:

    [Command("SelectionChanged")]
    public void SelectionChanged(User selectedUser)
    {
        if (selectedUser != null)
        {
            if (selectedUser is User)
            {
                if (selectedUser.Profile != null)
                {
                    this.SelectedUser = selectedUser;
                    this.selectedProfile = this.Profiles.Where(p => p.Id == this.SelectedUser.Profile.Id).FirstOrDefault();
                    MessageBroker.Instance.NotifyColleagues("ShowItemDetails"); 
                }
            }
        }            
    }

Ich hoffe es hilft dir ... Ich habe viel Zeit damit verbracht, nach Antworten zu suchen, aber ich konnte nicht finden.

10
Breno P. Lucena

Beim Verlassen der aktuellen Seite wird die CollectionView, die der ItemsSource-Eigenschaft der ComboBox zugeordnet ist, gelöscht. Und da die Eigenschaft ComboBoxIsSyncronizedWithCurrent standardmäßig auf true gesetzt ist, werden die Eigenschaften SelectedItem und SelectedValue zurückgesetzt.
Dies scheint ein interner Datentyp in der Bindung zu sein. Wenn andere SelectedValue verwendet werden, anstatt sie an eine int -Eigenschaft des Viewmodels zu binden, wird dies funktionieren. Eine Abkürzung für Sie wäre, den Equals-Operator in MyObject zu überschreiben, sodass beim Vergleichen zweier MyObjects die tatsächliche Id Eigenschaften werden verglichen.

Noch ein Hinweis: Wenn Sie Ihre Viewmodels umstrukturieren und SelectedValue verwenden, verwenden Sie sie nur dann, wenn SelectedValuePath=Id, wobei Idint ist. Binden Sie bei Verwendung eines Zeichenfolgeschlüssels an die Text-Eigenschaft der ComboBox statt an SelectedValue.

4
Dave

Ich habe dieses Verhalten auch schon vorher bemerkt. Ich habe festgestellt, dass die SelectedIndex-Eigenschaft nicht denselben Fehler verursacht. Wenn Sie ViewModel umstrukturieren können, um den Index des ausgewählten Elements verfügbar zu machen, und an das binden, sollten Sie bereit sein.

3
Abe Heidebrecht

Ich hatte das gleiche Problem. Die Sache ist. Das ausgewählte Element weiß nicht, welches Objekt aus der Sammlung verwendet werden soll. Sie müssen also zu dem ausgewählten Artikel sagen, um den Artikel aus der Sammlung zu verwenden.

public MyObject SelectedObject
 {
      get
      {
          Objects.find(x => x.id == _selectedObject.id)
          return _selectedObject;
      }
      set
      {
           _selectedObject = value;
      }
 }

Ich hoffe das hilft.

2
StefanHa

Ich habe eine sehr einfache Antwort auf dieses Problem. Fügen Sie der View IsSynchronizedWithCurrentItem = "True" zuerst den folgenden Code hinzu. 

Nächstes Mal, wenn Sie dieser Eigenschaft ein neues Objekt im ViewModel zuweisen, sollte SelectedObject in dieser Eigenschaft und nicht im privaten Member gespeichert werden. 

Das Ansichtsmodell Proptery sollte so aussehen

    public Role SelectedObject 
    {
        get { return object; }
        set
        {
            if (value != null)
            {
                if (!object.Equals(value))
                {
                    object = value;
                    OnPropertyChanged(() => SelectedObject );
                }
            }
        }
    }

Dies sollte das Problem beheben.

2
Tim

Ich habe eine Weile mit diesem Thema gekämpft. In meinem Fall habe ich in complex type (List) als Elementquelle verwendet und einen KeyType als ausgewählten Wert verwendet. Beim Ladeereignis wurde der KeyType auf null gesetzt. Dies führte dazu, dass alles kaputt ging. Keines der Unterelemente wird aktualisiert, wenn der Schlüssel geändert wird. Es stellte sich heraus, dass beim Hinzufügen einer Prüfung, ob der vorgeschlagene Wert für KeyType nicht null ist, alles wie erwartet funktioniert hat.

    #region Property: SelectedKey
    // s.Append(string.Format("SelectedKey : {0} " + Environment.NewLine, SelectedKey.ToString()));

    private KeyType _SelectedKey = new KeyType();
    public KeyType SelectedKey
    {
        get { return _SelectedKey; }
        set
        {
            if(value != null )
                if (!_SelectedKey.Equals(value))
                {
                    _SelectedKey = value;
                    OnPropertyChanged("SelectedKey");
                }
        }
    }
    #endregion SelectedKey
1
Scott McFadden

Der Typ von SelectedValuePath und SelectedValue muss genau der gleiche sein.

Wenn zum Beispiel der Typ von SelectedValuePathInt16 ist und der Typ der Eigenschaft, die an SelectedValue gebunden ist, int ist, funktioniert dies nicht.

Ich verbringe Stunden damit, das herauszufinden, und deshalb beantworte ich hier nach so langer Zeit die Frage. Vielleicht sieht es ein anderer armer Kerl wie ich mit dem gleichen Problem.

1
Dummy01

Ich hatte dieses Problem mit einer ComboBox, die eine Liste von Farben (List <Brush>) anzeigt.
Die Auswahl einer Farbe war möglich, wurde jedoch nicht angezeigt, wenn die Auswahl geschlossen wurde (obwohl die Eigenschaft geändert wurde!)

Der Fix überschrieb die Equals (object obj) -Methode für den in der ComboBox (Brush) ausgewählten Typ, was nicht einfach war, da Brush versiegelt ist . Also habe ich eine Klasse geschrieben GleichheitBrush einen Pinsel enthalten und Equals implementieren:

public class EqualityBrush
{
    public SolidColorBrush Brush { get; set; }

    public override bool Equals(object o)
    {
        if (o is EqualityBrush)
        {
            SolidColorBrush b = ((EqualityBrush)o).Brush;
            return b.Color.R == this.Brush.Color.R && b.Color.G == this.Brush.Color.G && b.Color.B == this.Brush.Color.B;
        }
        else
            return false;
    }
}

Das Verwenden einer Liste meiner neuen EqualityBrush-Klasse anstelle der normalen Brush-Klasse hat das Problem behoben!

Meine Combobox XAML sieht aus wie das:

<ComboBox ItemsSource="{Binding BuerkertBrushes}" SelectedItem="{Binding Brush, Mode=TwoWay}" Width="40">
    <ComboBox.Resources>
        <DataTemplate DataType="{x:Type tree:EqualityBrush}">
            <Rectangle Width="20" Height="12" Fill="{Binding Brush}"/>
        </DataTemplate>
    </ComboBox.Resources>
</ComboBox>

Denken Sie daran, dass meine "Brush" -Eigenschaft in der ViewModel muss jetzt vom Typ GleichheitBrush sein!

1
JCH2k

Ich habe das Problem durch Hinzufügen eines Dispatchers im UserControl_Loaded-Ereignis behoben

 Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() =>
 {
     combobox.SelectedIndex = 0;
 }));
0
Peter T.

Geladenes Ereignis verwenden: 

private void cmb_Loaded(object sender, RoutedEventArgs e) {
    if (cmb.Items.Count > 0) cmb.SelectedIndex = 0;          
}

Für mich geht das.

0
Vinicius

IsSyncronizedWithCurrent = False bewirkt, dass es funktioniert.

0
Rohit

Dies kann die Art sein, wie Sie den DataContext auf die Seite anwenden. In WPF wird jedes Mal, wenn Sie zu einer Seite navigieren, alles neu initialisiert, der Konstruktor aufgerufen, Methoden geladen, alles. Wenn Sie also Ihren DataContext in Ihrer Ansicht einstellen, werden Sie zweifellos das vom Benutzer ausgewählte SelectedItem wegblasen. Um dies zu vermeiden, verwenden Sie die KeepAlive-Eigenschaft Ihrer Seiten.

<Page KeepAlive="True" ...>
   ...
</Page>

Dies führt dazu, dass nur das Loaded-Ereignis ausgelöst wird, wenn Sie zu einer bereits besuchten Seite zurückkehren. Sie müssen also sicherstellen, dass Sie DataContext auf Initialize setzen (entweder extern oder innerhalb des Konstruktors) und nicht auf Laden. 

Dies funktioniert jedoch nur für diese Instanz der Seite. Wenn Sie zu einer neuen Instanz dieser Seite navigieren, wird der Konstruktor erneut aufgerufen.

0
markti

ComboBox.SelectionBoxItem.ToString ()

0
Sean Campbell