WPF - MVVM - ComboBox SelectedItem

55,410

Solution 1

The category you are setting in this line -

NodeCategory = some_list_of_other_objects.Category;

and one present in your Categories collection(ItemsSource="{Binding Categories}") should be referring to same object. If they are not then SelectedItem won't work.

Solution 1 -

You can also try to use SelectedValuePath like this -

<ComboBox x:Name="categoryComboBox" 
          ItemsSource="{Binding Categories}"
          DisplayMemberPath="Name" 
          SelectedValuePath="Name" 
          SelectedValue="{Binding NodeCategory, Mode=TwoWay}" />

and in code you can do something like this -

private string _NodeCategory;
public string NodeCategory
{
    get
    {
        return _NodeCategory;
    }
    set
    {
        _NodeCategory = value;
        OnPropertyChanged("NodeCategory");
    }
}

and set selected item like this -

NodeCategory = some_list_of_other_objects.Category.Name;

and use selected value like this -

Category selectedCategory = 
   some_list_of_other_objects.FirstOrDefault(cat=> cat.Name == NodeCategory);

or

Category selectedCategory = 
   Categories.FirstOrDefault(cat=> cat.Name == NodeCategory);

Solution 2 -

Another possible solution can be -

NodeCategory = 
  Categories.FirstOrDefault(cat=> cat.Name == some_list_of_other_objects.Category.Name);

this way your NodeCategory property will have the reference of an object in Categories collection and SelectedItem will work.

Solution 2

Your XAML needs a couple of modifications but I think the real problem is with the code you have posted which I don't think is telling the full story. For starters, your combobox ItemSource is bound to a property called Categories but you do not show how this property is coded or how your NodeCategory property is initially synced with the item.

Try using the following code and you will see that the selected item is kept in sync as the user changes the value in the combobox.

XAML

<Window x:Class="WpfApplication1.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>
    <ComboBox x:Name="categoryComboBox"
              Grid.Column="1"
              Grid.Row="3"
              Grid.ColumnSpan="2"
              Margin="10"
              ItemsSource="{Binding Categories}"
              DisplayMemberPath="Name"
              SelectedItem="{Binding NodeCategory}" />
    <Label Content="{Binding NodeCategory.Name}" />
</StackPanel>

Code-behind

public partial class MainWindow : Window, INotifyPropertyChanged
{
    private ObservableCollection<Category> _categories = new ObservableCollection<Category>
    {
        new Category { Name = "Squares"},
        new Category { Name = "Triangles"},
        new Category { Name = "Circles"},
    };

    public MainWindow()
    {
        InitializeComponent();
        NodeCategory = _categories.First();
        this.DataContext = this;
    }

    public IEnumerable<Category> Categories
    {
        get { return _categories; }
    }

    private Category _NodeCategory;
    public Category NodeCategory
    {
        get
        {
            return _NodeCategory;
        }
        set
        {
            _NodeCategory = value;
            OnPropertyChanged("NodeCategory");
        }
    }

    public void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

[Serializable]
public class Category : INotifyPropertyChanged
{
    private string _Name;
    [XmlAttribute("Name")]
    public string Name
    {
        get
        {
            return _Name;
        }
        set
        {
            _Name = value;
            OnPropertyChanged("Name");
        }
    }

    public void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    [field: NonSerialized]
    public event PropertyChangedEventHandler PropertyChanged;
}

Solution 3

From my little example:

Note: This is setting just a string (or a category from another list), but the basics should be same here:

Basically this is done:

private void button1_Click(object sender, RoutedEventArgs e)
{
    (this.DataContext as ComboBoxSampleViewModel).SelectCategory("Categorie 4");
}

Here is my XAML:

<Grid>
    <ComboBox Height="23" HorizontalAlignment="Left" Margin="76,59,0,0"   
              Name="comboBox1" VerticalAlignment="Top" Width="120" 
              ItemsSource="{Binding List.Categories}" 
              DisplayMemberPath="Name" 
              SelectedValue="{Binding NodeCategory, Mode=TwoWay}" />
    <Button Content="Button" Height="27" HorizontalAlignment="Left" 
            Margin="76,110,0,0" Name="button1" VerticalAlignment="Top" 
            Width="120" Click="button1_Click" />
</Grid>

and in the ViewModel of the Window

class ComboBoxSampleViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(string info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }

    public CategoryList List { get; set; }

    public ComboBoxSampleViewModel()
    {
        this.List = new CategoryList();
        NodeCategory = List.Selected;
    }

    private ComboBoxSampleItemViewModel nodeCategory;
    public ComboBoxSampleItemViewModel NodeCategory
    {
        get
        {
            return nodeCategory;
        }
        set
        {
            nodeCategory = value;
            NotifyPropertyChanged("NodeCategory");
        }
    }

    internal void SelectCategory(string p)
    {
        this.List.SelectByName(p);
        this.NodeCategory = this.List.Selected;
    }
}

With the help of this little class:

