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)); } }
问题分析与修复方案
问题根源
- 新增行是DataGrid自动生成的临时对象,Enter触发时ComboBox的选中值可能未完成绑定提交
SM_CollectionDetail_M未实现INotifyPropertyChanged,数据源变更无法通知UI,也无法确保绑定值同步- 按下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
相关产品推荐
相关产品推荐

