我的应用程序中有一个视图,在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));
}
}
答案 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
,一切都会好的。