WPF 4 DataGrid:将行号获取到 RowHeader 中

发布于 2024-10-11 05:36:50 字数 177 浏览 7 评论 0原文

我希望将行号放入 WPF 4 DataGrid 的 RowHeader 中,以便它有一个类似于 Excel 的列用于显示 DataGrid 的行号。

我在网上看到的解决方案建议向业务对象添加索引字段。这并不是一个真正的选择,因为 DataGrid 将被频繁使用,并且我们不想不断跟踪这些索引字段的更改。

多谢

I am looking to get the row number into the RowHeader of the WPF 4 DataGrid so it has an Excel-like column for the row numbers of the DataGrid.

The solution I've seen out there on the web suggests adding an index field to the business objects. This isn't really an option because the DataGrid will be getting resorted a lot and we don't want to have to keep track of changing these index fields constantly.

Thanks a lot

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

温柔戏命师 2024-10-18 05:36:50

一种方法是将它们添加到 DataGrid 的 LoadingRow 事件中。

<DataGrid Name="DataGrid" LoadingRow="DataGrid_LoadingRow" ... />
void DataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
    // Adding 1 to make the row count start at 1 instead of 0
    // as pointed out by daub815
    e.Row.Header = (e.Row.GetIndex() + 1).ToString(); 
}

更新
为了使其能够与 WPF Toolkit 中的 .NET 3.5 DataGrid 一起使用,需要进行一些修改。使用虚拟化时,索引仍然可以正确生成,但输出失败。对 RowHeaderTemplate 的以下修改修复了此

<toolkit:DataGrid LoadingRow="DataGrid_LoadingRow">
    <toolkit:DataGrid.RowHeaderTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type toolkit:DataGridRow}},
                                      Path=Header}"/>
        </DataTemplate>
    </toolkit:DataGrid.RowHeaderTemplate>
</toolkit:DataGrid>

编辑 2012-07-05
如果从源列表中添加或删除项目,则数字会不同步,直到滚动列表,从而再次调用 LoadingRow 。解决这个问题有点复杂,我现在能想到的最佳解决方案是保留上面的 LoadingRow 解决方案,并

  • 订阅 dataGrid.ItemContainerGenerator.ItemsChanged
  • 中在事件处理程序中,找到可视化树中的所有子 DataGridRows
  • 将 Header 设置为每个 DataGridRow 的索引

以下是执行此操作的附加行为。像这样使用它

<DataGrid ItemsSource="{Binding ...}"
          behaviors:DataGridBehavior.DisplayRowNumber="True">

DisplayRowNumber

public class DataGridBehavior
{
    #region DisplayRowNumber

    public static DependencyProperty DisplayRowNumberProperty =
        DependencyProperty.RegisterAttached("DisplayRowNumber",
                                            typeof(bool),
                                            typeof(DataGridBehavior),
                                            new FrameworkPropertyMetadata(false, OnDisplayRowNumberChanged));
    public static bool GetDisplayRowNumber(DependencyObject target)
    {
        return (bool)target.GetValue(DisplayRowNumberProperty);
    }
    public static void SetDisplayRowNumber(DependencyObject target, bool value)
    {
        target.SetValue(DisplayRowNumberProperty, value);
    }

    private static void OnDisplayRowNumberChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        DataGrid dataGrid = target as DataGrid;
        if ((bool)e.NewValue == true)
        {
            EventHandler<DataGridRowEventArgs> loadedRowHandler = null;
            loadedRowHandler = (object sender, DataGridRowEventArgs ea) =>
            {
                if (GetDisplayRowNumber(dataGrid) == false)
                {
                    dataGrid.LoadingRow -= loadedRowHandler;
                    return;
                }
                ea.Row.Header = ea.Row.GetIndex();
            };
            dataGrid.LoadingRow += loadedRowHandler;

            ItemsChangedEventHandler itemsChangedHandler = null;
            itemsChangedHandler = (object sender, ItemsChangedEventArgs ea) =>
            {
                if (GetDisplayRowNumber(dataGrid) == false)
                {
                    dataGrid.ItemContainerGenerator.ItemsChanged -= itemsChangedHandler;
                    return;
                }
                GetVisualChildCollection<DataGridRow>(dataGrid).
                    ForEach(d => d.Header = d.GetIndex());
            };
            dataGrid.ItemContainerGenerator.ItemsChanged += itemsChangedHandler;
        }
    }

    #endregion // DisplayRowNumber

    #region Get Visuals

    private static List<T> GetVisualChildCollection<T>(object parent) where T : Visual
    {
        List<T> visualCollection = new List<T>();
        GetVisualChildCollection(parent as DependencyObject, visualCollection);
        return visualCollection;
    }

    private static void GetVisualChildCollection<T>(DependencyObject parent, List<T> visualCollection) where T : Visual
    {
        int count = VisualTreeHelper.GetChildrenCount(parent);
        for (int i = 0; i < count; i++)
        {
            DependencyObject child = VisualTreeHelper.GetChild(parent, i);
            if (child is T)
            {
                visualCollection.Add(child as T);
            }
            if (child != null)
            {
                GetVisualChildCollection(child, visualCollection);
            }
        }
    }

    #endregion // Get Visuals
}

