在CellEditEnding事件上修改DataGrid单元格的值

4
我一直在尝试在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,但这些方法似乎也没有什么帮助。
我找到的一个解决方案是以下代码片段:
var frameworkElement = this.MyDataGrid.Columns[1].GetCellContent(e.Row);
frameworkElement.SetValue(TextBlock.TextProperty, "a string that should be displayed immediatly");

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

所以问题是,在不使用提供的第二个代码片段的情况下,我该如何实现对新行的UI更新与现有行相同?感觉我错过了什么..

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

编辑2:XAML目前是普通的DataGrid,我尝试手动设置列及其绑定,但由于没有使它们工作,我已将其删除以使示例代码简单。

<DataGrid x:Name="MyDataGrid" />

编辑3: 添加此图像以更好地描述问题。因此,单击下一个单元格后,我希望文本被显示。 问题示例图像

编辑4: 使用Snoop查看单元格似乎设置了元素可见,同时调用绑定的.UpdateTarget();似乎有所帮助,但它不会自动工作。仍然想知道原因是什么...

编辑5: 我希望这可以与DataTable一起使用,因为我需要它的其他功能。


你可以使用粘贴机制:column.OnPastingCellClipboardContent( Items[ i ],"不可见字符串" ); - SnowballTwo
这似乎改变了第二个单元格的值,但现在原始单元格编辑已丢失,因此感觉有点不正规。谢谢评论,虽然可能实际上没有任何好的方法来解决这个问题 :/ - user5677774
@user5677774,你能发布一下你的XAML代码吗? - Ilan
@Ilan 根据您的要求添加了 XAML 代码。 - user5677774
现在我真正理解你想要什么了...创建一个DatagridTemplateColumn并在CellTemplate中处理这个特殊的行为,例如CellEditTemplate是一个Textbox,CellTemplate是一个TextBlock,它显示“不可见字符串”。如果您无法创建自己的列,请将原始值存储在附加属性中。 - SnowballTwo
@SnowballTwo 嗯,我不确定是否理解了,因此我更新了问题以更详细地描述。在查看模式下,单元格元素似乎默认为TextBlock,而在编辑模式下为TextBox。如果需要,我可以创建自己的列,但我不知道应该在其中执行什么特殊行为。我注意到的一件事是,如果我继续显示Snoop中的DataGridCell元素,则文本将出现在UI中,因此代码似乎是正确的,但其他方面可能存在问题。 - user5677774
2个回答

4

经过测试各种不同类型的解决方案后,我最终决定在开始编辑单元格时添加一行。由于我正在使用的功能已经在现有行中工作,这确实解决了问题。

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

1

我建议您使用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;
    }
}

请注意,您的模型属性对象可能很复杂(例如图片或描述另一个视图模型的类),但在这种情况下,您必须对数据网格列和/或单元格进行模板化(示例链接:WPF DataGrid Control)。
如果您遇到代码问题,我非常乐意提供帮助。
更新: 1. 代码后台 - 您应该更新DataTable,但请注意更新必须在已存在于表中的行上执行。
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的动态数据。您有任何想法如何使用DataTable实现相同的功能吗?很抱歉没有在初始时提到这一点。 - user5677774
最初的问题在于我无法通过新行获得与旧行相同的功能。我感觉可以有一种方法来改变这个值,即使此时该行不在表格中。 - user5677774
@user5677774 请尝试使用另一个事件。 - Ilan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接