调用ScrollToBottom()后,ScrollViewer不会停止滚动

时间:2013-10-18 18:54:08

标签: .net wpf scroll scrollviewer

我的应用程序中有一个视图,在ItemsControl中包含ScrollViewer以显示日志消息。作为消息的字符串集合是我的viewmodel中的ObservableCollection<string>

添加新邮件时,ScrollViewer需要滚动到列表底部,具体取决于是否选中了复选框。我想在视图中完全处理这个问题。我找到了一个不错的解决方案(here)。默认情况下,最初会选中该复选框。

我看到了奇怪的行为:

  • 在添加任何消息之前,如果取消选中该复选框,并将消息添加到列表中,则列表不会滚动(正确行为)。
  • 如果我选中复选框并添加消息,则列表会滚动(正确行为)。
  • 如果我在列表中添加了一些消息,则取消选中该框,然后在列表中添加更多内容,列表仍会滚动(行为不正确)。

我把它简化为一个非常简单的WPF应用程序来演示这个问题,如下所示。

导致此问题的原因以及如何解决?

XAML:

<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="229" Width="551">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <CheckBox Grid.Row="0" Content="Autoscroll?" Name="AutoscrollCheckBox"  IsChecked="True" />
        <Button Grid.Row="1" Content="Add a message" Name="AddMessageButton" Click="AddMessageButton_Click" />
        <ScrollViewer Name="MessagesScrollViewer" Grid.Row="2">
            <ItemsControl Name="MessagesList" ItemsSource="{Binding Messages}" />
        </ScrollViewer>
    </Grid>
</Window>

背后的代码:

public partial class MainWindow : Window, INotifyPropertyChanged {

    public MainWindow() {
        InitializeComponent();
        DataContext = this;
        ((INotifyCollectionChanged)MessagesList.Items).CollectionChanged += Messages_CollectionChanged;
    }

    private void Messages_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
        Console.WriteLine((bool)AutoscrollCheckBox.IsChecked);
        if (AutoscrollCheckBox.IsChecked.HasValue && (bool)AutoscrollCheckBox.IsChecked && (e.Action == NotifyCollectionChangedAction.Add))
            MessagesScrollViewer.ScrollToBottom();
    }

    private ObservableCollection<string> m_Messages = new ObservableCollection<string>();
    public ObservableCollection<string> Messages {
        get { return m_Messages; }
        set { m_Messages = value; NotifyPropertyChanged("Messages"); }
    }

    private int _msgNumber = 0;

    private void AddMessageButton_Click(object sender, RoutedEventArgs e) {
        Messages.Add(String.Format("Message #{0}", _msgNumber++));
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(string name) {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(name));
    }
}

1 个答案:

答案 0 :(得分:1)

这应该是ScrollViewer Source Code的解释:

如果您致电ScrollToBottom,则Double.PositiveInfinity值将设为VerticalOffset

/// <summary>
/// Vertically scroll to the end of the content.
/// </summary>
public void ScrollToBottom()
{
    EnqueueCommand(Commands.SetVerticalOffset, Double.PositiveInfinity, null);
}

添加新项ScrollViewer后,使用仍为VerticalOffset的最后PositiveInfinity更新视图,这意味着它再次滚动到最后。与

var verticalOffset = MessagesScrollViewer.VerticalOffset;
MessagesScrollViewer.ScrollToVerticalOffset(verticalOffset)‌​;

ScrollViewer设置真实的VerticalOffset,一切都会好的。