Reputation: 37
I am trying to get multiple ComboBoxes populated with data from a specific record. I can get one of them functioning correctly, but I think a conceptual error is preventing me from getting the second one working right. I have searched various threads for SelectedItem, SelectedValue, and SelectedIndex and tried fashioning my code after the guidance provided. Obviously I am doing something wrong, but not sure what. I am very much a novice with everything I've learned being self-taught from the internet.
I am using an Entity Framework 6 Database First data model. I have one table (Master) which has a foreign key to link to the Shift and another for the Personnel tables. Shift table has the fields of ShiftId (PK) which has Identity set to true and Shift which is just a string. Personnel table has PersonnelId (PK) which has Identity set to false (I'm populating the IDs manually from elsewhere) and first and last name fields.
The desired flow of my project is: 1) Get the current record, 2) Populate the Shift ComboBox, 3) Display the shift associated with the current record, 4) Populate the Personnel ComboBox, 5) Display the person associated with the current record, 6) Save any changes to the record (changes established by selecting a different value for either of the ComboBoxes).
My ViewModel is utilizing INotifyPropertyChanged, and I think I have implemented this correctly since the Shift ComboBox is functional.
1) Getting the desired record works fine. It is then stored as "CurrentRecord"
2) Populating the Shift ComboBox works fine.
In my Model I have:
private ICollection<Gen_All_Shift> shiftList;
public ICollection<Gen_All_Shift> GetShiftList()
{
shiftList = context.Gen_All_Shift.OrderBy(p => p.ShiftId)
.ToArray();
return shiftList;
}
In my ViewModel I have:
public ICollection<Gen_All_Shift> ShiftList
{
get { return context.GetShiftList(); }
}
private Gen_All_Shift selectedShift;
public Gen_All_Shift SelectedShift
{
get { return selectedShift; }
set
{
if (value != selectedShift)
{
selectedShift = value;
NotifyPropertyChanged("SelectedShift");
}
}
}
Then the XAML is:
<ComboBox x:Name="cboShift"
ItemsSource="{Binding ShiftList}"
DisplayMemberPath="Shift"
SelectedIndex="{Binding CurrentRecord.ShiftIdFk,
UpdateSourceTrigger=PropertyChanged}" />
3) Displaying the current shift works as long as I have the binding to SelectedIndex. If I use SelectedItem or SelectedValue, the ComboBox is blank when the screen loads. (I've also tried binding to SelectedShift.ShiftId but that doesn't give me the desired results.) SelectedIndex will allow me to change the selection and allows me to properly save the changes to the actual database. I'm not sure what I am doing wrong that prevents SelectedItem or SelectedValue from working. Not understanding this is what is causing me to have problems with the personnel combobox I believe.
4) Populating the Personnel ComboBox works.
In my Model I have:
private ICollection<Personnel_All_PersonnelList> personnelList;
public ICollection<Personnel_All_PersonnelList> GetPersonnelList()
{
personnelList = context.Personnel_All_PersonnelList.Where(p=>p.Active == true)
.OrderBy(p => p.LastName)
.ThenBy(p => p.FirstName)
.ToArray();
return personnelList;
}
In my ViewModel I have:
public ICollection<Personnel_All_PersonnelList> PersonnelList
{
get
{
return context.GetPersonnelList();
}
}
private Personnel_All_PersonnelList selectedPerson;
public Personnel_All_PersonnelList SelectedPerson
{
get { return selectedPerson; }
set
{
if (value != selectedPerson)
{
selectedPerson = value;
NotifyPropertyChanged("SelectedPerson");
}
}
}
Then in my XAML I have:
<ComboBox x:Name="cboTechnician"
ItemsSource="{Binding PersonnelList}"
DisplayMemberPath="LastName"
SelectedIndex="{Binding CurrentRecord.PersonnelIdFk,
UpdateSourceTrigger=PropertyChanged}" />
5) Displaying the current person does not work no matter which way I try (SelectedIndex, SelectedItem, SelectedValue). I'm guessing the reason SelectedIndex does not work the way it did with the shift is because the index number does not line up properly with the PersonnelId, so the system has no idea which one is actually selected.
6) Saving the changes to the records works if I change the Shift only. If I select a person in the Personnel ComboBox and try to save, I get a System Null Exception. I assume this is caused by the same issue that prevents SelectedIndex from working with the personnel combobox.
I think if someone can point me in the right direction as to why I am unable to get the binding to SelectedItem working right, I'll be able to figure out how to fix the Personnel ComboBox.
Upvotes: 2
Views: 2379
Reputation: 37
@Mike Eason - Thanks, that gave me what I needed!
The ViewModel for the Shift now looks like this:
private ICollection<Gen_All_Shift> shiftList;
public ICollection<Gen_All_Shift> ShiftList
{
get { return shiftList; }
set { shiftList = value; NotifyPropertyChanged("ShiftList"); }
}
private Gen_All_Shift selectedShift;
public Gen_All_Shift SelectedShift
{
get { return selectedShift; }
set { selectedShift = value; NotifyPropertyChanged("SelectedShift"); }
}
public void LoadShiftList()
{
ShiftList = context.GetShiftList();
SelectedShift = ShiftList.Where(p => p.ShiftId == CurrentRecord.ShiftIdFk)
.FirstOrDefault();
}
With a call to the LoadShiftList() in the ViewModel constructor.
Then the XAML now has the binding set to the SelectedItem as desired:
<ComboBox x:Name="cboShift"
ItemsSource="{Binding ShiftList}"
DisplayMemberPath="Shift"
SelectedItem="{Binding SelectedShift}" />
Previously I had been trying to set SelectedShift.ShiftId == CurrentRecord.ShiftIdfk. That kept giving me a null exception. Pointing out to use the .FirstOrDefault() put me back on the right path. Thanks again.
Upvotes: 0
Reputation: 9713
The first thing you tried was actually correct however there are a couple of things that let you down.
For example:
get { return context.GetShiftList(); }
Generally, this is not a good idea. Anyone calling this property will invoke the database call every time which will hurt performance and simply isn't needed.
A better idea would be to instead call a method to load the ShiftList
property, such as this:
public void ReloadShiftList()
{
//TODO: Your logic here
ShiftList = ...
}
Note: Be sure to implement INotifyPropertyChanged
on your ShiftList
property in order to update the view whenever this property is changed.
As for your ComboBox
, binding to the SelectedItem
is certainly the preferred method. The reason why the selected item was blank on first load was because SelectedShift
was set to null. To remedy this, you simply need to set the SelectedShift
after you have loaded the ShiftList
.
ShiftList = ...
SelectedShift = ShiftList.FirstOrDefault();
By setting the SelectedShift
to the .FirstOrDefault()
of the ShiftList
, you will guarantee that there will always be something selected in the ComboBox
(unless there are no items in the ShiftList
).
Finally, your binding would look like this:
<ComboBox ItemsSource="{Binding ShiftList}"
SelectedItem="{Binding SelectedShift}"
... />
Upvotes: 1