One way is to add them in the LoadingRow event for the DataGrid.

<DataGrid Name="DataGrid" LoadingRow="DataGrid_LoadingRow" ... />
void DataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
    // Adding 1 to make the row count start at 1 instead of 0
    // as pointed out by daub815
    e.Row.Header = (e.Row.GetIndex() + 1).ToString(); 
}

Update
To get this to work with the .NET 3.5 DataGrid in WPF Toolkit a little modification is needed. The index is still generated correctly but the output fails when using virtualization. The following modification to the RowHeaderTemplate fixes this

<toolkit:DataGrid LoadingRow="DataGrid_LoadingRow">
    <toolkit:DataGrid.RowHeaderTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type toolkit:DataGridRow}},
                                      Path=Header}"/>
        </DataTemplate>
    </toolkit:DataGrid.RowHeaderTemplate>
</toolkit:DataGrid>

Edit 2012-07-05
If items are added or removed from the source list then the numbers get out of sync until the list is scrolled so LoadingRow is called again. Working around this issue is a little more complex and the best solution I can think of right now is to keep the LoadingRow solution above and also

  • Subscribe to dataGrid.ItemContainerGenerator.ItemsChanged
  • In the event handler, find all the child DataGridRows in the visual tree
  • Set the Header to the index for each DataGridRow

Here is an attached behavior which does this. Use it like this

<DataGrid ItemsSource="{Binding ...}"
          behaviors:DataGridBehavior.DisplayRowNumber="True">

DisplayRowNumber

public class DataGridBehavior
{
    #region DisplayRowNumber

    public static DependencyProperty DisplayRowNumberProperty =
        DependencyProperty.RegisterAttached("DisplayRowNumber",
                                            typeof(bool),
                                            typeof(DataGridBehavior),
                                            new FrameworkPropertyMetadata(false, OnDisplayRowNumberChanged));
    public static bool GetDisplayRowNumber(DependencyObject target)
    {
        return (bool)target.GetValue(DisplayRowNumberProperty);
    }
    public static void SetDisplayRowNumber(DependencyObject target, bool value)
    {
        target.SetValue(DisplayRowNumberProperty, value);
    }

    private static void OnDisplayRowNumberChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        DataGrid dataGrid = target as DataGrid;
        if ((bool)e.NewValue == true)
        {
            EventHandler<DataGridRowEventArgs> loadedRowHandler = null;
            loadedRowHandler = (object sender, DataGridRowEventArgs ea) =>
            {
                if (GetDisplayRowNumber(dataGrid) == false)
                {
                    dataGrid.LoadingRow -= loadedRowHandler;
                    return;
                }
                ea.Row.Header = ea.Row.GetIndex();
            };
            dataGrid.LoadingRow += loadedRowHandler;

            ItemsChangedEventHandler itemsChangedHandler = null;
            itemsChangedHandler = (object sender, ItemsChangedEventArgs ea) =>
            {
                if (GetDisplayRowNumber(dataGrid) == false)
                {
                    dataGrid.ItemContainerGenerator.ItemsChanged -= itemsChangedHandler;
                    return;
                }
                GetVisualChildCollection<DataGridRow>(dataGrid).
                    ForEach(d => d.Header = d.GetIndex());
            };
            dataGrid.ItemContainerGenerator.ItemsChanged += itemsChangedHandler;
        }
    }

    #endregion // DisplayRowNumber

    #region Get Visuals

    private static List<T> GetVisualChildCollection<T>(object parent) where T : Visual
    {
        List<T> visualCollection = new List<T>();
        GetVisualChildCollection(parent as DependencyObject, visualCollection);
        return visualCollection;
    }

    private static void GetVisualChildCollection<T>(DependencyObject parent, List<T> visualCollection) where T : Visual
    {
        int count = VisualTreeHelper.GetChildrenCount(parent);
        for (int i = 0; i < count; i++)
        {
            DependencyObject child = VisualTreeHelper.GetChild(parent, i);
            if (child is T)
            {
                visualCollection.Add(child as T);
            }
            if (child != null)
            {
                GetVisualChildCollection(child, visualCollection);
            }
        }
    }

    #endregion // Get Visuals
}
浮华 2024-10-18 05:36:50

