.NET Framework Developer Center > .NET Development Forums > Windows Presentation Foundation (WPF) > Using ComboBox with ViewModel Pattern does not work?
Ask a questionAsk a question
 

AnswerUsing ComboBox with ViewModel Pattern does not work?

  • Thursday, September 04, 2008 2:19 PMJohn Fredman_ Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Has Code
    Hi,

    I'm trying to control which ComboBoxItem in a ComboBox is selected by setting the IsSelected property of the underlying data items that ComboBox's ItemsSource points to. In other words, I want the ComboBox to work similar to the TreeView in Josh Smith's excellent example found here: Simplifying the WPF TreeView by Using the ViewModel Pattern

    However when I try to do this, the ComboBox fails to show the item that is selected according to the underlying data! But strangely enough, when I drop down the ComboBox, the correct item is pre-selected. So now I wonder why the ComboBox won't show the selected item until the drop down is expanded, and if there is any way to make the ComboBox display the selected item before this?

    I have included an example below. In the example the ComboBox is supposed to display a number of items of the Person class. The Person class has an IsSelected property which is supposed to control which Person is selected in the ComboBox. Therefore the ComboBox.ItemsContainerStyle binds the Person's IsSelected property to the corresponding ComboBoxItem's IsSelected property.

    I would have expected the Person instance with Name="Mike" to be shown as selected in the ComboBox, but it's not. And that's what I want to fix! Any suggestions are welcome.

    See XAML code below:

    <Window x:Class="WpfApplication1.Window1" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      Title="Window1" Height="300" Width="300" x:Name="Window"
      <StackPanel> 
        <ComboBox 
          ItemsSource="{Binding Path=Persons, ElementName=Window}" 
          DisplayMemberPath="Name"
        <!-- Bind ComboBoxItem's IsSelected to underlying Person's IsSelected --> 
        <
    ComboBox.ItemContainerStyle> 
            <Style TargetType="{x:Type ComboBoxItem}"
              <Setter 
                 Property="IsSelected" 
                 Value="{Binding Path=IsSelected, Mode=TwoWay}"/> 
            </Style> 
          </ComboBox.ItemContainerStyle> 
        </ComboBox> 
      </StackPanel> 
    </Window> 
     

    And the code behind:

    using System.Windows; 
    using System.Collections.ObjectModel; 
    using System.ComponentModel; 
     
    namespace WpfApplication1 
      public partial class Window1 : Window 
      { 
        public ObservableCollection<Person> Persons 
        { 
          get { return (ObservableCollection<Person>)GetValue(PersonsProperty); } 
          set { SetValue(PersonsProperty, value); } 
        } 
        public static readonly DependencyProperty PersonsProperty = 
          DependencyProperty.Register("Persons"typeof(ObservableCollection<Person>), typeof(Window1)); 
     
        public Window1() 
        { 
          InitializeComponent(); 
          // Create Persons. Pre-select the person named "Mike".
          Persons = new ObservableCollection<Person>()
               { 
    new Person("John"false), new Person("Mike"true) }; 
        } 
      } 
     
      public class Person : INotifyPropertyChanged 
      { 
        public event PropertyChangedEventHandler PropertyChanged; 
        public string Name { getset; } 
     
        private bool isSelected; 
        public bool IsSelected 
        { 
          get { return isSelected; } 
          set 
          { 
            if (value != isSelected) 
            { 
              isSelected = value; 
              OnPropertyChanged("IsSelected"); 
            } 
          } 
        } 
     
        public Person(string name, bool isSelected) 
        { 
          Name = name; 
          IsSelected = isSelected; 
        } 
     
        protected virtual void OnPropertyChanged(string propertyName) 
        { 
          if (PropertyChanged != null
          { 
            PropertyChanged(thisnew PropertyChangedEventArgs(propertyName)); 
          } 
        } 
      } 
     

    • Edited byJohn Fredman_ Thursday, September 04, 2008 2:21 PMClarification.
    • Edited byJohn Fredman_ Thursday, September 04, 2008 2:22 PMMisspelled.
    • Edited byJohn Fredman_ Thursday, September 04, 2008 2:25 PMMisspelled.
    • Edited byJohn Fredman_ Thursday, September 04, 2008 2:42 PMFixed formatting.
    •  

Answers

  • Monday, September 08, 2008 6:25 AMMarco Zhou Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    This is expected, because the ItemContainerStyle will be applied only when the container aka ComboBoxItem is generated which happens when you open the ComboBox dropdown menu, to workaround this issue, you could try define another dependency property called SelectedPerson, and let the ComoBox's SelectedValue property bind to SelectedPerson property as follows:

    <StackPanel>
      <ComboBox
        ItemsSource="{Binding Path=Persons, ElementName=Window}"
        SelectedValue="{Binding Path=SelectedPerson, ElementName=Window}"
        DisplayMemberPath="Name">
        <ComboBox.ItemContainerStyle>
          <Style TargetType="{x:Type ComboBoxItem}">
                <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}"/>
          </Style>
        </ComboBox.ItemContainerStyle>
      </ComboBox>
    </StackPanel>

    public partial class Window1 : Window
    {
        public ObservableCollection<Person> Persons
        {
            get { return (ObservableCollection<Person>)GetValue(PersonsProperty); }
            set { SetValue(PersonsProperty, value); }
        }

        public object SelectedPerson
        {
            get { return (object)GetValue(SelectedPersonProperty); }
            set { SetValue(SelectedPersonProperty, value); }
        }

        public static readonly DependencyProperty SelectedPersonProperty =
            DependencyProperty.Register("SelectedPerson", typeof(object), typeof(Window1), new UIPropertyMetadata(null));


        public static readonly DependencyProperty PersonsProperty =
          DependencyProperty.Register("Persons", typeof(ObservableCollection<Person>), typeof(Window1));

        public Window1()
        {
            InitializeComponent();
            // Create Persons. Pre-select the person named "Mike".
            Persons = new ObservableCollection<Person>() { new Person("John", false), new Person("Mike", true) };
            SelectedPerson = Persons[1];
        }
    }

    Hope this helps
    • Marked As Answer byMarco Zhou Wednesday, September 10, 2008 9:50 AM
    • Marked As Answer byMarco Zhou Wednesday, September 10, 2008 9:49 AM
    •  

