baozi
baozi

Reputation: 709

WPF ComboBox selectedValue binding fails

I have an ObservableCollection<Employee>..., an ObservableCollection<Departments> and Enployee is defined as

public class Employee
{
    [Key]
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime Birthday { get; set; }
    public int DepId { get; set; }

    [ForeignKey("DepId")]
    public virtual Departments Departments { get; set; } 

}

and Departmentis defined as

public class Departments
{
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual IEnumerable<Employee> Employees { get; set; } 
}

In the database I have

enter image description here

It looks like last ComboBox fails to locate the DepId which belongs to the corresponding Employee!! Any ideas guys?

    <DataGrid  Name="DataGrid1" Grid.Row="3" Margin="10,0,10,10"
               RenderOptions.ClearTypeHint="Enabled"
               TextOptions.TextFormattingMode="Display"
               CanUserAddRows="False"
               CanUserDeleteRows="False"
               SelectionUnit="FullRow" 
               AutoGenerateColumns="false"
               SelectedItem="{Binding CurrentSelectedEmployee, Mode=TwoWay}"
               ItemsSource="{Binding Employees, Mode=TwoWay}">
        <DataGrid.Columns>
            <!--Column 1: Employee Id-->
            <DataGridTextColumn Header="Emplyee Id" Binding="{Binding Id}"/>

            <!--Column 2: First Name-->
            <DataGridTextColumn Header="First Name" Binding="{Binding FirstName}"/>

            <!--Column 3: Last Name-->
            <DataGridTextColumn Header="Last Name" Binding="{Binding LastName}"/>

            <!--Column 4: Birth Day-->
            <DataGridTemplateColumn Header="Birth Day" >
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <DatePicker SelectedDate="{Binding Birthday}"  BorderThickness="0" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>

            <!--Column 5: Department Id-->
            <DataGridTemplateColumn Header="Department" >
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <ComboBox ItemsSource="{Binding Departments}"
                                  DisplayMemberPath="Name" SelectedValuePath="Id" SelectedValue="{Binding DepId}" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>

        </DataGrid.Columns>
    </DataGrid>

It looks like last ComboBox fails to locate the DepId which belongs to the corresponding Employee!! Any ideas guys?

enter image description here

UPDATE: My viewModel

public class MainViewModel : ViewModelBase
{
    private readonly ManagerDbContext _context = new ManagerDbContext();
    public MainViewModel()
    {

    }
    private IEnumerable<Departments> _departments;
    public ObservableCollection<Departments> Departments
    {
        get
        {
            return
                new ObservableCollection<Departments>(_context.Departments);
        }
        set
        {
            _departments = value;
            RaisePropertyChanged("Departments");
        }
    }

    private IEnumerable<Employee> _employee;

    public IEnumerable<Employee> Employees
    {
        get
        {
            return
                new ObservableCollection<Employee>(_context.Employees.Include(e => e.Department));
        }
        set
        {
            _employee = value;
            RaisePropertyChanged("Employees");
        }
    }
    private Employee _currentSelectedEmployee;
    public Employee CurrentSelectedEmployee
    {
        get
        {
            return _currentSelectedEmployee;
        }
        set
        {
            _currentSelectedEmployee = value;
            RaisePropertyChanged("CurrentSelectedEmployee");
        }
    }

}

Upvotes: 0

Views: 2039

Answers (3)

Rachel
Rachel

Reputation: 132558

The correct way to set a ComboBox's ItemsSource in a DataGrid would be something like this :

// simplified datacontext class
public class MainViewModel
{
    public ObservableCollection<Employee> Employees;
    public ObservableCollection<Department> Departments;
}
<DataGrid Name="DataGrid1" ItemsSource="{Binding Employees}">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Emplyee Id" Binding="{Binding Id}"/>
        <DataGridTextColumn Header="First Name" Binding="{Binding FirstName}"/>
        <DataGridTextColumn Header="Last Name" Binding="{Binding LastName}"/>

        <DataGridTemplateColumn Header="Department" >
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <!-- Note the change in the ItemsSource Binding -->
                    <ComboBox ItemsSource="{Binding ElementName=DataGrid1, Path=DataContext.Departments}"
                          DisplayMemberPath="Name" SelectedValuePath="Id" SelectedValue="{Binding DepId}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>

    </DataGrid.Columns>
</DataGrid>

By using an ElementName (or RelativeSource) binding here, we only need to maintain one copy of the entire Departments list in our main view model, instead of needing a separate copy of the entire Departments list on each and every data item in the DataGrid.


But anyways, your actual problem appears to be that you're setting the ComboBox.ItemsSource to Employee.Departments, which is defined as

public virtual Departments Departments { get; set; }

Since this is not a collection or a list of objects, the ItemsSource does not bind correctly, and therefore the SelectedValue binding does not work as expected.

Upvotes: 0

Jarek
Jarek

Reputation: 166

now I see the problem. When you use the ItemsSource, then each item of ComboBox gets the binding to Department entity, not to Employee. Does Department entity has a property DepId? Probably not, and that's is the problem. If you need to refer to Employee you need to do this.

