问题
I've a ComboBox Bound To a CollectionViewSource and I want that by default the selected index should be -1 (Because nothing should be selected) I've set SelectedIndex="-1" and the first item is still selected. Why?
My View xaml:
<Window.Resources>
<CollectionViewSource x:Key="States" Source="{Binding States}"/>
<CollectionViewSource x:Key="Cities" Source="{Binding Source={StaticResource States}, Path=Cities}"/>
</Window.Resources>
<ComboBox SelectedValue="{Binding Person.State}" Width="150" SelectedValuePath="StateInitial" DisplayMemberPath="StateName" ItemsSource="{Binding Source={StaticResource States}}"/>
<ComboBox SelectedValue="{Binding Person.City}" Width="150" SelectedValuePath="CityId" DisplayMemberPath="CityName" ItemsSource="{Binding Source={StaticResource Cities}}"/>
Models c#
public class State
{
public State(string stateInitial, string stateName, ICollection<City> cities)
{
StateInitial = stateInitial;
StateName = stateName;
Cities = cities;
}
public string StateInitial { get; set; }
public string StateName { get; set; }
public virtual ICollection<City> Cities { get; set; }
}
public class City
{
public int CityId { get; set; }
public string CityName {get;set;}
}
public class Person : INotifyPropertyChanged
{
#region Fields
private string state;
private int city;
#endregion
public string State
{
get
{
return state;
}
set
{
state = value;
OnPropertyChanged("State");
}
}
public int CityId
{
get
{
return city;
}
set
{
city = value;
OnPropertyChanged("City");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
ViewModel c#
public class MainWindowViewModel
{
public Person NewPerson { get; set; }
public List<State> States { get; private set; }
public MainWindowViewModel()
{
States = GetStates();
NewPerson = new Person();
}
public List<State> GetStates()
{
var stateList = new List<State>();
var TaxesCities = new List<City>();
TaxesCities.Add(new City(){ CityId = 1, CityName = "Dalles"});
TaxesCities.Add(new City(){ CityId = 2, CityName = "Houstin"});
stateList.Add(new State("TX", "Taxes" , TaxesCities));
var NYCities = new List<City>();
NYCities.Add(new City() { CityId = 3, CityName = "New York" });
NYCities.Add(new City() { CityId = 4, CityName = "Brooklyn" });
stateList.Add(new State("NY", "New York", NYCities));
return stateList;
}
}
回答1:
The problem is you're using a CollectionViewSource which is automatically selecting the first element in the list because it can't find an entry for "null". Personally I don't think it's a good idea to use a CollectionViewSource in this case...you're already keeping track of the current state in Person.State so why do you need a CollectionViewSource to keep track of the current item as well? Also what happens if you have two ComboBox's on the same page? You'll need to create another CollectionViewSource so that they don't both share the same index. And finally any logic involving the UI (e.g. managing a currently selected item) should be done in the view model so that it can be tested.
There are a few problems with your code that need fixing, the first is the fact that you're binding yoru first ComboBox's SelectedValue to Person.State. There is no "Person" member of the main view model, I suspect you meant NewPerson, correct? Secondly the Person.State field is a string but your ComboBox is binding to a list of States, so when the user selects a state WPF will call that state's ToString() member and set that at the value i.e. your Person.State field will wind up with something like "YourProjectName.State". You can fix your binding to do this, but a better solution is to modify your Person class to reference State and City directly:
private State state;
public State State
{
get {return state;}
set {state = value; OnPropertyChanged("State");}
}
private City city;
public City City
{
get {return city;}
set {city = value; OnPropertyChanged("City");}
}
(Note there was a problem in your original City member...you'd changed it to CityId but you were doing OnPropertyChanged on "City").
Apart from being much better performance-wise you won't have problems with the combobox setting the intial state incorrectly, and if you fix up the other problems in your XAML bindings your master-view will now work properly as well:
<ComboBox SelectedValue="{Binding NewPerson.State}" Width="150" DisplayMemberPath="StateName" ItemsSource="{Binding States}" />
<ComboBox SelectedValue="{Binding NewPerson.City}" DisplayMemberPath="CityName" ItemsSource="{Binding NewPerson.State.Cities}" />
If you really do want your Person class to use the state name and city id then you should create a PersonViewModel that holds a reference to it and does the conversion. This is, after-all, how you should be structuring your data in MVVM...Person is really a model object.
回答2:
You're initializing SelectedIndex to -1 but then binding SelectedValue which is overwriting the initial selection. You have 3 options:
- Get rid of SelectedIndex and set your initial SelectedValue to something that isn't in the list.
- Bind SelectedIndex to an integer in your view model and set it to -1 at run-time once the bindings have been established.
- Keep things as they are but set your SelectedValue's binding mode to OneWayToSource. This will prevent the binding from overwriting the initial value but it won't reflect any changes to SelectedIndex if you make them in the view model.
来源:https://stackoverflow.com/questions/26638526/combo-box-selected-index-1-bound-to-collection-view-source