Search code examples
wpfbindingobservablecollectionselecteditem

Bound DataGrid ComboBox column won't update when setting the Selected Item property


I have a template column with a bound combobox. Everything works fine except when I set the SelectedItem programmatically it doesn't update UI or the parent row itemsource, the customer object does reflect the change. I know it is a XAML/binding problem. When I look at SelectedItem.FullName in the ComboBox binding it contains the Customer name instead of the tech name. But the binding for the combobox items works fine. I just can't see the problem that I know is there.

XAML for grid and colum...

<DataGrid x:Name="CustomerGrid" Grid.Row="2" Grid.ColumnSpan="3" Margin="5,10,5,5" ItemsSource="{Binding Customers}" SelectedItem="{Binding SelectedCustomer}">

            
            <DataGrid.Columns>
                <DataGridTemplateColumn Header="Technician" MinWidth="180" IsReadOnly="True">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ComboBox x:Name="cb1" VerticalContentAlignment="Center" ToolTip="{Binding Path=SelectedItem.FullName,ElementName=cb1}"
                            ItemsSource="{Binding Technicians, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
                            SelectedItem="{Binding SelectedTechnician, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
                            SelectedValuePath="TechnicianID" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
                                <ComboBox.Resources>
                                    <Style TargetType="Popup">
                                        <Setter Property="Width" Value="{Binding Path=ActualWidth+10, ElementName=cb1}"/>
                                    </Style>
                                </ComboBox.Resources>
                                <ComboBox.ItemTemplate>
                                    <DataTemplate>
                                        <TextBlock Text="{Binding FullName}" ToolTipService.ToolTip="{Binding FullName}" TextWrapping="NoWrap"/>
                                    </DataTemplate>
                                </ComboBox.ItemTemplate>
                            </ComboBox>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