编辑:显然滚动会更改索引,因此绑定不会像那样工作......

(看似)干净的模板解决方案:
Xaml:

<Window
    ...
    xmlns:local="clr-namespace:Test"
    DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}">
    <Window.Resources>
        <local:RowToIndexConv x:Key="RowToIndexConv"/>
    </Window.Resources>
        <DataGrid ItemsSource="{Binding GridData}">
            <DataGrid.RowHeaderTemplate>
                <DataTemplate>
                    <TextBlock Margin="2" Text="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow}, Converter={StaticResource RowToIndexConv}}"/>
                </DataTemplate>
            </DataGrid.RowHeaderTemplate>
        </DataGrid>
</Window>

转换器:

public class RowToIndexConv : IValueConverter
{

    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        DataGridRow row = value as DataGridRow;
        return row.GetIndex() + 1;
    }

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

    #endregion
}

Edit: Apparently scrolling changes the index so the binding won't work like that...

A (seemingly) clean templating solution:
Xaml:

<Window
    ...
    xmlns:local="clr-namespace:Test"
    DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}">
    <Window.Resources>
        <local:RowToIndexConv x:Key="RowToIndexConv"/>
    </Window.Resources>
        <DataGrid ItemsSource="{Binding GridData}">
            <DataGrid.RowHeaderTemplate>
                <DataTemplate>
                    <TextBlock Margin="2" Text="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow}, Converter={StaticResource RowToIndexConv}}"/>
                </DataTemplate>
            </DataGrid.RowHeaderTemplate>
        </DataGrid>
</Window>

Converter:

public class RowToIndexConv : IValueConverter
{

    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        DataGridRow row = value as DataGridRow;
        return row.GetIndex() + 1;
    }

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

    #endregion
}
春庭雪 2024-10-18 05:36:50

如果添加或删除行,所有这些方法都将不起作用。在这种情况下,您应该刷新行索引。看看这个行为:

public static class DataGridBehavior
{
    #region RowNumbers property

    public static readonly DependencyProperty RowNumbersProperty =
        DependencyProperty.RegisterAttached("RowNumbers", typeof (bool), typeof (DataGridBehavior), 
        new FrameworkPropertyMetadata(false, OnRowNumbersChanged));

    private static void OnRowNumbersChanged(DependencyObject source, DependencyPropertyChangedEventArgs args)
    {
        DataGrid grid = source as DataGrid;
        if (grid == null)
            return;
        if ((bool)args.NewValue)
        {
            grid.LoadingRow += onGridLoadingRow;
            grid.UnloadingRow += onGridUnloadingRow;
        }
        else
        {
            grid.LoadingRow -= onGridLoadingRow;
            grid.UnloadingRow -= onGridUnloadingRow;
        }
    }

    private static void refreshDataGridRowNumbers(object sender)
    {
        DataGrid grid = sender as DataGrid;
        if (grid == null)
            return;

        foreach (var item in grid.Items)
        {
            var row = (DataGridRow)grid.ItemContainerGenerator.ContainerFromItem(item);
            if (row != null)
                row.Header = row.GetIndex() + 1;
        }
    }

    private static void onGridUnloadingRow(object sender, DataGridRowEventArgs e)
    {
        refreshDataGridRowNumbers(sender);
    }

    private static void onGridLoadingRow(object sender, DataGridRowEventArgs e)
    {
        refreshDataGridRowNumbers(sender);
    }

    [AttachedPropertyBrowsableForType(typeof(DataGrid))]
    public static void SetRowNumbers(DependencyObject element, bool value)
    {
        element.SetValue(RowNumbersProperty, value);
    }

    public static bool GetRowNumbers(DependencyObject element)
    {
        return (bool) element.GetValue(RowNumbersProperty);
    }

