Страницы

Поиск по вопросам

четверг, 23 мая 2019 г.

Как в WPF MVVM сделать привязку данных к ComboBox

Здравствуйте. Столкнулся с проблемой. Делаю приложение на WPF с использованием паттерна MVVM. Работу с базой данных осуществляю с помощью Entity Framework. Есть 2 таблицы: Сотрудники(ФИО, id должности) и Должности(id, название). Я хочу, чтобы на форме с сотрудниками, когда я выбираю одного из них, выводилась вся информация о нём (ФИО, должность и тд). Это я осуществил, создав класс EmployeesViewModel:
class EmployeesViewModel : INotifyPropertyChanged { OpticsEntities db; private Employee selectedEmployees; public ObservableCollection Employees { get; set; }
public Employee SelectedEmployees { get { return selectedEmployees; } set { selectedEmployees = value; OnPropertyChanged("SelectedEmployees"); } }
public EmployeesViewModel() { db = new OpticsEntities(); db.Employees.Load(); Employees = db.Employees.Local; }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string prop = "") { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
private RelayCommand addCommand; private RelayCommand saveCommand; private RelayCommand removeCommand; private RelayCommand changeCommand;
public RelayCommand AddCommand { get { return addCommand ?? (addCommand = new RelayCommand(obj => { Employee employees = new Employee(); Employees.Insert(0, employees); SelectedEmployees = employees; })); } }
public RelayCommand SaveCommand { get { return saveCommand ?? (saveCommand = new RelayCommand(obj => { Employee employees = obj as Employee; if (employees != null) { db.Employees.Add(employees); db.SaveChanges(); } })); } }
public RelayCommand ChangeCommand { get { return changeCommand ?? (changeCommand = new RelayCommand(obj => { Employee employees = obj as Employee; if (employees != null) { var changeEmployees = db.Employees.Find(employees.id); employees = changeEmployees; db.SaveChanges(); } })); } } }
и представление:



Всё отлично работает. Можно добавить нового сотрудника и сохранить.
Но проблема вот в чем. Поле "Должность" связано с таблицей "Должности" по внешнему ключу (id). И я хочу, чтобы вместо текстового поля с id должности был ComboBox, в котором бы находились названия всех должностей из таблицы "Должности". По умолчанию в нем была бы та должность, которой соответствует сотрудник, но можно было бы открыть ComboBox и присвоить ему другую. После нажатия на Save всё сохранилось бы в базе.
Максимум, что я смог сделать, это создать ComboBox, в котором будут отображаться эти должности. Но для этого пришлось сделать еще одну коллекцию во ViewModel и обращаться уже к ней в ItemsSource. Естественно, что бы я в ней не выбрал, никакой связи с сотрудником не будет, так как данные не связаны получается.
Как мне сделать, чтобы в ComboBox отображались должности из таблицы Должности, и при выборе одной из них она бы присваивалась полю Должность у Сотрудника? Напомню, что таблицы связаны внешним ключом по id должности.


Ответ

Тут достаточно много вариантов реализации, красивых и не очень, но если попробовать сделать с участием дополнительной коллекции моделей представления для каждой должности, как вы описали.
Пример искусственный, написан для демонстрации подхода к использованию тонкостей привязки ComboBox к данным, ни в коем случае не является примером реализации правильного MVVM.
Модели сущностей предметной области:
public class Employee { public string Name { get; set; } public int PostID { get; set; } }
public class Post { public int ID { get; set; } public string Name { get; set; } }
Модели представления:
public class EmployeeViewModel : NotificationObject { public EmployeeViewModel(Employee model, IEnumerable posts) { Model = model; var postViewModels = new List(); foreach (var post in posts) { postViewModels.Add(new PostViewModel(post)); } PostViewModels = postViewModels; }
public Employee Model { get; set; } public IEnumerable PostViewModels { get; private set; } }
public class PostViewModel : NotificationObject { public PostViewModel(Post model) { Model = model; }
public Post Model { get; set; } }
Класс NotificationObject просто реализует INotifyPropertyChanged. В DataContext окна попадает EmployeeViewModel
Разметка:

Пример работающий, в Combobox будут отображаться названия должностей, при этом в объект Employee при выборе в ComboBox будет присваиваться валидный идентификатор должности.
У ComboBox есть несколько крайне полезных свойств.
DisplayMemberPath - путь к свойству сущности в DataContext элемента списка ComboBox, который будет отображаться в выпадающем списке и в основном теле ComboBox SelectedValue - наравне с SelectedItem можно биндиться к свойству SelectedValue, значение которого берётся по определённому пути из SelectedItem SelectedValuePath - путь от SelectedItem, по которому берётся SelectedValue
В моём примере при выборе в ComboBox в его свойство SelectedItem попадает PostViewModel, а в свойство SelectedValue попадает уже искомый идентификатор должности. Это свойство и забиндено на PostID сущности Employee
Надеюсь, принцип понятен и удастся перенести это на ваш случай.

Комментариев нет:

Отправить комментарий