//partial customer class
internal class Customer : ViewModelBase
    {
        public delegate void SendReconnectHandler(ref Customer customer);
        public event SendReconnectHandler SendReconnect;

        public Customer(List<Technician> technicians)
        {
            Technicians = new ObservableCollection<Technician>(technicians);
        }

        #region Properties
        private ObservableCollection<Technician> _Technicians = new ObservableCollection<Technician>();
        public ObservableCollection<Technician> Technicians
        {
            get
            { return _Technicians; }
            set
            {
                if (value != _Technicians)
                {
                    _Technicians = value;
                    NotifyPropertyChanged();
                }
            }
        }

        private Technician _selectedTechnician = new Technician();
        public Technician SelectedTechnician
        {
            get
            { return _selectedTechnician ?? new Technician(); }
            set
            {
                if (value != _selectedTechnician)
                { 
                    _selectedTechnician = value; 
                    NotifyPropertyChanged();
                }

                if (!string.IsNullOrEmpty(_selectedTechnician.TechnicianID))
                { TechSelected = true; }
                else
                { TechSelected = false; }
            }
        }

        public bool TechSelected
        {
            get
            { return _TechSelected; }
            set
            {
                if (value != _TechSelected)
                {
                    _TechSelected = value;
                    NotifyPropertyChanged();
                }
            }
        }
//method for updating the technician, along with crazy things I have tried
private void AssignTechs()
        {
            var routes = Customers.ToList().Select(c => c.Route).Distinct().ToList();
            var vm = new AssignmentViewModel(routes);
            var vw = new AssignmentWindow(vm);
            vw.ShowDialog();

            foreach (var va in vm.RouteAssignments)
            {
                foreach (var cust in Customers.Where(c => c.Route == va.RouteNumber))
                {
                    cust.SelectedTechnician = va.SelectedTechnician;
                }
            }

            //foreach (var va in vm.RouteAssignments)
            //{
            //    foreach (var cust in Customers.Where(c => c.Route == va.RouteNumber))
            //    {
            //        cust.SelectedTechnician = va.SelectedTechnician;
            //        CurrentMainWindow.CustomerGrid.Columns[12].SetCurrentValue(ComboBox.SelectedItemProperty, va.SelectedTechnician);
            //        //CurrentMainWindow.CustomerGrid.SelectedItem = cust;
            //        //((Customer)CurrentMainWindow.CustomerGrid.SelectedItem).SelectedTechnician = va.SelectedTechnician;
            //    }
            //}
            //CurrentMainWindow.CustomerGrid.SelectedItem = Customers.FirstOrDefault();
            //CurrentMainWindow.CustomerGrid.ScrollIntoView(CurrentMainWindow.CustomerGrid.SelectedItem);
        }
internal class Technician : ViewModelBase//, IEditableObject
    {
        #region Properties
        private String _TechnicianID = string.Empty;
        public String TechnicianID
        {
            get
            { return _TechnicianID ?? string.Empty; }
            set
            {
                if (value != _TechnicianID)
                {
                    _TechnicianID = value;
                    NotifyPropertyChanged();
                }
            }
        }

        private String _FirstName = string.Empty;
        public String FirstName
        {
            get
            { return _FirstName ?? string.Empty; }
            set
            {
                if (value != _FirstName)
                {
                    _FirstName = value;
                    NotifyPropertyChanged();
                }
            }
        }

        private String _LastName = string.Empty;
        public String LastName
        {
            get
            { return _LastName ?? string.Empty; }
            set
            {
                if (value != _LastName)
                {
                    _LastName = value;
                    NotifyPropertyChanged();
                }
            }
        }

        private String _FullName = string.Empty;
        public String FullName
        {
            get
            { return BuildFullName() ?? string.Empty; }
            set
            {
                if (value != _FullName)
                {
                    _FullName = value;
                    NotifyPropertyChanged();
                }
            }
        }

        private String _EmailAddress = string.Empty;
        public String EmailAddress
        {
            get
            { return _EmailAddress ?? string.Empty; }
            set
            {
                if (value != _EmailAddress)
                {
                    _EmailAddress = value;
                    NotifyPropertyChanged();
                }
            }
        }

        private ObservableCollection<Models.ComboBoxItem> _FridayDates = new ObservableCollection<Models.ComboBoxItem>();
        public ObservableCollection<Models.ComboBoxItem> FridayDates
        {
            get
            { return _FridayDates; }
            set
            {
                if (value != _FridayDates)
                {
                    _FridayDates = value;
                    NotifyPropertyChanged();
                }
            }
        }

        private Models.ComboBoxItem _SelectedFridayDate = null;
        public Models.ComboBoxItem SelectedFridayDate
        {
            get
            { return _SelectedFridayDate ?? new Models.ComboBoxItem(); }
            set
            {
                if (value != _SelectedFridayDate)
                {
                    _SelectedFridayDate = value;
                    NotifyPropertyChanged();
                }
            }
        }

        private string _AssignedFriday = string.Empty;
        public String AssignedFriday
        {
            get
            {
                if ((SelectedFridayDate != null) && (!string.IsNullOrEmpty(SelectedFridayDate.Value)))
                {
                    if (DateTime.TryParse(SelectedFridayDate.Value, out var date))
                    {
                        return date.ToString("yyyy-MM-dd");
                    }

                }
                return SelectedFridayDate?.Value ?? string.Empty;
            }
            set
            {
                if (value != _AssignedFriday)
                {
                    {
                        _AssignedFriday = value;
                    }
                }
            }
        }
        #endregion

        public Technician()
        {
            LoadFridayDates();
        }

        private string BuildFullName()
        {
            string fullName = string.Empty;

            if (string.IsNullOrEmpty(_FullName))
            {
                fullName = FirstName + " " + LastName;
            }
            else if (string.IsNullOrEmpty(FirstName) || string.IsNullOrEmpty(LastName))
            {
                var names = _FullName.Split(' ');
                if (names.Count() == 2)
                {
                    FirstName = names[0];
                    LastName = names[1];
                }
                else
                {
                    LastName = _FullName;
                }
                fullName = _FullName;
            }
            else
            { fullName = _FullName; }

            if (string.IsNullOrEmpty(fullName.Trim()) && !string.IsNullOrEmpty(EmailAddress))
            {
                var s1 = EmailAddress.Split('@');
                if (s1.Count() == 2) { fullName = s1[0]; }
            }

            return fullName.Trim();
        }

        private void LoadFridayDates()
        {
            DateTime dt = DateTime.Now.AddDays(-1);
            var weekday = DayOfWeek.Friday;

            List<ComboBoxItem> list = new List<ComboBoxItem>() { new ComboBoxItem() };

            while (dt.DayOfWeek != weekday)
                dt = dt.AddDays(1);

            list.Add(new ComboBoxItem()
            {
                Value = dt.ToLongDateString(),
                Key = "0"
            });

            for (int i = 0; i < 20; i++)
            {
                dt = dt.AddDays(7);
                list.Add(new ComboBoxItem()
                {
                    Value = dt.ToLongDateString(),
                    Key = (i + 1).ToString()
                }); ;
            }

            FridayDates = new ObservableCollection<ComboBoxItem>(list);
        }

    }

I have tried everything I can think of. The SelectedItem property set, and NotifyPropertyChanged, do fire when I set the value.


Solution

  • SelectedItem is supposed to exist in ItemsSource. so you need to set backing property accordingly:

    foreach (var va in vm.RouteAssignments)
    {
        foreach (var cust in Customers.Where(c => c.Route == va.RouteNumber))
        {
            cust.SelectedTechnician = cust.Technicians.FirstOrDefault(t => t.TecnicianID == va.SelectedTechnician.TechnicianID);
        }
    }