在 TabItems 之间切换后设置 DataGridRow RowDetails 可见性
Setting DataGridRow RowDetails visibility after toggling between TabItems
我有一个TabControl
,在每个TabItem
中有一个DataGrid
。每个 DataGridRow
在 DataGridRowHeader
上都有一个按钮,用于展开和折叠 RowDetails
。
由于我使用了行详情扩展功能,所以将VirtualizingPanel ScrollUnit设置为Pixel,所以滚动更自然一些。
程序在 ObservableCollection<int>
中捕获哪些行已扩展,其中包含要扩展的行索引。
当我切换到新的 TabItem
并返回原来的 TabItem
时,它会丢失已展开的行。
在更改 DataContext
后,我想使用 ObservableCollection
来重置展开的行。我在 DataGrid
DataContextChanged
事件中尝试过这个。
public class DataGridBehaviors : Behavior<DataGrid>{
protected override void OnAttached(){
base.OnAttached();
this.AssociatedObject.DataContextChanged += DataGrid_DataContextChanged;
}
protected override void OnDetaching(){
this.AssociatedObject.DataContextChanged -= DataGrid_DataContextChanged;
base.OnDetaching();
}
private void DataGrid_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e){
ModuleGeometry oldModuleGeometry = (ModuleGeometry)e.OldValue;
ModuleGeometry newModuleGeometry = (ModuleGeometry)e.NewValue;
Task.Factory.StartNew(() => {
PerformRowExpansions(newModuleGeometry);
});
ScrollViewer scrollViewer = GetVisualChild<ScrollViewer>(this.AssociatedObject);
if (scrollViewer != null){
/*
* do some stuff
*/
}
}
private void PerformRowExpansions(ModuleGeometry newModuleGeometry){
ObservableCollection<int> tempExpandedIndexes = new ObservableCollection<int>(newModuleGeometry.ExpandedIndexes);
newModuleGeometry.ExpandedIndexes = new ObservableCollection<int>();
if (this.Dispatcher.CheckAccess()){
foreach (int index in tempExpandedIndexes)
{
DataGridRow row = this.AssociatedObject.ItemContainerGenerator.ContainerFromIndex(index) as DataGridRow;
row.DetailsVisibility = Visibility.Visible;
}
}
else{
this.Dispatcher.Invoke(new Action(() =>{
foreach (int index in tempExpandedIndexes){
DataGridRow row = this.AssociatedObject.ItemContainerGenerator.ContainerFromIndex(index) as DataGridRow;
row.DetailsVisibility = Visibility.Visible;
}
}));
}
}
private static T GetVisualChild<T>(DependencyObject parent) where T : Visual{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++){
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
child = GetVisualChild<T>(v);
if (child != null)
break;
}
return child;
}
}
但是,它不喜欢对当前不在 VirtualizingPanel
中的行执行此操作。
我怎样才能做到这一点?
我应该只扩展当前虚拟化的行吗?
DataGrid
ScrollViewer
是否也应该以编程方式进行操作?我如何解释 ScrollViewer
中不可见的行?
除了我应该将 RowDetails
Visibility
设置为 DataGridRow
之外,还有其他对象吗?
当前不在 VirtualizingPanel
中的行没有 DataGridRow
个容器。这就是虚拟化的工作原理。
如果你想在选项卡切换之间保留一些信息,你应该将要保留的数据存储在你的视图模型中。例如,您可以将 DataGridRowHeader
中的 Button
绑定到一些命令,该命令在基础数据 class 的集合中添加和删除索引,即父 DataContext
DataGridRow
、TabItem
或 TabControl
.
除非禁用 UI 虚拟化,否则尝试遍历可视 DataGridRow
容器将无法工作。这当然可能会导致性能问题。
我有一个TabControl
,在每个TabItem
中有一个DataGrid
。每个 DataGridRow
在 DataGridRowHeader
上都有一个按钮,用于展开和折叠 RowDetails
。
由于我使用了行详情扩展功能,所以将VirtualizingPanel ScrollUnit设置为Pixel,所以滚动更自然一些。
程序在 ObservableCollection<int>
中捕获哪些行已扩展,其中包含要扩展的行索引。
当我切换到新的 TabItem
并返回原来的 TabItem
时,它会丢失已展开的行。
在更改 DataContext
后,我想使用 ObservableCollection
来重置展开的行。我在 DataGrid
DataContextChanged
事件中尝试过这个。
public class DataGridBehaviors : Behavior<DataGrid>{
protected override void OnAttached(){
base.OnAttached();
this.AssociatedObject.DataContextChanged += DataGrid_DataContextChanged;
}
protected override void OnDetaching(){
this.AssociatedObject.DataContextChanged -= DataGrid_DataContextChanged;
base.OnDetaching();
}
private void DataGrid_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e){
ModuleGeometry oldModuleGeometry = (ModuleGeometry)e.OldValue;
ModuleGeometry newModuleGeometry = (ModuleGeometry)e.NewValue;
Task.Factory.StartNew(() => {
PerformRowExpansions(newModuleGeometry);
});
ScrollViewer scrollViewer = GetVisualChild<ScrollViewer>(this.AssociatedObject);
if (scrollViewer != null){
/*
* do some stuff
*/
}
}
private void PerformRowExpansions(ModuleGeometry newModuleGeometry){
ObservableCollection<int> tempExpandedIndexes = new ObservableCollection<int>(newModuleGeometry.ExpandedIndexes);
newModuleGeometry.ExpandedIndexes = new ObservableCollection<int>();
if (this.Dispatcher.CheckAccess()){
foreach (int index in tempExpandedIndexes)
{
DataGridRow row = this.AssociatedObject.ItemContainerGenerator.ContainerFromIndex(index) as DataGridRow;
row.DetailsVisibility = Visibility.Visible;
}
}
else{
this.Dispatcher.Invoke(new Action(() =>{
foreach (int index in tempExpandedIndexes){
DataGridRow row = this.AssociatedObject.ItemContainerGenerator.ContainerFromIndex(index) as DataGridRow;
row.DetailsVisibility = Visibility.Visible;
}
}));
}
}
private static T GetVisualChild<T>(DependencyObject parent) where T : Visual{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++){
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
child = GetVisualChild<T>(v);
if (child != null)
break;
}
return child;
}
}
但是,它不喜欢对当前不在 VirtualizingPanel
中的行执行此操作。
我怎样才能做到这一点?
我应该只扩展当前虚拟化的行吗?
DataGrid
ScrollViewer
是否也应该以编程方式进行操作?我如何解释 ScrollViewer
中不可见的行?
除了我应该将 RowDetails
Visibility
设置为 DataGridRow
之外,还有其他对象吗?
当前不在 VirtualizingPanel
中的行没有 DataGridRow
个容器。这就是虚拟化的工作原理。
如果你想在选项卡切换之间保留一些信息,你应该将要保留的数据存储在你的视图模型中。例如,您可以将 DataGridRowHeader
中的 Button
绑定到一些命令,该命令在基础数据 class 的集合中添加和删除索引,即父 DataContext
DataGridRow
、TabItem
或 TabControl
.
除非禁用 UI 虚拟化,否则尝试遍历可视 DataGridRow
容器将无法工作。这当然可能会导致性能问题。