2016-08-23 61 views
1

我有一个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是我现在用MoveCurrentToLast绑定到ICollectionView

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

+0

AFAIK,滚动是一个属于VisualTree的进程。如果没有树,则不会滚动。解决方案[这里](http://stackoverflow.com/a/26274919/4558029) – lokusking

+0

向下滚动对我来说不是一个问题,效果很好......问题在于它在初始化时始终显示一些项目。我不能简单地说:嘿,只要向下滚动到底部而不显示顶部!对我来说问题是当列表达到最高点时我运行了一些命令。 –

+0

我试过你的代码,无法重现你所描述的内容。你能提供一些数据(VM)来处理吗? – lokusking

回答

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> 

在那里我还添加了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; 
    } 

and in the code-behind

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来自AssociatedObject_SelectionChanged

所以基本上我建议两件事:

  • 添加(另一个)ScrollIntoView(用于收集的最后一个项目)从Loaded处理器在 代码隐藏
  • 无论您需要执行什么操作当您必须检测用户是否已经看到某些项目时,首先检查IsLoaded以排除任何瞬态效应
-1

为什么不在设置DataContext或ItemSource后简单地滚动到集合中的最后一个值?直到您设置数据上下文并且直到您退出构造函数为止,才会呈现数据。据我的理解,如果你在构造函数中按顺序执行以下步骤,它应该按预期工作。

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