    #endregion
}

All of this approaches will not work if you add or remove rows. You should refresh row indexes in such cases. Look at this behavior:

public static class DataGridBehavior
{
    #region RowNumbers property

    public static readonly DependencyProperty RowNumbersProperty =
        DependencyProperty.RegisterAttached("RowNumbers", typeof (bool), typeof (DataGridBehavior), 
        new FrameworkPropertyMetadata(false, OnRowNumbersChanged));

    private static void OnRowNumbersChanged(DependencyObject source, DependencyPropertyChangedEventArgs args)
    {
        DataGrid grid = source as DataGrid;
        if (grid == null)
            return;
        if ((bool)args.NewValue)
        {
            grid.LoadingRow += onGridLoadingRow;
            grid.UnloadingRow += onGridUnloadingRow;
        }
        else
        {
            grid.LoadingRow -= onGridLoadingRow;
            grid.UnloadingRow -= onGridUnloadingRow;
        }
    }

    private static void refreshDataGridRowNumbers(object sender)
    {
        DataGrid grid = sender as DataGrid;
        if (grid == null)
            return;

        foreach (var item in grid.Items)
        {
            var row = (DataGridRow)grid.ItemContainerGenerator.ContainerFromItem(item);
            if (row != null)
                row.Header = row.GetIndex() + 1;
        }
    }

    private static void onGridUnloadingRow(object sender, DataGridRowEventArgs e)
    {
        refreshDataGridRowNumbers(sender);
    }

    private static void onGridLoadingRow(object sender, DataGridRowEventArgs e)
    {
        refreshDataGridRowNumbers(sender);
    }

    [AttachedPropertyBrowsableForType(typeof(DataGrid))]
    public static void SetRowNumbers(DependencyObject element, bool value)
    {
        element.SetValue(RowNumbersProperty, value);
    }

    public static bool GetRowNumbers(DependencyObject element)
    {
        return (bool) element.GetValue(RowNumbersProperty);
    }

    #endregion
}
把回忆走一遍 2024-10-18 05:36:50

@Fredrik Hedblad 的答案对我有用。谢谢!

我添加了另一个属性来获取“偏移”值,以便 DataGrid 可以从 0 或 1(或任何设置的值)开始。

要使用该行为,(注意“b”是命名空间)

<DataGrid ItemsSource="{Binding ...}"
      b:DataGridBehavior.DisplayRowNumberOffset="1"
      b:DataGridBehavior.DisplayRowNumber="True">

修改后的类:

/// <summary>
/// Collection of DataGrid behavior
/// </summary>
public static class DataGridBehavior
{
    #region DisplayRowNumberOffset

    /// <summary>
    /// Sets the starting value of the row header if enabled
    /// </summary>
    public static DependencyProperty DisplayRowNumberOffsetProperty =
        DependencyProperty.RegisterAttached("DisplayRowNumberOffset",
                                            typeof(int),
                                            typeof(DataGridBehavior),
                                            new FrameworkPropertyMetadata(0, OnDisplayRowNumberOffsetChanged));

    public static int GetDisplayRowNumberOffset(DependencyObject target)
    {
        return (int)target.GetValue(DisplayRowNumberOffsetProperty);
    }

    public static void SetDisplayRowNumberOffset(DependencyObject target, int value)
    {
        target.SetValue(DisplayRowNumberOffsetProperty, value);
    }

    private static void OnDisplayRowNumberOffsetChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        DataGrid dataGrid = target as DataGrid;
        int offset = (int)e.NewValue;

