我对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();
}
}
}
答案 0 :(得分:3)
好的,这是我为自己想出的答案。
我发现依赖对象有一个GetSelfAndAncestors方法。使用它,我能够获得我的AssociatedObject(ItemsControl)的ScrollViewer祖先(如果有的话)并用它来操作它。
所以我将此字段添加到我的行为
private ScrollViewer scrollViewer;
private bool isScrollDownEnabled;
在OnLoaded事件处理程序中,我使用以下代码
分配了它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
}
}
总而言之,代码如下所示
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();
}
}
}
}
正如评论中所说,要使用行为,我只需要在包含我行为的代码中向区域的xaml文件中添加一个新的xml。
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>
i 类只是交互命名空间。的的xmlns:ⅰ= “CLR-名称空间:System.Windows.Interactivity;装配= System.Windows.Interactivity”强>
答案 1 :(得分: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
。