WPF - MVVM - ComboBox SelectedItem

前端 未结 4 648
春和景丽
春和景丽 2021-02-07 05:21

I have ViewModel(implemented INotifyPropertyChanged) in the background and class Category which has only one property of type string

相关标签:
4条回答
  • 2021-02-07 05:44

    If Combobox is bound to object class of the View Model, while the SelectionBoxItem of the sender object (in the SelectionChanged in code behind) has not that type, it means it's still loading.

    ComboBox combo = sender as ComboBox;
    if (combo.SelectionBoxItem.GetType() == typeof(BindedClass))
    {
                // Not loading
    }
    
    0 讨论(0)
  • 2021-02-07 05:45

    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;
    }
    
    0 讨论(0)
  • 2021-02-07 05:46

    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.

    0 讨论(0)
  • 2021-02-07 05:55

    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");
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题