        if (GetDisplayRowNumber(target))
        {
            WPFUtil.GetVisualChildCollection<DataGridRow>(dataGrid).
                    ForEach(d => d.Header = d.GetIndex() + offset);
        }
    }

    #endregion

    #region DisplayRowNumber

    /// <summary>
    /// Enable display of row header automatically
    /// </summary>
    /// <remarks>
    /// Source: 
    /// </remarks>
    public static DependencyProperty DisplayRowNumberProperty =
        DependencyProperty.RegisterAttached("DisplayRowNumber",
                                            typeof(bool),
                                            typeof(DataGridBehavior),
                                            new FrameworkPropertyMetadata(false, OnDisplayRowNumberChanged));

    public static bool GetDisplayRowNumber(DependencyObject target)
    {
        return (bool)target.GetValue(DisplayRowNumberProperty);
    }

    public static void SetDisplayRowNumber(DependencyObject target, bool value)
    {
        target.SetValue(DisplayRowNumberProperty, value);
    }

    private static void OnDisplayRowNumberChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        DataGrid dataGrid = target as DataGrid;
        if ((bool)e.NewValue == true)
        {
            int offset = GetDisplayRowNumberOffset(target);

            EventHandler<DataGridRowEventArgs> loadedRowHandler = null;
            loadedRowHandler = (object sender, DataGridRowEventArgs ea) =>
            {
                if (GetDisplayRowNumber(dataGrid) == false)
                {
                    dataGrid.LoadingRow -= loadedRowHandler;
                    return;
                }
                ea.Row.Header = ea.Row.GetIndex() + offset;
            };
            dataGrid.LoadingRow += loadedRowHandler;

            ItemsChangedEventHandler itemsChangedHandler = null;
            itemsChangedHandler = (object sender, ItemsChangedEventArgs ea) =>
            {
                if (GetDisplayRowNumber(dataGrid) == false)
                {
                    dataGrid.ItemContainerGenerator.ItemsChanged -= itemsChangedHandler;
                    return;
                }
                WPFUtil.GetVisualChildCollection<DataGridRow>(dataGrid).
                    ForEach(d => d.Header = d.GetIndex() + offset);
            };
            dataGrid.ItemContainerGenerator.ItemsChanged += itemsChangedHandler;
        }
    }

    #endregion // DisplayRowNumber
}

@Fredrik Hedblad's answer works for me. Thanks!

I added another property to get an "offset" value so the DataGrid can start from either 0 or 1 (or whatever is set).

To use the behavior, (note 'b' is the namespace)

<DataGrid ItemsSource="{Binding ...}"
      b:DataGridBehavior.DisplayRowNumberOffset="1"
      b:DataGridBehavior.DisplayRowNumber="True">

The modified classes:

/// <summary>
/// Collection of DataGrid behavior
/// </summary>
public static class DataGridBehavior
{
    #region DisplayRowNumberOffset

    /// <summary>
    /// Sets the starting value of the row header if enabled
    /// </summary>
    public static DependencyProperty DisplayRowNumberOffsetProperty =
        DependencyProperty.RegisterAttached("DisplayRowNumberOffset",
                                            typeof(int),
                                            typeof(DataGridBehavior),
                                            new FrameworkPropertyMetadata(0, OnDisplayRowNumberOffsetChanged));

    public static int GetDisplayRowNumberOffset(DependencyObject target)
    {
        return (int)target.GetValue(DisplayRowNumberOffsetProperty);
    }

    public static void SetDisplayRowNumberOffset(DependencyObject target, int value)
    {
        target.SetValue(DisplayRowNumberOffsetProperty, value);
    }

    private static void OnDisplayRowNumberOffsetChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        DataGrid dataGrid = target as DataGrid;
        int offset = (int)e.NewValue;

        if (GetDisplayRowNumber(target))
        {
            WPFUtil.GetVisualChildCollection<DataGridRow>(dataGrid).
                    ForEach(d => d.Header = d.GetIndex() + offset);
        }
    }

    #endregion

    #region DisplayRowNumber

    /// <summary>
    /// Enable display of row header automatically
    /// </summary>
    /// <remarks>
    /// Source: 
    /// </remarks>
    public static DependencyProperty DisplayRowNumberProperty =
        DependencyProperty.RegisterAttached("DisplayRowNumber",
                                            typeof(bool),
                                            typeof(DataGridBehavior),
                                            new FrameworkPropertyMetadata(false, OnDisplayRowNumberChanged));

    public static bool GetDisplayRowNumber(DependencyObject target)
    {
        return (bool)target.GetValue(DisplayRowNumberProperty);
    }

    public static void SetDisplayRowNumber(DependencyObject target, bool value)
    {
        target.SetValue(DisplayRowNumberProperty, value);
    }

    private static void OnDisplayRowNumberChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        DataGrid dataGrid = target as DataGrid;
        if ((bool)e.NewValue == true)
        {
            int offset = GetDisplayRowNumberOffset(target);

            EventHandler<DataGridRowEventArgs> loadedRowHandler = null;
            loadedRowHandler = (object sender, DataGridRowEventArgs ea) =>
            {
                if (GetDisplayRowNumber(dataGrid) == false)
                {
                    dataGrid.LoadingRow -= loadedRowHandler;
                    return;
                }
                ea.Row.Header = ea.Row.GetIndex() + offset;
            };
            dataGrid.LoadingRow += loadedRowHandler;

            ItemsChangedEventHandler itemsChangedHandler = null;
            itemsChangedHandler = (object sender, ItemsChangedEventArgs ea) =>
            {
                if (GetDisplayRowNumber(dataGrid) == false)
                {
                    dataGrid.ItemContainerGenerator.ItemsChanged -= itemsChangedHandler;
                    return;
                }
                WPFUtil.GetVisualChildCollection<DataGridRow>(dataGrid).
                    ForEach(d => d.Header = d.GetIndex() + offset);
            };
            dataGrid.ItemContainerGenerator.ItemsChanged += itemsChangedHandler;
        }
    }

    #endregion // DisplayRowNumber
}
撩心不撩汉 2024-10-18 05:36:50

