2011-01-11 113 views
19

我正在寻找将行号放入WPF 4 DataGrid的RowHeader,因此它具有类似Excel的DataGrid行号。WPF 4 DataGrid:获取RowHeader的行号

我在网上看到的解决方案建议在业务对象中添加一个索引字段。这不是一个真正的选择,因为DataGrid将会得到很大的提升,我们不希望不断跟踪改变这些索引字段。

非常感谢

回答

36

一种方法是将它们添加在LoadingRow事件为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(); 
} 

更新
为了得到这个在.NET 3.5中的DataGrid WPF工具包需要稍加修改工作。索引仍然正确生成,但使用虚拟化时输出失败。以下修改的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在视觉树
  • 设置头到索引为每个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 
} 
+2

指数从0开始,所以你应该添加1. – kevindaub 2011-01-11 23:29:52

+0

@ daub815:好点 – 2011-01-11 23:31:33

+1

谷歌搜索,你会发现这个答案也在其他论坛中提供,这是完全错误的。它可能工作的条件非常严格,以至于使这个“解决方案”成为一个笑话。就像,如果你添加5个项目到你的列表中,然后永远不要调整它,不要调整它,不要添加或删除项目,不要做任何会影响重新显示这些项目的东西,那么你很好。事实是,行索引是行数据(项目)的内部容器集合中的索引。这与其集合中的项目(数据)本身的索引无关。 – Tony 2012-05-26 13:16:29

7

编辑:显然滚动变化的指标,因此结合不喜欢你的工作...

A(貌似)清洁模板解决方案:
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 
} 
2

这一切如果您添加或删除行办法是行不通的。在这种情况下,您应该刷新行索引。看看这种行为:

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 
} 
1

@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 
} 
0

LoadingRowEvent由该触发:

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