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

WPF中ComboBox新增行时无法更新DataGrid Itemsource求助

WPF DataGrid新增行ComboBox绑定不更新数据源问题

本人刚接触WPF,需求是修改ComboBox选项后按下Enter键更新DataGrid的Itemsource。当行内包含TextBox等其他字段时,保存更新正常;但在仅包含ComboBox的DataGrid中,现有行可正常修改,新增行却无法更新,疑似ComboBox未在新增行时更新数据源。尝试在SelectionChanged事件及DataGrid新增行事件中强制更新,均未成功。


相关代码

XAML代码

<Window x:Class="ClinPath.Test"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ClinPath"
        mc:Ignorable="d"
        Title="Test" Height="450" Width="800"
        xmlns:clinpath="clr-namespace:ClinPath">
    
    <Window.Resources>
        <clinpath:BindingProxy x:Key="CollectionList" Data="{Binding Path=collectionList}"/>
    </Window.Resources>

    <DataGrid 
                x:Name="dgCollection"
                ItemsSource="{Binding CollectionDetails}"
                AutoGenerateColumns="False"
                Grid.Row="5" Grid.Column="5"  Grid.ColumnSpan="4" 
                PreviewKeyDown="dgCollection_PreviewKeyDown" SelectionChanged="dgCollection_SelectionChanged"
                CanUserAddRows="True"
                >
        <DataGrid.Columns >

            <DataGridTextColumn 
                        Header="ID" 
                        Binding="{Binding ID ,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"     
                        Width="*" Visibility="Visible"

                        />

            <DataGridTemplateColumn Header="Collection" Width="*" >
                <DataGridTemplateColumn.CellTemplate >
                    <DataTemplate>

                        <ComboBox 
                                    ItemsSource="{Binding Source={StaticResource ResourceKey=CollectionList}, Path=Data}" 
                                    SelectedValue="{Binding CollectionID,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, BindsDirectlyToSource=True}"    

                                    SelectedValuePath="ID"  DisplayMemberPath="Details"                                
                                    />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>

        </DataGrid.Columns>
    </DataGrid>
</Window>

后台代码

using ClinPath.StudyManagement.DataConnector;
using ClinPath.StudyManagement.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;


namespace ClinPath
{
    /// <summary>
    /// Interaction logic for Test.xaml
    /// </summary>
    public partial class Test : Window, INotifyPropertyChanged
    {
        public Test()
        {
            InitializeComponent();

            DataContext = this;


            //preload Lists and Combobox's
            SM_StudyDetail_DA da = new SM_StudyDetail_DA();
            ComboLists = da.GetStudyDetailLists();

            collectionList = ComboLists.CollectionList;

        }

        public int selectedProduct = 1;

        public SM_ComboLists_M ComboLists { get; }
        public List<comboObj> collectionList { get; set; }

        public ObservableCollection<SM_CollectionDetail_M> collectionDetails;
        public ObservableCollection<SM_CollectionDetail_M> CollectionDetails
        {
            get
            {
                return collectionDetails;
            }
            set
            {
                collectionDetails = value;
                OnPropertyChanged();

            }
        }

        #region Collection Data Grid

        private void CollectionEventUpdate()
        {
            SM_CollectionDetail_DA da = new SM_CollectionDetail_DA();
            CollectionDetails = da.GetCollectionList(selectedProduct);
        }
        private void CollectionUpdate()
        {
            SM_CollectionDetail_DA da = new SM_CollectionDetail_DA();
            da.Add_Update_CollectionList(CollectionDetails, selectedProduct);
        }

        private void dgCollection_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            DataGrid _dg = (DataGrid)sender;

            if (Key.Delete == e.Key)
            {
                int collectionID = ((SM_CollectionDetail_M)((DataGrid)sender).CurrentItem).ID;


                string message = String.Format("You are about to delete the following Collection!  \r\nCollection:{0}\r\nWould you like to continue?", collectionID);
                string caption = "Confirmation";
                MessageBoxButton buttons = MessageBoxButton.YesNo;
                MessageBoxImage icon = MessageBoxImage.Question;

                if (MessageBox.Show(message, caption, buttons, icon) == MessageBoxResult.Yes)
                {
                    SM_CollectionDetail_DA da = new SM_CollectionDetail_DA();
                    da.DeleteCollection(collectionID);
                    CollectionDetails.Remove(CollectionDetails.Where(x => x.ID == collectionID).FirstOrDefault());


                    e.Handled = true;
                }
                else
                {
                    // Cancel code here  
                }
                e.Handled = true;
            }