{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGrid}}, Path=DepId}

Employee.Departments what is this? Are you sure it's initialized?

This is what works for me:

{
    public partial class MainWindow : Window
    {
        public ObservableCollection<Employee> Employees { get; set; }

        public MainWindow()
        {
            InitializeComponent();
            Department dept1 = new Department() { Id = 1, Name = "aaa" };
            Department dept2 = new Department() { Id = 2, Name = "bbb" };
            Department dept3 = new Department() { Id = 3, Name = "ccc" };

            ObservableCollection<Department> depts = new ObservableCollection<Department>();
            depts.Add(dept1);
            depts.Add(dept2);
            depts.Add(dept3);

            this.Employees = new ObservableCollection<Employee>();
            this.Employees.Add(new Employee() { Id = 1, Birthday = DateTime.Now, FirstName = "aaa", LastName = " aaaa", DepId = 1, Departments = depts });
            this.Employees.Add(new Employee() { Id = 2, Birthday = DateTime.Now, FirstName = "aaa", LastName = " bbbb", DepId = 2, Departments = depts });
            this.Employees.Add(new Employee() { Id = 3, Birthday = DateTime.Now, FirstName = "aaa", LastName = " cccc", DepId = 3, Departments = depts });
            this.Employees.Add(new Employee() { Id = 4, Birthday = DateTime.Now, FirstName = "aaa", LastName = " dddd", DepId = 2, Departments = depts });

            this.DataContext = this;
        }
    }
}

public class Employee
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime Birthday { get; set; }
    public int DepId { get; set; }

    public virtual ObservableCollection<Department> Departments { get; set; }
}

public class Department
{
    public int Id { get; set; }
    public string Name { get; set; }
}

and this is XAML:

<DataGrid  Name="DataGrid1" Grid.Row="3" Margin="10,0,10,10"
       RenderOptions.ClearTypeHint="Enabled"
       TextOptions.TextFormattingMode="Display"
       CanUserAddRows="False"
       CanUserDeleteRows="False"
       SelectionUnit="FullRow" 
       AutoGenerateColumns="false"
       SelectedItem="{Binding CurrentSelectedEmployee, Mode=TwoWay}"
       ItemsSource="{Binding Employees, Mode=TwoWay}">
    <DataGrid.Columns>
        <!--Column 1: Employee Id-->
        <DataGridTextColumn Header="Emplyee Id" Binding="{Binding Id}"/>

        <!--Column 2: First Name-->
        <DataGridTextColumn Header="First Name" Binding="{Binding FirstName}"/>

        <!--Column 3: Last Name-->
        <DataGridTextColumn Header="Last Name" Binding="{Binding LastName}"/>

        <!--Column 4: Birth Day-->
        <DataGridTemplateColumn Header="Birth Day" >
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <DatePicker SelectedDate="{Binding Birthday}"  BorderThickness="0" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>

        <!--Column 5: Department Id-->
        <DataGridTemplateColumn Header="Department" >
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <ComboBox ItemsSource="{Binding Departments}"
                          DisplayMemberPath="Name" SelectedValuePath="Id" SelectedValue="{Binding DepId}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>

    </DataGrid.Columns>
</DataGrid>

enter image description here

Upvotes: 1

Ugur
Ugur

Reputation: 1256

Please have look, it works like that:

  <!--Column 5: Department Id-->
                <DataGridTemplateColumn Header="Department" >
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ComboBox ItemsSource="{Binding Departments}"
                                  DisplayMemberPath="Name" SelectedIndex="0"  SelectedValuePath="Id" SelectedValue="{Binding DepId}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

enter image description here

and code behind:

 public partial class MainWindow : Window
    {

        private ObservableCollection<Employee> employees;
        public ObservableCollection<Employee> Employees
        {
            get { return employees; }
            set { employees = value; }
        }

        public MainWindow()
        {
            InitializeComponent();

            this.DataContext= this;

            Department dept1 = new Department() { Id = 1, Name = "aaa" };
            Department dept2 = new Department() { Id = 2, Name = "bbb" };
            Department dept3 = new Department() { Id = 3, Name = "ccc" };


            ObservableCollection<Department> depts = new ObservableCollection<Department>();
            depts.Add(dept1);
            depts.Add(dept2);
            depts.Add(dept3);



            Employees  =new ObservableCollection<Employee>();

            this.Employees.Add(new Employee() { Id = 1, Birthday = DateTime.Now, FirstName = "aaa", LastName = " aaaa", DepId = 1, Departments = depts });
            this.Employees.Add(new Employee() { Id = 2, Birthday = DateTime.Now, FirstName = "aaa", LastName = " bbbb", DepId = 2, Departments = depts });
            this.Employees.Add(new Employee() { Id = 3, Birthday = DateTime.Now, FirstName = "aaa", LastName = " cccc", DepId = 3, Departments = depts });
            this.Employees.Add(new Employee() { Id = 4, Birthday = DateTime.Now, FirstName = "aaa", LastName = " dddd", DepId = 2, Departments = depts });


        }

    }

Upvotes: 0

Related Questions