[wpf] WPF MVVM ComboBox SelectedItem or SelectedValue not working

Update

After a bit of investigating. What seems to be the issue is that the SelectedValue/SelectedItem is occurring before the Item source is finished loading. If I sit in a break point and wait a few seconds it works as expected. Don't know how I'm going to get around this one.

End Update

I have an application using in WPF using MVVM with a ComboBox. Below is the ViewModel Example. The issue I'm having is when we leave our page and migrate back the ComboBox is not selecting the current Value that is selected.

View Model

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

For the sake of this example lets say MyObject has two properties (Text and Id). My XAML for the ComboBox looks like this.

XAML

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

No matter which way I configure this when I come back to the page and the object is reassembled the ComboBox will not select the value. The object is returning the correct object via the get in the property though.

I'm not sure if this is just an issue with the way the ComboBox and MVVM pattern works. The text box binding we are doing works correctly.

This question is related to wpf mvvm combobox

The answer is


I have noticed this behavior before as well. I have noticed that the SelectedIndex property doesn't cause the same bug. If you can restructure your ViewModel to expose the index of the selected item, and bind to that, you should be good to go.


I have had similar issues and it was solved by making sure I was implementing IEquatable properly. When the binding occurs, it is trying to see if the objects match so make sure you are properly implementing your equality checking.


The type of the SelectedValuePath and the SelectedValue must be EXACTLY the same.

If for example the type of SelectedValuePath is Int16 and the type of the property that binds to SelectedValue is int it will not work.

I spend hours to find that, and that's why I am answering here after so much time the question was asked. Maybe another poor guy like me with the same problem can see it.


I had this problem with a ComboBox displaying a list of colors ( List<Brush> ).
Selecting a color was possible but it wasnt displayed when the selection closed (although the property was changed!)

The fix was overwriting the Equals(object obj) method for the type selected in the ComboBox (Brush), which wasnt simple because Brush is sealed. So i wrote a class EqualityBrush containing a Brush and implementing Equals:

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

Using a List of my new EqualityBrush class instead of normal Brush class fixed the problem!

My Combobox XAML looks like this:

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

Remember that my "Brush"-Property in the ViewModel now has to be of Type EqualityBrush!


I was fighting with this issue for a while. In my case I was using in complex type (List) as the Item Source and was using a KeyType as the selected value. On the load event, the KeyType was getting set to null. This caused everything to break. None of the sub elements would get updated when the key changed. It turned out that when I added a check to make sure the proposed value for KeyType was not null, everything worked as expected.

    #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

When leaving the current page, the CollectionView associated with the ItemsSource property of the ComboBox is purged. And because the ComboBox IsSyncronizedWithCurrent property is true by default, the SelectedItem and SelectedValue properties are reset.
This seems to be an internal data type issue in the binding. As others suggested above, if you use SelectedValue instead by binding to an int property on the viewmodel, it will work. A shortcut for you would be to override the Equals operator on MyObject so that when comparing two MyObjects, the actual Id properties are compared.

Another hint: If you do restructure your viewmodels and use SelectedValue, use it only when SelectedValuePath=Id where Id is int. If using a string key, bind to the Text property of the ComboBox instead of SelectedValue.


I had the same problem. The thing is. The selected item doesnt know which object it should use from the collection. So you have to say to the selected item to use the item from the collection.

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

I hope this helps.


I have a very simply answer for this problem. First add the following code to the View IsSynchronizedWithCurrentItem="True".

Next when ever you assign a new object in the ViewModel to that Property SelectedObject should be saved to that Property and not the private member.

The viewmodel Proptery should look like this

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

This should fix the issue.


ComboBox.SelectionBoxItem.ToString()


Setting IsSynchronizedWithCurrentItem="True" worked for me!


IsSyncronizedWithCurrent=False will make it work.


In this case, the selecteditem bind doesn't work, because the hash id of the objects are different.

One possible solution is:

Based on the selected item id, recover the object on the itemsource collection and set the selected item property to with it.

Example:

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

The Property binded to ItemSource is:

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

The property binded to SelectedItem is:

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

The recovery code is:

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

I hope it helps you. I spent a lot of my time searching for answers, but I couldn´t find.


I solved the problem by adding dispatcher in UserControl_Loaded event

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

It could be the way you are applying the DataContext to the Page. In WPF, everytime you navigate to a Page everything gets re-initialized, constructor gets called, loaded methods, everything. so if you are setting your DataContext inside your View you will no doubt be blowing away that SelectedItem that the user selected. In order to avoid that use the KeepAlive property of your pages.

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

This will result in only the Loaded event being fired when navigating back to a page you have already visited. So you will need to ensure that you are setting the DataContext on Initialize (either externally or within the constructor) rather than Load.

However, this will only work for that instance of the Page. If you navigate to a new instance of that page it constructor will be called again.


Use Loaded event:

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

It works for me.


You need to put the ItemsSource property BEFORE the SelectedItem property. I came across a blog a few days ago mentioning the issue.


You can also bind your SelectedIndex to a property in your ViewModel and manipulate your SelectedItem that way:

        public int SelectedIndex
        {
            get { return _selectedIndex; }
            set
            {
                _selectedIndex = value;
                OnPropertyChanged();
            }    
        }

And in your XAML:

<ComboBox SelectedIndex="{Binding SelectedIndex,Mode=TwoWay}" ... >

Examples related to wpf

Error: the entity type requires a primary key Reportviewer tool missing in visual studio 2017 RC Pass command parameter to method in ViewModel in WPF? Calling async method on button click Setting DataContext in XAML in WPF How to resolve this System.IO.FileNotFoundException System.Windows.Markup.XamlParseException' occurred in PresentationFramework.dll? Binding an Image in WPF MVVM How to bind DataTable to Datagrid Setting cursor at the end of any text of a textbox

Examples related to mvvm

Vue.js—Difference between v-model and v-bind Pass command parameter to method in ViewModel in WPF? [Vue warn]: Cannot find element Binding an Image in WPF MVVM Binding ComboBox SelectedItem using MVVM What is difference between MVC, MVP & MVVM design pattern in terms of coding c# How to make all controls resize accordingly proportionally when window is maximized? Add directives from directive in AngularJS Wpf DataGrid Add new row Close Window from ViewModel

Examples related to combobox

How to set combobox default value? PHP code to get selected text of a combo box How to add items to a combobox in a form in excel VBA? How add items(Text & Value) to ComboBox & read them in SelectedIndexChanged (SelectedValue = null) Get Selected value of a Combobox jQuery "on create" event for dynamically-created elements How to get the selected item of a combo box to a string variable in c# HTML combo box with option to type an entry twitter bootstrap autocomplete dropdown / combobox with Knockoutjs C# winforms combobox dynamic autocomplete