You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

绑定的DataGrid ComboBox列设置SelectedItem后无法更新UI问题

问题描述

在DataGrid的模板列中使用绑定的ComboBox,其他功能正常,但通过代码设置SelectedItem时,UI和父行数据源均未更新,不过Customer对象确实已反映出该变更。另外发现ComboBox绑定的SelectedItem.FullName显示的是客户名称而非技术员名称,但ComboBox的项绑定工作正常。

XAML代码

<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>

Customer类代码

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

Technician类代码

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

}
解决方案

问题1:代码设置SelectedTechnician后UI不更新

核心问题是对象引用不匹配:从AssignmentViewModel获取的va.SelectedTechnician实例,和当前Customer.Technicians集合中的实例不是同一个对象。WPF的SelectedItem匹配基于对象引用,即使两个Technician的TechnicianID相同,只要不是同一实例,ComboBox就无法匹配到对应项。

修正代码:

foreach (var va in vm.RouteAssignments)
{
    foreach (var cust in Customers.Where(c => c.Route == va.RouteNumber))
    {
        // 从当前客户的Technicians集合中查找匹配ID的实例
        var matchedTech = cust.Technicians.FirstOrDefault(t => t.TechnicianID == va.SelectedTechnician.TechnicianID);
        if (matchedTech != null)
        {
            cust.SelectedTechnician = matchedTech;
        }
    }
}

问题2:ComboBox的SelectedItem.FullName显示客户名称

这是因为SelectedItem为null时,绑定会回退到父级DataContext(即Customer对象),如果Customer也有FullName属性,就会显示客户的名称。

修正ToolTip绑定:

ToolTip="{Binding Path=SelectedItem.FullName, ElementName=cb1, FallbackValue='未选择技术员'}"

同时修改Customer.SelectedTechnician的getter,避免返回不在集合中的新实例:

public Technician SelectedTechnician
{
    get
    { 
        // 直接返回null,让ComboBox显示空状态
        return _selectedTechnician; 
    }
    set
    {
        if (value != _selectedTechnician)
        { 
            _selectedTechnician = value; 
            NotifyPropertyChanged();
        }

        TechSelected = !string.IsNullOrEmpty(_selectedTechnician?.TechnicianID);
    }
}

内容的提问来源于stack exchange,提问作者Steve S

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 09:27:01