views:

60

answers:

1

I am trying to declaratively bind a ComboBox within a DataGrid CellEditingTemplate using a ViewModel. The ComboBox is not being bound. What am I doing wrong?

XAML:

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing"
    xmlns:data="clr-namespace:SilverlightApplication1"
    mc:Ignorable="d"
    x:Class="SilverlightApplication1.EmployeeDetail"
    Width="640" Height="480">

    <UserControl.Resources>
        <data:EmployeeDetailsViewModel
            x:Key="ViewModel"
            d:IsDataSource="True" />
    </UserControl.Resources>

    <Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource ViewModel}}" Background="White">

        <sdk:DataGrid ItemsSource="{Binding Employees,Mode=TwoWay}" AutoGenerateColumns="False" CanUserSortColumns="True" CanUserReorderColumns="True" CanUserResizeColumns="True" GridLinesVisibility="All" Height="317" HorizontalAlignment="Left" Margin="12,136,0,0" Name="EmployeesGrid" VerticalAlignment="Top" Width="605">
            <sdk:DataGrid.Columns>


<!-- snipped from brevity -->

                <sdk:DataGridTemplateColumn Header="Status">
                    <sdk:DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding EmployeeStatus.Description}" TextWrapping="Wrap"></TextBlock>
                        </DataTemplate>
                    </sdk:DataGridTemplateColumn.CellTemplate>
                    <sdk:DataGridTemplateColumn.CellEditingTemplate>
                        <DataTemplate>
                            <ComboBox ItemsSource="{Binding Path=EmployeeStatuses}" SelectedItem="{Binding EmployeeStatus, Mode=TwoWay}" />
                        </DataTemplate>
                    </sdk:DataGridTemplateColumn.CellEditingTemplate>
                </sdk:DataGridTemplateColumn>

            </sdk:DataGrid.Columns>
        </sdk:DataGrid>
        <TextBlock x:Name="SearchLabel" HorizontalAlignment="Left" Margin="12,95,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="106" Height="34"><Run FontWeight="Bold" Text="Search By Name: "/><Run FontSize="9.333" Text="(Last, First)"/></TextBlock>
        <TextBox x:Name="SearchParam" HorizontalAlignment="Left" Margin="144,101,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="162"/>
        <Button x:Name="SearchButton" Content="Search" HorizontalAlignment="Right" Margin="0,102,242,0" VerticalAlignment="Top" Width="75" Click="SearchButton_Click"/>    


    </Grid>
</UserControl>

VIEW MODEL:

using System.Collections.ObjectModel;
using SilverlightApplication1.EmployeeService;
using SilverlightApplication1.ViewModels;

namespace SilverlightApplication1
{
    public class EmployeeDetailsViewModel : ViewModelBase
    {
        readonly IEmployeeServiceAgent _serviceAgent;
        ObservableCollection<EmployeeStatus> _employeeStatuses { get; set; }
        ObservableCollection<Employee> _employees { get; set; }

        public EmployeeDetailsViewModel() : this(new EmployeeServiceAgent()) { }
        public EmployeeDetailsViewModel(IEmployeeServiceAgent serviceAgent)
        {
            if (!IsDesignTime)
            {
                _serviceAgent = serviceAgent;
                GetAllEmployees();
                GetEmployeeStatuses();
            }

        }

        public ObservableCollection<Employee> Employees
        {
            get { return _employees; }
            set
            {
                if(_employees!=value)
                {
                    _employees = value;
                    OnNotifyPropertyChanged("Employees");
                }
            }
        }

        public ObservableCollection<EmployeeStatus> EmployeeStatuses
        {
            get { return _employeeStatuses; }
            set
            {
                if (_employeeStatuses != value)
                {
                    _employeeStatuses = value;
                    OnNotifyPropertyChanged("EmployeeStatuses");
                }
            }
        }

        private void GetAllEmployees()
        {
            _serviceAgent.GetAll((s, e) => Employees = e.Result);
        }

        private void GetEmployeeStatuses()
        {
            _serviceAgent.GetEmployeeStatuses((s, e) => EmployeeStatuses = e.Result);
        }

    }
}

Update:

This seems wrong but I figured out how to get the binding working by re-referencing the ViewModel in the ItemSource Binding:

<ComboBox ItemsSource="{Binding Source={StaticResource ViewModel},Path=EmployeeStatuses}"
                                      DisplayMemberPath="Description"
                                      SelectedItem="{Binding EmployeeStatus, Mode=TwoWay}"  />

However, a am now experiencing a problem where the SelectedItem is not bound! What am I doing wrong?

+2  A: 

The problem is a common one that people run into. When you're in the data template of the column, you're no longer bound the the view model. At that point your data context is the EmployeeStatus object (which doesn't have an EmployeeStatuses property to bind to).

So to get the combobox binding to work you can use the ElementName=LayoutRoot to bind back up the tree to the root ViewModel.

Update: Here would be the full syntax for your binding:

{Binding DataContext.EmployeeStatuses, ElementName=LayoutRoot}

Update2: I've actually run into this as well and there is a workaround you have to implement to get the element name binding to work inside a datagrid.

Bryant
Did you mean add ElementName=LayoutRoot to the ItemsSource of the ComboBox? I did this but the ComboBox still does not get bound. <ComboBox ItemsSource="{Binding ElementName=LayoutRoot, Path=EmployeeStatuses}" ...
Rokal
Bryant, I tried <ComboBox ItemsSource="{Binding DataContext.EmployeeStatuses, ElementName=LayoutRoot}"... but the items still do not get bound.
Rokal
Try using the workaround at the link in the last update.
Bryant
This didn't seem to help either
Rokal