LoadingRowEvent 是这样触发的:

ICollectionView view = CollectionViewSource.GetDefaultView(_dataGrid.ItemsSource);
view.Refresh();

LoadingRowEvent is triggered by this:

ICollectionView view = CollectionViewSource.GetDefaultView(_dataGrid.ItemsSource);
view.Refresh();
橙味迷妹 2024-10-18 05:36:50

另一个解决方案与上面的 IValueConverter 答案有些相似,但限制很少,但具有巨大的优势。

  1. ItemsSource 必须绑定到实现 IList 的集合,因为需要 IndexOf 方法来查找项目的索引。 ObservableCollection 在这里很好。
  2. 集合项与 IndexOf 的比较方法不同。

该解决方案的优点是独立于DataGrid 的虚拟化类型。特别是默认虚拟化设置有效。因此它可以在相当大的虚拟化网格中正常工作。

首先,我们需要一个多值转换器:

public class RowIndexConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values.Length == 2 && values[1] is IList items)
        {
            int index = items.IndexOf(values[0]);
            if (index >= 0)
            {
                return $"{index + 1}";
            }
        }
        return string.Empty;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

然后在 XAML 资源中注册它:

<Window.Resources>
    <local:RowIndexConverter x:Key="RowIndexConverter"/>
</Window.Resources>

然后在 DataGrid 的行标题模板中使用该转换器:

<DataGrid.RowHeaderTemplate>
    <ItemContainerTemplate>
        <TextBlock>
            <TextBlock.Text>
                <MultiBinding Converter="{StaticResource RowIndexConverter}">
                    <Binding Path="DataContext" RelativeSource="{RelativeSource AncestorType=DataGridRow}"/>
                    <Binding Path="ItemsSource" RelativeSource="{RelativeSource AncestorType=DataGrid}"/>
                </MultiBinding>
            </TextBlock.Text>
        </TextBlock>
    </ItemContainerTemplate>
</DataGrid.RowHeaderTemplate>

就是这样。

Another solution which is somewhat similar to the above answer with IValueConverter but have few restrictions and a huge advantage.

  1. The ItemsSource must be bound to the collection which implements IList bacause the method IndexOf is required to find the item's index. ObservableCollection<T> is fine here.
  2. Collection items are unique from IndexOf's approach to compare.

The advantage of the solution is independency of DataGrid's virtualization type. In particular default virtualization settings works. So it will work properly with pretty large virtualized grids.

First of all we need a multi-value converter:

public class RowIndexConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values.Length == 2 && values[1] is IList items)
        {
            int index = items.IndexOf(values[0]);
            if (index >= 0)
            {
                return 
quot;{index + 1}";
            }
        }
        return string.Empty;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

Then register it in XAML resources:

<Window.Resources>
    <local:RowIndexConverter x:Key="RowIndexConverter"/>
</Window.Resources>

Then use the converter in DataGrid's row header template:

<DataGrid.RowHeaderTemplate>
    <ItemContainerTemplate>
        <TextBlock>
            <TextBlock.Text>
                <MultiBinding Converter="{StaticResource RowIndexConverter}">
                    <Binding Path="DataContext" RelativeSource="{RelativeSource AncestorType=DataGridRow}"/>
                    <Binding Path="ItemsSource" RelativeSource="{RelativeSource AncestorType=DataGrid}"/>
                </MultiBinding>
            </TextBlock.Text>
        </TextBlock>
    </ItemContainerTemplate>
</DataGrid.RowHeaderTemplate>

That's it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文