【问题标题】:Modifying DataGrid cell values on CellEditEnding event在 CellEditEnding 事件上修改 DataGrid 单元格值
【发布时间】:2016-03-19 23:33:12
【问题描述】:

我一直试图在 WPF DataGrid 控件的 CellEditEnding 事件处理程序中更改同一行中的不同单元格值。

如果我编辑现有行中的单元格值,它会按预期工作。但是,在添加新行时,如果在未进入编辑模式的情况下通过鼠标单击另一个单元格值,则更改不会反映到 UI。

通过按 Tab 将焦点移动到下一个单元格也可以按预期工作,但它不应该也适用于单击吗?

public MainWindow()
{
    InitializeComponent();

    DataTable dataTable = new DataTable();
    dataTable.Columns.Add(new DataColumn());
    dataTable.Columns.Add(new DataColumn());
    DataRow existingRow = dataTable.NewRow();
    dataTable.Rows.Add(existingRow);

    this.MyDataGrid.ItemsSource = dataTable.DefaultView;
    this.MyDataGrid.CellEditEnding += MyDataGrid_CellEditEnding;
}

void MyDataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
    (e.Row.Item as DataRowView).Row[1] = "a string that should be displayed immediatly";
}

到目前为止,我已尝试更改绑定 UpdateSourceTrigger、NotifyOnSourceUpdated 和 NotifyOnTargetUpdated,但似乎没有任何效果。我还尝试通过阅读相关主题来进行修复:http://codefluff.blogspot.fi/2010/05/commiting-bound-cell-changes.html

但这些似乎也没有帮助..

我发现一种可行的方法是遵循 sn-p:

var frameworkElement = this.MyDataGrid.Columns[1].GetCellContent(e.Row);
frameworkElement.SetValue(TextBlock.TextProperty, "a string that should be displayed immediatly");

但我不想依赖dependencyproperties,因为我可能不知道底层的显示类型。

所以问题是,在不使用提供的第二个代码 sn-p 的情况下,我可以做些什么来在新行中实现 UI 更新,因为它可以与现有行一起使用?我觉得我错过了什么..

编辑:使用 ClipboardPaste 也可以设置新值,但由于某种原因,原始编辑值会丢失。

Edit2:xaml 目前是普通的 DataGrid,我尝试手动执行列及其绑定,但由于我没有让它们工作,我已经删除它们以保持示例代码简单

<DataGrid x:Name="MyDataGrid" />

Edit3 :添加此图像以更好地描述问题。因此,单击下一个单元格后,我希望显示文本 Example image of the problem

Edit4 :使用 snoop 查看单元格似乎将元素设置为可见,也调用 .UpdateTarget();因为绑定似乎有帮助,但它不能自动工作。还在想是什么原因..

Edit5:我希望它与 DataTable 一起使用,因为我需要它的其他功能

【问题讨论】:

  • 你可以使用粘贴机制:column.OnPastingCellClipboardContent( Items[ i ],"invisible string" );
  • 这似乎改变了第二个单元格的值,但现在原始单元格编辑丢失了,因此感觉有点hacky。感谢您的评论,实际上可能没有任何好的方法可以做到这一点:/
  • @user5677774 你能发布你的 XAML 代码吗?
  • @Ilan 按要求添加了 xaml 代码
  • 现在我真的明白你想要什么了......创建一个 DatagridTemplateColumn 并在你的 CellTemplate 中处理这种特殊行为,例如CellEditTemplate 是一个文本框,CellTemplate 是一个显示“不可见字符串”的文本块。如果您无法创建自己的列,只需将原始值存储在附加属性中

标签: c# wpf datagrid datatable


【解决方案1】:

在测试了大量不同类型的解决方案后,我最终在开始编辑单元格时添加了行。由于我正在寻找的功能是在现有行中工作,它确实解决了问题。

public class MyDataGrid : DataGrid
{
    protected override void OnBeginningEdit(DataGridBeginningEditEventArgs e)
    {
        base.OnBeginningEdit(e);
        this.CommitEdit();
    }

    protected override void OnCellEditEnding(DataGridCellEditEndingEventArgs e)
    {
        base.OnCellEditEnding(e);
        (e.Row.Item as DataRowView).Row[1] = "a string that should be displayed immediatly";
    }
}

