【问题标题】:How to trigger an update of a specific cell in a DataGrid with WPF binding如何使用 WPF 绑定触发 DataGrid 中特定单元格的更新
【发布时间】:2017-11-29 10:04:48
【问题描述】:

我有一个自定义类,通过readonly 属性提供 3d 点信息:

public class Point3d
{
    private readonly double x;
    public double X { get { return x; } }

    private readonly double y;
    public double Y { get { return y; } }

    private readonly double z;
    public double Z { get { return z; } }

    public PoseCartesian(double x, double y, double z)
    {
        this.x = x;
        this.y = y;
        this.z = z;
    }
}

为了显示多个 3d 点,我在 WPF 中使用了带有四列的DataGrid。第一列应该显示条目的行号。

<DataGrid Name="dgrPoints" AutoGenerateColumns="False" 
    ItemsSource="{Binding UpdateSourceTrigger=Default, Mode=OneWay}"
    SelectionChanged="dgr_Poses_SelectionChanged" CanUserSortColumns="False"
    IsReadOnly="True">
    <DataGrid.Columns>
          <DataGridTextColumn x:Name="colI" Binding="{Binding Mode=OneWay,
               RelativeSource={RelativeSource AncestorType=DataGridRow},
               Converter={local:RowToIndexConverter}}"/>
          <DataGridTextColumn x:Name="colX" Binding="{Binding X}" Header="X"/>
          <DataGridTextColumn x:Name="colY" Binding="{Binding Y}" Header="Y"/>
          <DataGridTextColumn x:Name="colZ" Binding="{Binding Z}" Header="Z"/>
     </DataGrid.Columns>
</DataGrid>

要将点集合绑定到DataGrid,我使用ObservableCollection

private ObservableCollection<Point3d> pointList;

通过设置DataContext结束绑定:

this.pointList = new ObservableCollection<Point3d>();
dgrPoints.ItemsSource= this.poseList;

要检索行号,我使用Andy 在此answer 中建议的以下转换器:

public class RowToIndexConverter : MarkupExtension, IValueConverter
{
    static RowToIndexConverter converter;

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        DataGridRow row = value as DataGridRow;
        if (row != null)
            return row.GetIndex() + 1;
        else
            return -1;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        if (converter == null) converter = new RowToIndexConverter();
        return converter;
    }

    public RowToIndexConverter()
    {
    }
}

这一切正常,并在添加新行 (Point3d) 时在 DataGrid 的第一列中设置相应的行号。但无论何时删除一行,行号都不会更新,因为转换器是通过添加新的DataGridRow 来触发的。现在我通过将DataGrid.DataContext 设置为null 来强制取消更新并再次添加:

this.pointList.RemoveAt(0);
dgrPoints.ItemsSource = null;
dgrPoints.ItemsSource = this.pointList;

这不是实现行号取消更新的正确解决方案。每当pointList 更改时,为每一行触发转换器的最佳方法是什么?

我不想使用DataGridRow.Header 属性,因为这会取代我的列。如果我错了,请纠正我。除此之外,我们欢迎所有考虑不同方法的建议。

【问题讨论】:

    标签: c# wpf binding


    【解决方案1】:

    您可以处理LoadingRowItemContainerGenerator.ItemsChanged 事件并将DataGridRow 容器的Tag 属性设置为相应的行号:

    public Window1()
    {
        InitializeComponent();
        dgrPoints.ItemContainerGenerator.ItemsChanged += ItemContainerGenerator_ItemsChanged;
        dgrPoints.LoadingRow += DgrPoints_LoadingRow;
        dgrPoints.ItemsSource = ...;
    }
    
    private void DgrPoints_LoadingRow(object sender, DataGridRowEventArgs e)
    {
        e.Row.Tag = (e.Row.GetIndex() + 1).ToString();
    }
    
    private void ItemContainerGenerator_ItemsChanged(object sender, ItemsChangedEventArgs e)
    {
        IEnumerable<DataGridRow> rows = FindVisualChildren<DataGridRow>(dgrPoints);
        foreach (DataGridRow row in rows)
            row.Tag = (row.GetIndex() + 1).ToString();
    }
    
    private static IEnumerable<T> FindVisualChildren<T>(DependencyObject dependencyObject) where T : DependencyObject
    {
        if (dependencyObject != null)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(dependencyObject); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(dependencyObject, i);
                if (child != null && child is T)
                {
                    yield return (T)child;
                }
    
                foreach (T childOfChild in FindVisualChildren<T>(child))
                {
                    yield return childOfChild;
                }
            }
        }
    }
    

    <DataGrid Name="dgrPoints" AutoGenerateColumns="False" 
              ItemsSource="{Binding UpdateSourceTrigger=Default, Mode=OneWay}"
              CanUserSortColumns="False"
              IsReadOnly="True">
        <DataGrid.Columns>
            <DataGridTextColumn x:Name="colI" Binding="{Binding Path=Tag, Mode=OneWay, 
                RelativeSource={RelativeSource AncestorType=DataGridRow}}"/>
            <DataGridTextColumn x:Name="colX" Binding="{Binding X}" Header="X"/>
            <DataGridTextColumn x:Name="colY" Binding="{Binding Y}" Header="Y"/>
            <DataGridTextColumn x:Name="colZ" Binding="{Binding Z}" Header="Z"/>
        </DataGrid.Columns>
    </DataGrid>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-12-12
      • 2013-04-10
      • 2017-11-05
      • 1970-01-01
      • 2014-09-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多