2012-09-03 72 views
0

我有一个附加的行为到一个ItemsControl滚动到底部,每当添加一个新的项目。由于我正在使用聊天类型的程序,因此如果用户的滚动条不在最底部,那么我不希望它滚动,否则会非常烦人(有些聊天程序会这样做,这很糟糕)。如何以编程方式获取ItemsControl滚动条位置?

我该如何做到这一点?我不知道如何访问包装ScrollViewer,或者弄清楚我是否需​​要将它放入视图中。

这是我从StackOverflow上的某个人得到的行为类。我仍然在自己学习行为。

public class ScrollOnNewItem : Behavior<ItemsControl> 
{ 
    protected override void OnAttached() 
    { 
     AssociatedObject.Loaded += OnLoaded; 
     AssociatedObject.Unloaded += OnUnLoaded; 
    } 

    protected override void OnDetaching() 
    { 
     AssociatedObject.Loaded -= OnLoaded; 
     AssociatedObject.Unloaded -= OnUnLoaded; 
    } 

    private void OnLoaded(object sender, RoutedEventArgs e) 
    { 
     var incc = AssociatedObject.ItemsSource as INotifyCollectionChanged; 
     if (incc == null) return; 

     incc.CollectionChanged += OnCollectionChanged; 
    } 

    private void OnUnLoaded(object sender, RoutedEventArgs e) 
    { 
     var incc = AssociatedObject.ItemsSource as INotifyCollectionChanged; 
     if (incc == null) return; 

     incc.CollectionChanged -= OnCollectionChanged; 
    } 

    private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 
    { 
     if (e.Action == NotifyCollectionChangedAction.Add) 
     { 
      int count = AssociatedObject.Items.Count; 
      if (count == 0) 
       return; 

      var item = AssociatedObject.Items[count - 1]; 

      var frameworkElement = AssociatedObject.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement; 
      if (frameworkElement == null) return; 

      frameworkElement.BringIntoView(); 
     } 
    } 
} 

回答

3

好的,这是我为自己提出的答案。

我想到了依赖对象有一个GetSelfAndAncestors方法。使用它,我能够获得我的AssociatedObject(ItemsControl)的ScrollViewer祖先(如果有的话)并用它来操纵它。

所以我加入这个领域对我的行为

private ScrollViewer scrollViewer; 
private bool isScrollDownEnabled; 

而在装载的事件处理程序,我用下面的代码

scrollViewer = AssociatedObject.GetSelfAndAncestors().Where(a => a.GetType().Equals(typeof(ScrollViewer))).FirstOrDefault() as ScrollViewer; 

分配它,并在OnCollectionChanged事件处理程序中,我径自在if语句中包含所有逻辑如下

 if (scrollViewer != null) 
     { 
      isScrollDownEnabled = scrollViewer.ScrollableHeight > 0 && scrollViewer.VerticalOffset + scrollViewer.ViewportHeight < scrollViewer.ExtentHeight; 

      if (e.Action == NotifyCollectionChangedAction.Add && !isScrollDownEnabled) 
      { 
       // Do stuff 
      } 
     } 

所以所有t ogether,代码如下所示

public class ScrollOnNewItem : Behavior<ItemsControl> 
{ 
    private ScrollViewer scrollViewer; 
    private bool isScrollDownEnabled; 

    protected override void OnAttached() 
    { 
     AssociatedObject.Loaded += OnLoaded; 
     AssociatedObject.Unloaded += OnUnLoaded; 
    } 

    protected override void OnDetaching() 
    { 
     AssociatedObject.Loaded -= OnLoaded; 
     AssociatedObject.Unloaded -= OnUnLoaded; 
    } 

    private void OnLoaded(object sender, RoutedEventArgs e) 
    { 
     var incc = AssociatedObject.ItemsSource as INotifyCollectionChanged; 
     if (incc == null) return; 

     incc.CollectionChanged += OnCollectionChanged; 
     scrollViewer = AssociatedObject.GetSelfAndAncestors().Where(a => a.GetType().Equals(typeof(ScrollViewer))).FirstOrDefault() as ScrollViewer; 
    } 

    private void OnUnLoaded(object sender, RoutedEventArgs e) 
    { 
     var incc = AssociatedObject.ItemsSource as INotifyCollectionChanged; 
     if (incc == null) return; 

     incc.CollectionChanged -= OnCollectionChanged; 
    } 

    private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 
    { 
     if (scrollViewer != null) 
     { 
      isScrollDownEnabled = scrollViewer.ScrollableHeight > 0 && scrollViewer.VerticalOffset + scrollViewer.ViewportHeight < scrollViewer.ExtentHeight; 

      if (e.Action == NotifyCollectionChangedAction.Add && !isScrollDownEnabled) 
      { 
       int count = AssociatedObject.Items.Count; 
       if (count == 0) 
        return; 

       var item = AssociatedObject.Items[count - 1]; 

       var frameworkElement = AssociatedObject.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement; 
       if (frameworkElement == null) return; 

       frameworkElement.BringIntoView(); 
      } 
     } 
    } 
} 

正如在评论中问道,使用行为,我只是需要一个新的xmlns添加到代码中包含我的行为该地区的我的XAML文件。

   xmlns:behaviors="clr-namespace:Infrastructure.Behaviors;assembly=Infrastructure" 

然后在控制上我只是添加了行为。

<ScrollViewer VerticalScrollBarVisibility="Auto"> 
     <ItemsControl Name="Blah" ItemsSource="{Binding Messages}" ItemTemplate="{StaticResource MessageTemplate}"> 
      <i:Interaction.Behaviors> 
       <behaviors:ScrollOnNewItem /> 
      </i:Interaction.Behaviors> 
     </ItemsControl> 
    </ScrollViewer> 

类只是互动的命名空间。 xmlns:i =“clr-namespace:System.Windows.Interactivity; assembly = System.Windows.Interactivity”

+0

如果你提供代码如何使用这个类,会更好! – Nolesh

+0

我继续前进,编辑底部以显示如何使用它。这只是在视图中添加行为的问题。 – Cowman

+0

感谢您的答复! +1 – Nolesh

1

还有另一种方法来实现此行为。这种方式比以上更容易。所有你应该做的是调用像下面的方法:

public void AppendText(RichTextBox richTextBox, string data){  
    richTextBox.AppendText(data); 
    bool isScrollDownEnabled = richTextBox.VerticalOffset == 0 || 
     richTextBox.VerticalOffset + richTextBox.ViewportHeight == richTextBox.ExtentHeight; 
    if (isScrollDownEnabled) 
     richTextBox.ScrollToEnd(); 
} 

适用于TextBox了。

+0

谢谢你的回答。 – Cowman

相关问题