列表框滚动查看器-默认滚动到底部

本文关键字:滚动 默认 底部 列表 | 更新日期: 2023-09-27 18:03:32

我有一个必须自动滚动到底部的ListBox。在我的应用程序中,我必须检测某些项是否已经对用户可见,并在可见的情况下执行一些业务逻辑。我在这里使用虚拟化,它只在项目(vm)可见时才调用项目(vm)属性。

对于自动滚动,我使用的是完美的listbox.ScrollIntoView(listbox.SelectedItem);,问题是ScrollIntoView只会在ListBox已经加载和渲染后运行,这意味着它首先从开始显示一些项目,之后它将滚动到底部…这对我来说是不可取的。我只是想立即滚动到底部(在ListBox渲染之前)。

下面是自动滚动到底部的行为:
protected override void OnAttached()
{
    base.OnAttached();
    this.AssociatedObject.SelectionChanged += AssociatedObject_SelectionChanged;
}
void AssociatedObject_SelectionChanged(object sender, EventArgs e)
{
    if (sender is ListBox)
    {
        ListBox listbox = (sender as ListBox);
        if (listbox.SelectedItem != null)
        {
            listbox.Dispatcher.BeginInvoke((Action)(() =>
            {
                listbox.UpdateLayout();
                if (listbox.SelectedItem != null)
                {
                    listbox.ScrollIntoView(listbox.SelectedItem);
                }
            }));
        }
    }
}
protected override void OnDetaching()
{
    base.OnDetaching();
    this.AssociatedObject.SelectionChanged -= AssociatedObject_SelectionChanged;
}

我的ListBox被设置为IsSynchronizedWithCurrentItem="True",它的ItemsSource被绑定到ICollectionView,我使用MoveCurrentToLast

所以问题是:有没有办法滚动到底部而不首先渲染它的顶部?

列表框滚动查看器-默认滚动到底部

我将您附加的命令复制为

public class MyBehavior : Behavior<ListBox>
    {

到一个XAML

    <ListBox SelectedItem="SelCust" Name="MyListBox" Loaded="MyListBox_Loaded" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="Name"  ItemsSource="{Binding Customers}">
        <i:Interaction.Behaviors>
            <local:MyBehavior/>
        </i:Interaction.Behaviors>
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="Loaded">
                <i:InvokeCommandAction Command="{Binding Path=LoadCommand}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </ListBox>

,我还添加了一个绑定的Load事件到ViewModel

    public CustomerViewModel()
    {
        IList<Customer> customers = Customer.GetCustomers().ToList();
        _customerView = CollectionViewSource.GetDefaultView(customers);
        _customerView.MoveCurrentToLast();
        _customerView.CurrentChanged += CustomerSelectionChanged;
    }
    private void CustomerSelectionChanged(object sender, EventArgs e)
    {
        // React to the changed selection
        Debug.WriteLine("Here");
        var sel = (sender as CollectionView).CurrentItem;
        if ( sel!= null)
        {
            //Do Something          
        }
    }
    private DelegateCommand loadCommand;
    public ICommand LoadCommand
    {
        get
        {
            if (loadCommand == null)
            {
                loadCommand = new DelegateCommand(VMLoad);
            }
            return (ICommand)loadCommand;
        }
    }
    bool IsLoaded = false;
    private void VMLoad(object obj)
    {
        IsLoaded = true;
    }

和代码后面的

    public MainWindow()
    {
        InitializeComponent();
        DataContext = new CustomerViewModel();
    }
    private void MyListBox_Loaded(object sender, RoutedEventArgs e)
    {
        MyListBox.ScrollIntoView(MyListBox.Items[MyListBox.Items.Count - 1]);
    }

当我调试它时,我看到这是触发的事件序列:

  1. CurrentChanged与最后一项的集合
  2. Loaded处理程序中的代码隐藏
  3. LoadCommand在ViewModel中,只有之后的
  4. ScrollIntoView from AssociatedObject_SelectionChanged

所以基本上我的建议是:

    Loaded处理程序中添加(另一个)ScrollIntoView(用于集合的最后一项)后台代码
  • 对于任何你需要执行的动作,当你必须检测是否有些项目已经对用户可见时,首先检查IsLoaded以排除任何瞬态效应

在设置了DataContext或ItemSource之后,为什么不简单地滚动到集合中的最后一个值?在设置数据上下文和退出构造函数之前,不会呈现任何数据。根据我的理解,如果你在构造函数中按顺序执行以下步骤,它应该像预期的那样工作。

listBox.DataContext = _items;
listBox.ScrollIntoView(_items.Last());