public class CategoryList
{
    public ObservableCollection<ComboBoxSampleItemViewModel> Categories { get; set; }
    public ComboBoxSampleItemViewModel Selected { get; set; }
    public CategoryList()
    {
        Categories = new ObservableCollection<ComboBoxSampleItemViewModel>();

        var cat1 = new ComboBoxSampleItemViewModel() { Name = "Categorie 1" };
        var cat2 = new ComboBoxSampleItemViewModel() { Name = "Categorie 2" };
        var cat3 = new ComboBoxSampleItemViewModel() { Name = "Categorie 3" };
        var cat4 = new ComboBoxSampleItemViewModel() { Name = "Categorie 4" };

        Categories.Add(cat1);
        Categories.Add(cat2);
        Categories.Add(cat3);
        Categories.Add(cat4);

        this.Selected = cat3;
    }

    internal void SelectByName(string p)
    {
        this.Selected = this.Categories.Where(s => s.Name.Equals(p)).FirstOrDefault();
    }
}

And this Item ViewModel

public class ComboBoxSampleItemViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(string info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }

    private string name;

    public string Name 
    { 
        get
        {
            return name;
        }
        set
        {
            name = value;
            NotifyPropertyChanged("Name");
        }
    }
}
Share:
55,410

Related videos on Youtube

Bip
Author by

Bip

Updated on July 09, 2022

Comments

  • Bip
    Bip almost 2 years

    I have ViewModel(implemented INotifyPropertyChanged) in the background and class Category which has only one property of type string. My ComboBox SelectedItem is bind to an instance of a Category. When i change the value of instance, SelectedItem is not being updated and Combobox is not changed.

    EDIT: code

    Combobox:

    <ComboBox x:Name="categoryComboBox" Grid.Column="1"  Grid.Row="3" Grid.ColumnSpan="2" 
              Margin="10" ItemsSource="{Binding Categories}"
              DisplayMemberPath="Name" SelectedValue="{Binding NodeCategory, Mode=TwoWay}"/>
    

    Property:

    private Category _NodeCategory;
    public Category NodeCategory
    {
        get
        {
            return _NodeCategory;
        }
        set
        {
            _NodeCategory = value;
            OnPropertyChanged("NodeCategory");
        }
    }
    
    [Serializable]
    public class Category : INotifyPropertyChanged
    {
        private string _Name;
        [XmlAttribute("Name")]
        public string Name
        {
            get
            {
                return _Name;
            }
            set
            {
                _Name = value;
                OnPropertyChanged("Name");
            }
        }
    
        public void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    
        [field:NonSerialized]
        public event PropertyChangedEventHandler PropertyChanged;
    }
    

    and what I am trying is: when I set

    NodeCategory = some_list_of_other_objects.Category;
    

    to have that item selected in Combobox with appropriate DisplayMemberPath

    • Mare Infinitus
      Mare Infinitus almost 12 years
      Can you post some code please, especially xaml
    • Andy
      Andy almost 12 years
      Does binding the SelectedItem property work instead of selected value?
    • Andy
      Andy almost 12 years
      Try adding the update source trigger to your bindings UpdateSourceTrigger=PropertyChanged
    • Bip
      Bip almost 12 years
      i've edited the post again with explanation at the end.
    • Bip
      Bip almost 12 years
      tried that already :) no success :/ because i change the value in the backend
    • Mare Infinitus
      Mare Infinitus almost 12 years
      you want to have an item selected by an external list? i have a small sample on how to achieve this... one moment
  • Mare Infinitus
    Mare Infinitus almost 12 years
    Thats exactly what i wanted to post now.
  • Bip
    Bip almost 12 years
    let me explain a bit more. I have list of categories. Category1, Category2...etc. I have a datagrid right near it and i clicked datagrid row. Ok. Now, when i clicked it everything else is populated with its data(i have few textboxes) BUT combobox selected item is blank
  • Bip
    Bip almost 12 years
    it is impossible in this stage of project to add logics to UI, sorry. But tnx for answer. voted up :)
  • akjoshi
    akjoshi almost 12 years
    @PredragPejic in case your ComboBox and DataGrid are two different lists, having different objects then using SelectedValuePath would be a good option.
  • akjoshi
    akjoshi almost 12 years
    @PredragPejic Added another solution which won't require you to make much changes in your current code.
  • Bip
    Bip almost 12 years
    that is not the issue. the issue is because NodeCategory has been changed, but the combobox selected item is not obviously.
  • akjoshi
    akjoshi almost 12 years
    Yes, because the Category object present in NodeCategory is not present in Categories(bound to ComboBox) collection.
  • Bip
    Bip almost 12 years
    thank you man, it is working now. i changed SelectedValue to SelectedItem and removed SelectedValuePath. i will buy you a beer if we ever meet :D cheers!
  • akjoshi
    akjoshi almost 12 years
    @PredragPejic Thanks, Glad it helped.
  • Mare Infinitus
    Mare Infinitus almost 12 years
    okay, no problem. the solution is very similar as far as i can see
  • Bahamut
    Bahamut over 11 years
    Thank you so much. Not setting the SelectedValuePath screwed me up, but with your help i got it working =)
  • akjoshi
    akjoshi over 11 years
    @Bahamut Glad it helped you :)
  • afterxleep
    afterxleep over 7 years
    This answer enabled me to get going so +1. For a start-to-finish example, I've blogged about it here: technical-recipes.com/2017/…
  • Luca Ziegler
    Luca Ziegler over 6 years
    Yes DisplayMemberPath is indispensable when you use objects
  • scsfdev
    scsfdev over 6 years
    "should be referring to same object" save my day! Thanks for the info.