【讨论】:

    【解决方案2】:

    我建议您使用 MVVM 方式,然后您将通过 WPF 本身的内置通知机制满足您的要求,在更新数据上下文中的某些属性时,您可以触发另一个属性的更新。例子: 1.Xaml代码:

    <Window x:Class="DataGridSoHelpAttempt.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:dataGridSoHelpAttempt="clr-namespace:DataGridSoHelpAttempt"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <dataGridSoHelpAttempt:MainViewModel/>
    </Window.DataContext>
    <Grid>
        <DataGrid x:Name="MyDataGrid" ItemsSource="{Binding DataSource}"/>
    </Grid></Window>
    

    2。查看型号代码:

    public class MainViewModel:BaseObservableObject
    {
        public MainViewModel()
        {
            DataSource = new ObservableCollection<BaseData>(new List<BaseData>
            {
                new BaseData {Name = "John"},
                new BaseData {Name = "Ron"},
                new BaseData {Name = "Bob"},
            });
        }
        public ObservableCollection<BaseData> DataSource { get; set; }
    }
    

    3。型号代码:

    public class BaseData:BaseObservableObject
    {
        private string _name;
        private string _description;
    
        public virtual string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                OnPropertyChanged();
                Description = "a string that should be displayed immediatly";
            }
        }
    
        public virtual object Description
        {
            get { return _description; }
            set
            {
                _description = (string) value;
                OnPropertyChanged();
            }
        }
    }
    

    4。 BaseObservableObject 一个基本的 INotifyPropertyChanged 实现:

    public class BaseObservableObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    
        protected virtual void OnPropertyChanged<T>(Expression<Func<T>> raiser)
        {
            var propName = ((MemberExpression)raiser.Body).Member.Name;
            OnPropertyChanged(propName);
        }
    
        protected bool Set<T>(ref T field, T value, [CallerMemberName] string name = null)
        {
            if (!EqualityComparer<T>.Default.Equals(field, value))
            {
                field = value;
                OnPropertyChanged(name);
                return true;
            }
            return false;
        }
    }
    

    5。请记住,您的模型的属性对象可能很复杂(例如描述另一个视图模型的图像或类),但在这种情况下,您必须模板化您的数据网格列或/和单元格(示例链接:WPF DataGrid Control)。

    如果您遇到代码问题,我非常乐意提供帮助。 问候

    更新 1. 代码隐藏 - 您应该更新 DataTable,但请注意必须对 Table 中已存在的行执行更新:

    public partial class MainWindow : Window
    {
        private readonly DataTable _dataTable;
    
        public MainWindow()
        {
            InitializeComponent();
    
            this.MyDataGrid.CellEditEnding += MyDataGrid_CellEditEnding;
            _dataTable = new DataTable();
            _dataTable.Columns.Add(new DataColumn());
            _dataTable.Columns.Add(new DataColumn());
            DataRow existingRow = _dataTable.NewRow();
            _dataTable.Rows.Add(existingRow);
    
            this.MyDataGrid.ItemsSource = _dataTable.DefaultView;
    
        }
    
    
        void MyDataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
        {
            var dataRowView = (e.Row.Item as DataRowView);
            var columnIndex = e.Column.DisplayIndex;
            var rowIndex = e.Row.GetIndex();
            var dv = _dataTable.DefaultView;
            var nextColumnIndex = columnIndex + 1;
            if (dv.Table.Columns.Count <= nextColumnIndex || dv.Table.Rows.Count <= rowIndex) return;
            dv.Table.Rows[rowIndex][nextColumnIndex] = "a string that should be displayed immediatly";
        }
    }
    

    2。 Xaml:

    <Window x:Class="GridViewWitaFDataTableSoHelpAttempt.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DataGrid x:Name="MyDataGrid" />
    </Grid></Window>
    

    【讨论】:

    • 非常感谢您的详细回答。但是,在将这些更改集成到目标项目中时,我仍然面临问题。我正在使用 DataTable.DefaultView 来显示数据(对于它的许多功能),因此我不能(?)在不重新实现整个 DataTable 的情况下实现 INotifyPropertyChanged。我认为在 DataTable 中,propertychanged 是以某种方式按行(?)完成的,这是我想采用的方式,因为我正在使用来自数据表的动态数据。您知道如何使用 DataTable 实现相同的功能吗?很抱歉在 init 中没有提及
    • 原来的问题是我不能用新行获得与旧行相同的功能。我觉得必须有一种方法可以更改此值,即使该行当时不在表中。
    • @user5677774 尝试使用另一个事件。
    猜你喜欢
    • 1970-01-01
    • 2015-09-10
    • 1970-01-01
    • 2016-03-29
    • 2013-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-08
    相关资源
    最近更新 更多