简体   繁体   中英

WPF multiple Combobox binding with model

I have two comboboxes. One is grade category (eg elementary school, middle school, high school), and another for year. I want to make sure that the year combobox only shows the relevant ones, so that there there will be none of stuffs like 4th year of middle school. This is how I implemented my combobox.

Model

public class ComboBoxLogic {
    private List<string> _Grade_category;
    private List<string> _Year;

    // There is no set property because all sets are done within the class.
    public IList<string>  public IList<string> Grade_category
    {
        get
        {
            return _Grade_category;
        }
    }
    public IList<string> Year
    {
        get
        {
            return _Year;
        }
    }

    public void Grade_category_selected(string s)
    {
        string temp;
        _Grade.Clear();
        if (s.Equals("Elementary"))
        {
            for (int i = 1; i <= 5; i++)
            {
                temp = i.ToString();
                _Grade.Add(temp);
            }
            return;
        }
        else if (s.Equals("Middle"))
        {
            _Grade.Add("1");
            _Grade.Add("2");
            _Grade.Add("3");
            return;
        }
        else if (s.Equals("High"))
        {
            _Grade.Add("1");
            _Grade.Add("2");
            _Grade.Add("3");
            _Grade.Add("4");
            return;
        }
    }

    public ComboBoxLogic() {
       _Grade_category = new List<string>() {"Elementary", "Middle", "High"}
       _Year = new List<string>();
    }
}

XAML

<Combobox
    Name="Grade_category"
    ItemsSource="{Bind Path=Grade_category}"
    SelectedItem="{Bind Path=sGrade_category}" />
<Combobox
    Name="Year"
    ItemsSource="{Bind Path=Year}"
    SelectedItem="{Bind Path=sYear}" />

View Model

class ComboBoxViewModel {
    private ComboBoxLogic cbl;
    public ComboBoxViewModel() {
        cbl = new ComboBoxLogic();
    }
    public IList<string> Grade_category {
        get { return cbl.Grade_category; }
    }
    public string Grade_category_selected {
        set {cbl.Grade_category_selected(value);}
    }
    public IList<string> Year {
        get { return cbl.Year; }
    }
    public string Year_selected {
        set { }
    }
}

There are 2 problems involved in this.

  1. When I try this, it only works in the first time. From second selection of grade, year doesn't get updated properly. I'm thinking it's because I did not notify propertychanged for Year when grade_category changed. But, how do I do that?

  2. In my project, I will have multiple of these comboboxes where one combobox depends on another. And, all implementation of dependency will be written in ComboboxLogic class. Is there a way to make this shorter instead of having to write every properties for ItemsSource and SelectedItem for every combobox?

"When I try this, it only works in the first time. From second selection of grade, year doesn't get updated properly. I'm thinking it's because I did not notify propertychanged for Year when grade_category changed. But, how do I do that?"

Wrong, it's not about property changes. To propagate collection changes to the view the collection must implement INotifyCollectionChanged like ObservableCollection does. Replacing Years:IList with Years:ObservableCollection would do it.

"In my project, I will have multiple of these comboboxes where one combobox depends on another. And, all implementation of dependency will be written in ComboboxLogic class. Is there a way to make this shorter instead of having to write every properties for ItemsSource and SelectedItem for every combobox?"

In your specific case the trick is create a data model, that reflects the subject. All displayed data relates to a single object instance - the selected object (under different circumstances the solution would be to filter an ICollectionView ).

A grade has a name and lasts a specific number of years. The Grade class can look as follows:

Grade.cs

public class Grade : INotifyPropertyChanged
{
  public string Name { get; set; }

  public IEnumerable<int> Years { get; set; }

  public event PropertyChangedEventHandler PropertyChanged;
  protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
  {
    this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  }
}

ViewModel.cs

class ViewModel : INotifyPropertyChanged
{
  public ObservableCollection<Grade> Grades { get; set; }

  private Grade selectedGrade;
  public Grade SelectedGrade
  {
    get => this.selectedGrade;
    set
    {
      this.selectedGrade = value; 
      OnPropertyChanged();
    }
  }

  public ViewModel()
  {
    this.Grades = new ObservableCollection<Grade>
    {
      new Grade() {Name = "Elementary", Years = Enumerable.Range(1, 4)},
      new Grade() {Name = "Middle", Years = Enumerable.Range(1, 3)},
      new Grade() {Name = "High", Years = Enumerable.Range(1, 4)},
    };
  }

  public event PropertyChangedEventHandler PropertyChanged;
  protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
  {
    this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  }
}

MainWindow.xaml

<Window>
  <Window.DataContext>
    <ViewModel />
  </Window.DatContext>

  <StackPanel>
    <ComboBox ItemsSource="{Binding Grades}"
              SelectedItem="{Binding SelectedGrade}"
              DisplayMemberPath="Name" />
    <ComboBox ItemsSource="{Binding SelectedGrade.Years}" />
  </StackPanel>
</Window>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM