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