我目前在 WPF 中遇到一个问题,当重新实例化绑定(bind)的 ObservableCollection 时,DataGrid 的水平 ScrollViewer 会捕捉到可能的滚动空间的右侧(显示 DataGrid 最右边的内容)。
即使我在调用绑定(bind)事件时触发手动将 HorizontalOffset 设置为 0 的行为,并在重新绑定(bind)列表后立即调用该事件,0 也会被忽略并且捕捉再次转到右侧。我认为这与 ScrollViewer 中的操作顺序和命令队列有关。
这似乎应该是默认行为(我不确定为什么您会希望在填充数据时滚动条默认对齐到右侧)。有人知道解决此问题的方法吗?
根据要求,我的复制项目中的代码文件。
MainWindow.xaml
<Window x:Class="WpfScrollViewer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfScrollViewer"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal">
<Button Content="Rebind" Command="{Binding RebindCommand}"/>
<Button Content="Clear and Set" Command="{Binding ClearCommand}"/>
</StackPanel>
<DataGrid ItemsSource="{Binding People}" Grid.Row="1" HorizontalScrollBarVisibility="Visible" FontSize="30">
</DataGrid>
</Grid>
Person.cs
namespace WpfScrollViewer
{
public class Person
{
public string FirstNames { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public string Address { get; set; }
public string PostCode { get; set; }
public string PhoneNumber { get; set; }
}
}
DelegateCommand.cs
using System;
using System.Windows.Input;
namespace WpfScrollViewer
{
public class DelegateCommand : ICommand
{
private readonly Action _fn;
private readonly Func<bool> _canExecute;
public event EventHandler CanExecuteChanged;
public DelegateCommand(Action fn, Func<bool> canExecute = null)
{
_fn = fn;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (_canExecute == null)
{
return true;
}
return _canExecute();
}
public void Execute(object parameter)
{
_fn();
}
}
}
MainViewModel.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Input;
namespace WpfScrollViewer
{
public class MainViewModel : INotifyPropertyChanged
{
private readonly Random _random = new Random();
private ObservableCollection<Person> _people;
public ObservableCollection<Person> People
{
get => _people;
set
{
if (_people == value)
{
return;
}
_people = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(People)));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public ICommand RebindCommand { get; }
public ICommand ClearCommand { get; }
public MainViewModel()
{
RebindCommand = new DelegateCommand(RebindPeople);
ClearCommand = new DelegateCommand(ClearAndSetPeople);
}
private void RebindPeople()
{
People = new ObservableCollection<Person>(GetPeople());
}
private void ClearAndSetPeople()
{
var people = GetPeople();
People.Clear();
foreach (var person in people)
{
People.Add(person);
}
}
private List<Person> GetPeople()
{
var people = new List<Person>
{
new Person
{
FirstNames = "John",
LastName = "Doe",
Address = "17 Random Street",
PostCode = "RN32 2JR",
Age = 31,
PhoneNumber = "07647123456"
},
new Person
{
FirstNames = "Jane",
LastName = "Doe",
Address = "17 Random Street",
PostCode = "RN32 2JR",
Age = 30
},
new Person
{
FirstNames = "Jack",
LastName = "Freens",
Address = "37 Badboi Lane",
Age = 30
},
new Person
{
FirstNames = "Richard",
LastName = "Brodget",
Address = "69 Meme Street",
Age = 31
},
new Person
{
FirstNames = "Sam",
LastName = "Orfitt",
Address = "16 Withernsea Road",
Age = 29
},
new Person
{
FirstNames = "Tom",
LastName = "Orfitt",
Address = "16 Withernsea",
Age = 27
}
};
var rmCount = _random.Next(1, 4);
for (var i = 0; i < rmCount; i++)
{
people.RemoveAt(_random.Next(people.Count));
}
return people;
}
}
}
使用“重新绑定(bind)”按钮会显示我在上面描述的行为。确保您稍微向右滚动,水平滚动条将在重新绑定(bind)时捕捉到右侧。如果滚动条完全位于左侧,水平滚动条将正确地向左对齐,正如我希望它在所有情况下都这样做。
干杯
最佳答案
这是由列自动生成功能引起的。每次更改数据时,它都会删除所有列(我猜在这个阶段滚动条位置丢失)并根据传递的数据创建新列。如果您静态定义列并使用 AutoGenerateColumns="False"
参数禁用该功能,它不会重置滚动条位置。
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding People}" Grid.Row="1" HorizontalScrollBarVisibility="Visible" FontSize="30">
<DataGrid.Columns>
<DataGridTextColumn Header="FirstNames" Binding="{Binding FirstNames}" />
<DataGridTextColumn Header="LastName" Binding="{Binding LastName}" />
<DataGridTextColumn Header="Age" Binding="{Binding Age}" />
<DataGridTextColumn Header="Address" Binding="{Binding Address}" />
<DataGridTextColumn Header="PostCode" Binding="{Binding PostCode}" />
<DataGridTextColumn Header="PhoneNumber" Binding="{Binding PhoneNumber}" />
</DataGrid.Columns>
</DataGrid>
如果您想动态生成列并且还需要记住滚动条位置,您可以使用反射从代码后面生成列。缺点是不能绑定(bind),必须手动生成。例如
<DataGrid AutoGenerateColumns="False" Loaded="DataGrid_Loaded" ItemsSource="{Binding People}" Grid.Row="1" HorizontalScrollBarVisibility="Visible" FontSize="30">
</DataGrid>
和DataGrid_Loaded:
DataGrid dg = (DataGrid)sender;
MainViewModel mvm = (MainViewModel)this.DataContext;
Type classType = typeof(Person);
PropertyInfo[] properties = classType.GetProperties();
foreach (PropertyInfo prop in properties) {
dg.Columns.Add(new DataGridTextColumn() {
Binding = new Binding(prop.Name),
Header = prop.Name
});
}
关于c# - WPF:DataGrid 上的水平滚动查看器在重新实例化绑定(bind)的 ObservableCollection 时捕捉到右侧,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57090666/