WPF 4 DataGrid:将行号放入 RowHeader
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4663771/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WPF 4 DataGrid: Getting the Row Number into the RowHeader
提问by Greg Andora
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.
我希望将行号放入 WPF 4 DataGrid 的 RowHeader 中,因此它有一个类似于 Excel 的列用于 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.
我在网上看到的解决方案建议向业务对象添加一个索引字段。这不是一个真正的选择,因为 DataGrid 将被大量使用,我们不希望必须不断跟踪更改这些索引字段。
Thanks a lot
非常感谢
回答by Fredrik Hedblad
One way is to add them in the LoadingRow event for the DataGrid
.
一种方法是将它们添加到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
更新
要使其与 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>
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
编辑 2012-07-05
如果从源列表中添加或删除项目,则数字不同步,直到列表滚动,因此LoadingRow
再次调用。解决这个问题有点复杂,我现在能想到的最佳解决方案是保留LoadingRow
上面的解决方案,并且
- 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
- 订阅
dataGrid.ItemContainerGenerator.ItemsChanged
- 在事件处理程序中,找到
DataGridRows
可视化树中的所有子项 - 将 Header 设置为每个的索引
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
}
回答by H.B.
Edit: Apparently scrolling changes the index so the binding won't work like that...
编辑:显然滚动会改变索引,所以绑定不会像那样工作......
A (seemingly) clean templating solution:
Xaml:
一个(看似)干净的模板解决方案:
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
}
回答by sedovav
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
}
回答by T.Yu
@Fredrik Hedblad's answer works for me. Thanks!
@Fredrik Hedblad 的回答对我有用。谢谢!
I added another property to get an "offset" value so the DataGrid can start from either 0 or 1 (or whatever is set).
我添加了另一个属性来获取“偏移量”值,因此 DataGrid 可以从 0 或 1(或设置的任何值)开始。
To use the behavior, (note 'b' is the namespace)
要使用该行为,(注意 'b' 是命名空间)
<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
}
回答by user3519482
LoadingRowEvent is triggered by this:
LoadingRowEvent 是由这个触发的:
ICollectionView view = CollectionViewSource.GetDefaultView(_dataGrid.ItemsSource);
view.Refresh();