            if (Key.Enter == e.Key)
            {
                CollectionUpdate();
                e.Handled = true;
            }
        }
        #endregion

        #region Property CHanged Events

        public event PropertyChangedEventHandler PropertyChanged;

        internal void OnPropertyChanged([CallerMemberName] string nameProp = "") =>
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameProp));
        #endregion
    }
}

数据模型代码

namespace ClinPath.StudyManagement.Models
{
    public class SM_CollectionDetail_M
    {
        public int ID { get; set; } 
        public int SampleProductID { get; set; }
        public int? CollectionID { get;set; }

    }

    public class SM_ComboLists_M
    {
        public List<comboObj> CollectionList { get; set; }

    }

    public class comboObj
    {
        int _ID;
        string _details;

        public int ID
        {
            get { return _ID; }
            set { _ID = value; }
        }

        public string Details
        {
            get { return _details; }   // get method
            set { _details = value; }  // set method
        }

    }
}

绑定代理类代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace ClinPath
{
    public class BindingProxy : Freezable
    {
        #region Overrides of Freezable

        protected override Freezable CreateInstanceCore()
        {
            return new BindingProxy();
        }

        #endregion

        public object Data
        {
            get { return (object)GetValue(DataProperty); }
            set { SetValue(DataProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Data.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DataProperty =
            DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
    }
}

问题分析与修复方案

问题根源

  1. 新增行是DataGrid自动生成的临时对象,Enter触发时ComboBox的选中值可能未完成绑定提交
  2. SM_CollectionDetail_M未实现INotifyPropertyChanged,数据源变更无法通知UI,也无法确保绑定值同步
  3. 按下Enter时未强制结束单元格编辑,导致ComboBox的值未提交到数据源

修复步骤

1. 给数据模型添加INotifyPropertyChanged实现

修改SM_CollectionDetail_M,确保属性变更能通知绑定系统:

namespace ClinPath.StudyManagement.Models
{
    public class SM_CollectionDetail_M : INotifyPropertyChanged
    {
        private int _id;
        private int _sampleProductID;
        private int? _collectionID;

        public int ID
        {
            get => _id;
            set
            {
                _id = value;
                OnPropertyChanged();
            }
        }

        public int SampleProductID
        {
            get => _sampleProductID;
            set
            {
                _sampleProductID = value;
                OnPropertyChanged();
            }
        }

        public int? CollectionID
        {
            get => _collectionID;
            set
            {
                _collectionID = value;
                OnPropertyChanged();
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

2. 修改Enter键事件,强制提交编辑状态

在dgCollection_PreviewKeyDown的Enter分支中添加代码,确保单元格和行的编辑状态提交:

if (Key.Enter == e.Key)
{
    // 强制结束当前单元格编辑,确保ComboBox值提交到数据源
    if (_dg.IsEditing())
    {
        _dg.CommitEdit(DataGridEditingUnit.Cell, true);
    }
    // 若处于新增行状态,提交行编辑
    if (_dg.IsAddingNew)
    {
        _dg.CommitEdit(DataGridEditingUnit.Row, true);
    }
    CollectionUpdate();
    e.Handled = true;
}

3. 优化ComboBox绑定配置

移除BindsDirectlyToSource=True,该属性会绕过DataGrid的绑定容器,可能导致新增行的绑定异常:

<ComboBox 
    ItemsSource="{Binding Source={StaticResource ResourceKey=CollectionList}, Path=Data}" 
    SelectedValue="{Binding CollectionID, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"    
    SelectedValuePath="ID"  DisplayMemberPath="Details"                                
/>

4. 确保collectionList触发属性变更

将collectionList改为带属性变更通知的属性:

private List<comboObj> _collectionList;
public List<comboObj> collectionList
{
    get => _collectionList;
    set
    {
        _collectionList = value;
        OnPropertyChanged();
    }
}

内容的提问来源于stack exchange,提问作者Christopher Parlato

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 20:40:26