WPF ComboBox selectedValue binding fails

久未见 提交于 2019-12-25 05:46:47

问题


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

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?

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

}

回答1:


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>




回答2:


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>

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


        }

    }



回答3:


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.



来源:https://stackoverflow.com/questions/31675743/wpf-combobox-selectedvalue-binding-fails

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!