Microsoft Developer Network > Domovská stránka fór > Windows Presentation Foundation (WPF) > Using ComboBox with ViewModel Pattern does not work?
Odeslat dotazOdeslat dotaz
 

OdpovědětUsing ComboBox with ViewModel Pattern does not work?

  • 4. září 2008 14:19John Fredman_ Uživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaile
     Obsahuje kód
    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)); 
          } 
        } 
      } 
     

Odpovědi

  • 8. září 2008 6:25Marco Zhou Uživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaile
     Odpovědět
    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
    • Označen jako odpověďMarco Zhou 10. září 2008 9:50
    • Označen jako odpověďMarco Zhou 10. září 2008 9:49
    •  

Všechny reakce

  • 8. září 2008 6:25Marco Zhou Uživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaileUživatelské medaile
     Odpovědět
    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
    • Označen jako odpověďMarco Zhou 10. září 2008 9:50
    • Označen jako odpověďMarco Zhou 10. září 2008 9:49
    •