当 ListView 获得焦点时,WPF GridSplitter 会卡住
WPF GridSplitter gets jammed when ListView gets focus
我的 WPF 应用程序中的 GridSplitter 行为异常。我的应用程序有一个 3 面板布局,实现为具有 5 列的网格,其中 0/2/4 有内容,1/3 有 GridSplitter。中间面板是带有 ListView 的 Grid。
一切正常,直到 ListView 获得焦点,此时拆分器大部分停止移动。您可以将它们拖动一两个像素,然后它们就会冻结。该应用程序看到一个初始运动事件,但没有进一步的。您可以重复此操作几次以将拆分器移动多个像素,但如果您尝试移动另一个拆分器,则前一个拆分器会跳回到原来的位置。侧板上还有两个分离器继续正常工作。
重新加载项目导致 ListView 失去焦点,拆分器再次开始工作。
这可能很难想象,所以制作了 a short video。
XAML 非常简单。唯一的怪癖是中心面板被定义了两次,具有互斥的可见性。
<Grid Name="triptychGrid" DockPanel.Dock="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MinWidth="100"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" MinWidth="150"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" MinWidth="100"/>
</Grid.ColumnDefinitions>
<GridSplitter Grid.Column="1" Width="4" HorizontalAlignment="Left"/>
<GridSplitter Grid.Column="3" Width="4" HorizontalAlignment="Center"/>
<Grid Grid.Column="0" Name="leftPanel"> ... </Grid>
<Grid Grid.Column="2" Name="launchPanel" Visibility="{Binding Path=LaunchPanelVisibility}"> ... </Grid>
<Grid Grid.Column="2" Name="codeListGrid" Visibility="{Binding Path=CodeListVisibility}"> ... </Grid>
<Grid Grid.Column="4" Name="rightPanel"> ... </Grid>
</Grid>
面板宽度上有事件侦听器,因此它们会在运行之间被记住。我在应用程序第一次启动时恢复了面板的宽度,但之后不要触摸它们。
在 ListView 获得焦点之前一切正常。如果它完全冻结就不会那么奇怪了。一次移动几个像素然后在触摸其他控件时跳回看起来很奇怪。
我在桌面 运行 最新的 Win10(.NET 报告 10.0.18362)和虚拟机 运行 最新的 Win7 上重现了这个。该项目是 open source.
出现此问题是因为您正在处理 ItemContainerGenerator.StatusChanged
事件。当 listView
由于网格拆分器而调整大小时,每个像素左右都会生成偶数,这会扰乱操作的流畅性。
一个快速而懒惰的解决方法是在滑动进行时取消该处理程序,您只需使用 boolean
即可实现,将其设置为 true
或 false
通过处理 GridSplitter
的 DragStarted
和 DragOver
:
<GridSplitter Name="leftSplitter" Width="4" Grid.Column="1" HorizontalAlignment="Left" DragStarted="LeftSplitter_OnDragStarted" DragOver="LeftSplitter_OnDragOver"/>
<GridSplitter Name="rightSplitter" Width="4" Grid.Column="3" HorizontalAlignment="Center" DragStarted="LeftSplitter_OnDragStarted" DragOver="LeftSplitter_OnDragOver"/>
处理程序看起来像这样:
private bool _isBeingDraged = false;
private void LeftSplitter_OnDragStarted(object sender, DragStartedEventArgs e)
{
_isBeingDraged = true;
}
private void LeftSplitter_OnDragOver(object sender, DragEventArgs e)
{
_isBeingDraged = false;
}
然后更新您的 ItemContainerGenerator_StatusChanged
以考虑该布尔值:
private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e) {
if(_isBeingDraged)
return;
if (codeListView.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) {
int index = codeListView.SelectedIndex;
if (index >= 0) {
ListViewItem item =
(ListViewItem)codeListView.ItemContainerGenerator.ContainerFromIndex(index);
if (item != null) {
item.Focus();
}
}
}
}
ItemContainerGenerator
可能会做一些更复杂的事情,但这需要更多的挖掘。
我的 WPF 应用程序中的 GridSplitter 行为异常。我的应用程序有一个 3 面板布局,实现为具有 5 列的网格,其中 0/2/4 有内容,1/3 有 GridSplitter。中间面板是带有 ListView 的 Grid。
一切正常,直到 ListView 获得焦点,此时拆分器大部分停止移动。您可以将它们拖动一两个像素,然后它们就会冻结。该应用程序看到一个初始运动事件,但没有进一步的。您可以重复此操作几次以将拆分器移动多个像素,但如果您尝试移动另一个拆分器,则前一个拆分器会跳回到原来的位置。侧板上还有两个分离器继续正常工作。
重新加载项目导致 ListView 失去焦点,拆分器再次开始工作。
这可能很难想象,所以制作了 a short video。
XAML 非常简单。唯一的怪癖是中心面板被定义了两次,具有互斥的可见性。
<Grid Name="triptychGrid" DockPanel.Dock="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MinWidth="100"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" MinWidth="150"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" MinWidth="100"/>
</Grid.ColumnDefinitions>
<GridSplitter Grid.Column="1" Width="4" HorizontalAlignment="Left"/>
<GridSplitter Grid.Column="3" Width="4" HorizontalAlignment="Center"/>
<Grid Grid.Column="0" Name="leftPanel"> ... </Grid>
<Grid Grid.Column="2" Name="launchPanel" Visibility="{Binding Path=LaunchPanelVisibility}"> ... </Grid>
<Grid Grid.Column="2" Name="codeListGrid" Visibility="{Binding Path=CodeListVisibility}"> ... </Grid>
<Grid Grid.Column="4" Name="rightPanel"> ... </Grid>
</Grid>
面板宽度上有事件侦听器,因此它们会在运行之间被记住。我在应用程序第一次启动时恢复了面板的宽度,但之后不要触摸它们。
在 ListView 获得焦点之前一切正常。如果它完全冻结就不会那么奇怪了。一次移动几个像素然后在触摸其他控件时跳回看起来很奇怪。
我在桌面 运行 最新的 Win10(.NET 报告 10.0.18362)和虚拟机 运行 最新的 Win7 上重现了这个。该项目是 open source.
出现此问题是因为您正在处理 ItemContainerGenerator.StatusChanged
事件。当 listView
由于网格拆分器而调整大小时,每个像素左右都会生成偶数,这会扰乱操作的流畅性。
一个快速而懒惰的解决方法是在滑动进行时取消该处理程序,您只需使用 boolean
即可实现,将其设置为 true
或 false
通过处理 GridSplitter
的 DragStarted
和 DragOver
:
<GridSplitter Name="leftSplitter" Width="4" Grid.Column="1" HorizontalAlignment="Left" DragStarted="LeftSplitter_OnDragStarted" DragOver="LeftSplitter_OnDragOver"/>
<GridSplitter Name="rightSplitter" Width="4" Grid.Column="3" HorizontalAlignment="Center" DragStarted="LeftSplitter_OnDragStarted" DragOver="LeftSplitter_OnDragOver"/>
处理程序看起来像这样:
private bool _isBeingDraged = false;
private void LeftSplitter_OnDragStarted(object sender, DragStartedEventArgs e)
{
_isBeingDraged = true;
}
private void LeftSplitter_OnDragOver(object sender, DragEventArgs e)
{
_isBeingDraged = false;
}
然后更新您的 ItemContainerGenerator_StatusChanged
以考虑该布尔值:
private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e) {
if(_isBeingDraged)
return;
if (codeListView.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) {
int index = codeListView.SelectedIndex;
if (index >= 0) {
ListViewItem item =
(ListViewItem)codeListView.ItemContainerGenerator.ContainerFromIndex(index);
if (item != null) {
item.Focus();
}
}
}
}
ItemContainerGenerator
可能会做一些更复杂的事情,但这需要更多的挖掘。