WPF:如何在调整大小后保持 ListBox SelectedItem 可见?

WPF: How to keep ListBox SelectedItem visible after resizing?

在我们当前的 C# MVVM 项目中,我们使用 ListBox 来显示项目。

ListBox 下方还有一个可以展开的容器。

一切正常。当容器展开时,ListBox 收缩并出现 ScrollBar

但是,如果在 ListBox 的底部选择了一个元素并且展开了容器,则该项目将在 ListBox 的末尾消失。

示例:

容器扩展前:

+--------------+
| Item 1       |
+--------------+
| Item 2       |
+--------------+
| Item 3       |
+--------------+
| Item 4       |
+--------------+
| SelectedItem |
+--------------+
| Item 6       |
+--------------+
| Item 7       |
+--------------+

+------------------+
| Container        |
+------------------+

容器扩展后:

+--------------+---+
| Item 1       | S |
+--------------+ c |
| Item 2       | r |
+--------------+ o |
| Item 3       | l |
+--------------+ l |
| Item 4       |   |
+--------------+---+

+------------------+
|                  |
|                  |
| Container        |
|                  |
|                  |
+------------------+

我想要实现的是保持可见 SelectedItem 而无需滚动到它。

像这样:

+--------------+---+
| Item 2       | S |
+--------------+ c |
| Item 3       | r |
+--------------+ o |
| Item 4       | l |
+--------------+ l |
| SelectedItem |   |
+--------------+---+

+------------------+
|                  |
|                  |
| Container        |
|                  |
|                  |
+------------------+

实现此目标的最佳方法是什么?

我在 SO 或其他任何地方都找不到有关它的任何信息。

我看到可以通过编程方式滚动:

我还发现可以知道 ListBoxItem 何时进入视图 ( and there),但由于我的项目已经加载然后隐藏,我认为这行不通。

我不想在需要此功能的每个视图中都必须复制代码隐藏。我考虑过在附加到 ListBox 的行为中实现它,但我非常怀疑这将是最好的解决方案。

我也考虑过编写自定义 ListBox 控件,但我认为这对于这么小的功能来说太多了。

有人能告诉我实现这种行为的最佳方法吗? 提前致谢。

A Behavior 是将此功能添加到控件的理想方式。下面的代码将在更改选择或调整大小后将 ListBox 的 SelectedItem 滚动到视图中。

public class perListBoxHelper : Behavior<ListBox>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.SelectionChanged += AssociatedObject_SelectionChanged;
        AssociatedObject.SizeChanged += AssociatedObject_SizeChanged;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.SelectionChanged -= AssociatedObject_SelectionChanged;
        AssociatedObject.SizeChanged -= AssociatedObject_SizeChanged;
        base.OnDetaching();
    }

    private static void AssociatedObject_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        ScrollSelectionIntoView(sender as ListBox);
    }

    private static void AssociatedObject_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        ScrollSelectionIntoView(sender as ListBox);
    }

    private static void ScrollSelectionIntoView(ListBox listBox)
    { 
        if (listBox?.SelectedItem == null)
            return;

        Action action = () =>
        {
            listBox.UpdateLayout();
            listBox.ScrollIntoView(listBox.SelectedItem);
        };

        listBox.Dispatcher.BeginInvoke(action, DispatcherPriority.ContextIdle);
    }
}

用法

<ListBox ... >
    <i:Interaction.Behaviors>
        <vhelp:perListBoxHelper />
    </i:Interaction.Behaviors>
</ListBox>

关于我最近 blog post 的行为的更多讨论。