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

时间:2016-08-23 13:48:19

标签: c# wpf listbox scrollviewer

我有一个ListBox,必须自动滚动到底部。在我的应用程序中,我必须检测用户是否已经看到某些项目,并且如果是这样的话,还要执行一些业务逻辑。我在这里使用虚拟化,只有当它可见时才调用item(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

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

2 个答案:

答案 0 :(得分:1)

我已将您附加的命令复制为

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>

我还在其中为ViewModel

添加了Load事件的绑定
    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. 代码隐藏 中的
  3. Loaded处理程序 ViewModel中只有
  4. LoadCommand,而只有
  5. 来自ScrollIntoView
  6. AssociatedObject_SelectionChanged

    所以基本上我建议做一些事情:

    • ScrollIntoView处理程序中添加(另一个)Loaded(对于集合的最后一项) 代码隐藏
    • 当您必须检测用户是否已看到某些项目时,您需要执行的操作,请先检查IsLoaded以排除任何瞬态效果

答案 1 :(得分:-1)

为什么不在设置DataContext或ItemSource后直接滚动到集合中的最后一个值?在设置数据上下文之前,不会呈现任何数据,直到退出构造函数为止。据我所知,如果你在构造函数中按顺序执行以下步骤,它应该按预期工作。

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