WPF GridSplitter заклинивает, когда ListView получает фокус - PullRequest
0 голосов
/ 01 ноября 2019

GridSplitters в моем приложении WPF ведут себя странно. Мое приложение имеет 3-панельный макет, реализованный в виде сетки с 5 столбцами, где 0/2/4 имеют контент, а 1/3 имеют GridSplitters. Средняя панель представляет собой Grid с ListView.

Все работает нормально, пока ListView не сфокусируется, и в этот момент разделители в основном перестают двигаться. Вы можете перетащить их на пиксель или два, затем они замерзнут. Приложение видит начальное событие движения, но ничего больше. Вы можете повторить это несколько раз, чтобы переместить разделитель на несколько пикселей, но если вы попытаетесь переместить другой разделитель, предыдущий отскочит назад, где он был. На боковых панелях есть еще два разделителя, которые продолжают работать в обычном режиме.

Перезагрузка проекта приводит к потере фокуса ListView, и разделители начинают работать снова.

Вероятно, это трудно визуализировать,так что короткое видео .

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. Проект с открытым исходным кодом .

1 Ответ

1 голос
/ 01 ноября 2019

Проблема возникает из-за обработки события ItemContainerGenerator.StatusChanged. Когда размер listView изменяется из-за разветвления сетки, для каждого пикселя генерируется четное число, что приводит к нарушению плавности операции.

Быстрое и ленивое решение проблемы заключается в отмене этого обработчика, когдаскольжение происходит, вы можете добиться этого просто с помощью boolean, установите его на true или false, обработав DragStarted и DragOver из GridSplitter s:

  <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, но для этого нужно больше копать.

...