All Replies

  • Monday, September 08, 2008 6:25 AMMarco Zhou Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    This is expected, because the ItemContainerStyle will be applied only when the container aka ComboBoxItem is generated which happens when you open the ComboBox dropdown menu, to workaround this issue, you could try define another dependency property called SelectedPerson, and let the ComoBox's SelectedValue property bind to SelectedPerson property as follows:

    <StackPanel>
      <ComboBox
        ItemsSource="{Binding Path=Persons, ElementName=Window}"
        SelectedValue="{Binding Path=SelectedPerson, ElementName=Window}"
        DisplayMemberPath="Name">
        <ComboBox.ItemContainerStyle>
          <Style TargetType="{x:Type ComboBoxItem}">
                <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}"/>
          </Style>
        </ComboBox.ItemContainerStyle>
      </ComboBox>
    </StackPanel>

    public partial class Window1 : Window
    {
        public ObservableCollection<Person> Persons
        {
            get { return (ObservableCollection<Person>)GetValue(PersonsProperty); }
            set { SetValue(PersonsProperty, value); }
        }

        public object SelectedPerson
        {
            get { return (object)GetValue(SelectedPersonProperty); }
            set { SetValue(SelectedPersonProperty, value); }
        }

        public static readonly DependencyProperty SelectedPersonProperty =
            DependencyProperty.Register("SelectedPerson", typeof(object), typeof(Window1), new UIPropertyMetadata(null));


        public static readonly DependencyProperty PersonsProperty =
          DependencyProperty.Register("Persons", typeof(ObservableCollection<Person>), typeof(Window1));

        public Window1()
        {
            InitializeComponent();
            // Create Persons. Pre-select the person named "Mike".
            Persons = new ObservableCollection<Person>() { new Person("John", false), new Person("Mike", true) };
            SelectedPerson = Persons[1];
        }
    }

    Hope this helps
    • Marked As Answer byMarco Zhou Wednesday, September 10, 2008 9:50 AM
    • Marked As Answer byMarco Zhou Wednesday, September 10, 2008 9:49 AM
    •