侧边栏壁纸
  • 累计撰写 82 篇文章
  • 累计创建 11 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录
WPF

ListView附加属性自滚动

祈安千
2025-11-04 / 0 评论 / 0 点赞 / 3 阅读 / 0 字

Why

  • 最近在回顾一些有关于附加属性和依赖属性的相关知识,因为很久没写过这一部分的代码了,所以有一些知识点忘记了,正好做一个简单的附加属性实现ListView的自滚动记录一下。

How

  • 主要的思路是:首先创建一个附加属性(AutoScrollToEnd),然后监听ListView绑定的内容改变的时候进行滚动到最后一行,比较简单。
  • 以下是Helper内容,至于为啥取名叫TextBoxHelper,这个不重要~
 public class TextBoxHelper
 {


     public static string GetTitle(DependencyObject obj)
     {
         return (string)obj.GetValue(TitleProperty);
     }

     public static void SetTitle(DependencyObject obj, string value)
     {
         obj.SetValue(TitleProperty, value);
     }

     public static readonly DependencyProperty TitleProperty =
         DependencyProperty.RegisterAttached("Title", typeof(string), typeof(TextBoxHelper), new PropertyMetadata(""));





     public static bool GetAutoScrollToEnd(DependencyObject obj)
     {
         return (bool)obj.GetValue(AutoScrollToEndProperty);
     }

     public static void SetAutoScrollToEnd(DependencyObject obj, bool value)
     {
         obj.SetValue(AutoScrollToEndProperty, value);
     }

     public static readonly DependencyProperty AutoScrollToEndProperty =
         DependencyProperty.RegisterAttached("AutoScrollToEnd", typeof(bool), typeof(TextBoxHelper), new PropertyMetadata(false, OnAutoScrollToEndChanged));

     private static void OnAutoScrollToEndChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
     {
         if (!(d is ListView listView))
         {
             return;
         }
         if ((bool)e.NewValue)
         {
             listView.Loaded += ListView_Loaded;
         }
         else
         {
             listView.Loaded -= ListView_Loaded;
         }


     }

     private static void ListView_Loaded(object sender, RoutedEventArgs e)
     {
         var listView = sender as ListView;
         var scrollViewer = FindScrollViewer(listView);
         if (scrollViewer == null)
             return;
         if (listView.Items.SourceCollection is INotifyCollectionChanged collectionChanged)
         {
             collectionChanged.CollectionChanged += (_, __) =>
             {
                 scrollViewer.ScrollToEnd();
             };
         }
     }

     private static void OnScrollCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
     {
         if (d is TextBox box)
         {
             var title = GetTitle(box);
             SetTitle(box, e.NewValue.ToString());
         }
         else if (d is ListView listView)
         {
             var scrollViewer = FindScrollViewer(listView);
             if (scrollViewer == null)
                 return;
             scrollViewer.ScrollToEnd();
         }
     }
     private static ScrollViewer FindScrollViewer(DependencyObject obj)
     {
         if (obj is ScrollViewer)
             return obj as ScrollViewer;

         for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
         {
             var child = VisualTreeHelper.GetChild(obj, i);
             var result = FindScrollViewer(child);
             if (result != null)
                 return result;
         }

         return null;
     }
 }
  • 可以看到里面如何找到滚动条的方法,网上很多资料都比较复杂。
  • 里面的Title是后面演示附加属性如何绑定用到的。
 <StackPanel VerticalAlignment="Center">
     <Button Content="Side View" />
     <TextBox comm:TextBoxHelper.Title="Hello World!" Text="{Binding RelativeSource={RelativeSource Self}, Path=(comm:TextBoxHelper.Title)}" />
     <Button Command="{Binding ChangeCountCommand}" Content="Change Count" />
     <ListView
         Height="100"
         comm:TextBoxHelper.AutoScrollToEnd="True"
         ItemsSource="{Binding Names}" />
 </StackPanel>
  • 注意附加属性绑定的时候一定需要加上(),固定格式。
  • 中间按钮是模仿添加一个名字。
public SideViewModel()
{
    Enumerable.Range(0, 100).ToList().ForEach(i => Names.Add(i.ToString()));
}

private ObservableCollection<string> names = new ObservableCollection<string>();

public ObservableCollection<string> Names
{
    get { return names; }
    set { SetProperty(ref names, value); }
}


public RelayCommand ChangeCountCommand => new RelayCommand(ChangeCount);

private void ChangeCount(object obj)
{
    Random random = new Random();
    Names.Add(random.Next(100).ToString());
}

Tips

  • 很简单,但是涉及到的知识点还算是比较多的,比如附加属性如何绑定,比如寻找滚动条还有listView.Items.SourceCollection其实可以转换成类型INotifyCollectionChanged
  • 如果想要精确控制,例如某个方法不滚动到最后,那个可以改写AutoScrollToEndint类型,然后只有这个值改变的时候才触发滚动事件即可。
0
博主关闭了所有页面的评论