Страницы

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

суббота, 7 марта 2020 г.

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

#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 должности.
    


Ответы

Ответ 1



Тут достаточно много вариантов реализации, красивых и не очень, но если попробовать сделать с участием дополнительной коллекции моделей представления для каждой должности, как вы описали. Пример искусственный, написан для демонстрации подхода к использованию тонкостей привязки 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. Надеюсь, принцип понятен и удастся перенести это на ваш случай.

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

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