Hello. Faced a problem. I make an application on WPF using the MVVM pattern. I work with the database using the Entity Framework. There are 2 tables: Employees (name, position id) and Posts (id, title). I want the form with the staff, when I choose one of them, to display all the information about it (name, position, etc.). I implemented this by creating the EmployeViewModel class:

class EmployeesViewModel : INotifyPropertyChanged { OpticsEntities db; private Employee selectedEmployees; public ObservableCollection<Employee> 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(); } })); } } } 

and presentation:

  <ListBox Grid.Column="0" ItemsSource="{Binding Employees}" SelectedItem="{Binding SelectedEmployees}" Margin="0,0,17,0"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Margin="5"> <TextBlock FontSize="18" Text="{Binding Path=lastname}" /> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> <StackPanel Grid.Row="1" Orientation="Horizontal"> <Button Command="{Binding AddCommand}">Add</Button> <Button Command="{Binding SaveCommand}" CommandParameter="{Binding SelectedEmployees}">Save</Button> </StackPanel> <StackPanel Grid.Column="1" DataContext="{Binding SelectedEmployees}"> <TextBlock FontWeight="Bold" Text="Имя" /> <TextBox> <TextBox.Text> <Binding Path="firstname" NotifyOnValidationError="True"> <Binding.ValidationRules> <DataErrorValidationRule /> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox> <TextBlock FontWeight="Bold" Text="Фамилия" /> <TextBox> <TextBox.Text> <Binding Path="lastname" NotifyOnValidationError="True"> <Binding.ValidationRules> <DataErrorValidationRule /> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox> <TextBlock FontWeight="Bold" Text="Должность" /> <TextBox> <TextBox.Text> <Binding Path="post" NotifyOnValidationError="True"> <Binding.ValidationRules> <DataErrorValidationRule /> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox> </StackPanel> 

Everything works fine. You can add a new employee and save. Window with employees

But the problem is this. The field "Position" is connected with the table "Positions" by the foreign key (id). And I want, instead of the text field with the post id, a ComboBox, in which the names of all posts from the "Posts" table would be found. By default, it would be the position to which the employee corresponds, but you could open the ComboBox and assign it to another. After clicking on Save, everything would be saved in the database.

The most I could do was create a ComboBox that would display these posts. But for this, I had to make another collection in the ViewModel and access it in ItemsSource. Naturally, whatever I chose in it, there will be no connection with the employee, since the data is not connected.

How can I make the ComboBox display positions from the Positions table, and if one of them was selected, it would be assigned to the Position field of the Employee? Let me remind you that the tables are connected by a foreign key according to the position id.

    1 answer 1

    There are a lot of options for implementation, beautiful and not so, but if you try to do with the participation of an additional collection of model submissions for each position, as you described.

    An artificial example, written to demonstrate the approach to using the subtleties of binding ComboBox to data, is by no means an example of implementing the correct MVVM.

    Subject domain entity models:

     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; } } 

    Presentation models:

     public class EmployeeViewModel : NotificationObject { public EmployeeViewModel(Employee model, IEnumerable<Post> posts) { Model = model; var postViewModels = new List<PostViewModel>(); foreach (var post in posts) { postViewModels.Add(new PostViewModel(post)); } PostViewModels = postViewModels; } public Employee Model { get; set; } public IEnumerable<PostViewModel> PostViewModels { get; private set; } } public class PostViewModel : NotificationObject { public PostViewModel(Post model) { Model = model; } public Post Model { get; set; } } 

    The NotificationObject class simply implements INotifyPropertyChanged . EmployeeViewModel enters the DataContext window.

    Markup:

     <StackPanel> <TextBlock Text="{Binding Model.Name}" /> <ComboBox ItemsSource="{Binding PostViewModels}" DisplayMemberPath="Model.Name" SelectedValue="{Binding Model.PostID}" SelectedValuePath="Model.ID" /> </StackPanel> 

    The example is working, the Combobox will be displayed in the ComboBox and a valid job ID will be assigned to the Employee object when selected in the ComboBox .

    ComboBox has some extremely useful properties.

    1. DisplayMemberPath is the path to the entity property in the DataContext of the ComboBox list item, which will be displayed in the drop-down list and in the main body of the ComboBox .
    2. SelectedValue - along with SelectedItem you can bind to the SelectedValue property, the value of which is taken along a certain path from the SelectedItem .
    3. SelectedValuePath - the path from SelectedItem , which is taken SelectedValue .

    In my example, when selected in a ComboBox , PostViewModel is PostViewModel in its SelectedItem property, and the required job ID is already in the SelectedValue property. This property is also zabindeno on the PostID Employee entity.

    I hope the principle is clear and it will be possible to transfer it to your case.

    • Thank you so much. I implemented it in myself, a bit differently, but everything works) - Artem Gutirchik