zoukankan      html  css  js  c++  java
  • 解决WPF程序中ListBox ItemsSource变化时不重置ScrollBar的问题

    当我们改变ListBox的ItemsSource时,会发现这样一个问题:数据源变化时,虽然控件中的内容会跟着变化,但滚动条却不会重置

    举个例子:

    1. 将ListBox绑定到一百个字符串:listbox.ItemsSource = Enumerable.Range(0, 100).Select(i => "## " + i);
    2. 将ListBox的滚动条拖到最后,使之能看到最后的"## 99",看不到最开始的"## 0"。
    3. 将ListBox绑定到另外一百个字符串:listbox.ItemsSource = Enumerable.Range(0, 100).Select(i => ">> " + i);这时我们会发现:虽然数据内容会变更,但滚动条仍然在最后,能看到最后的">> 99",看不到最开始的">> 0"。

    大多数情况下,这个并不是我们所期望的结果。如何解决这个问题,stackoverflow文章Reset scrollbar on ItemsSource change给了一个解决方案:找到ListBox的ScrollViewer,响应ListBox的SourceUpdated事件,滚动滚动条到顶端。

    listbox.SourceUpdated += (_1, _2) => scrollView.ScrollToTop();

    这种方法本身没有什么问题,但是由于ScrollViewer是视觉树的一部分,从ListBox上获取并不容易(可能会修改模板)。我后来又从Wordpress文章ListBox – Automatically scroll CurrentItem into View上找到了一个方案:响应ListBox的Items.CurrentChanged事件,通过函数ScrollIntoView实现滚动到顶端。

        listbox.Items.CurrentChanged += (_1, _2) => listbox.ScrollIntoView(listbox.Items[0]);

    原文本来的目的是为了实现将ListBox自动滚动到CurrentItem,也可用来解决这个问题。原文更是实现了一个附加属性,使得可以在XAML中直接使用,来非常方便。

    <ListBox local:ListBoxExtenders.AutoScrollToCurrentItem="True"/>

    由于众所周知的原因,Wordpress这个并不存在的网站只能从火星上访问,没有火星专线的朋友可以找方校长借,或者直接参考我下面的代码(稍微修改了点,貌似也没有什么bug)。

    View Code 
     1     /// <summary>
     2     /// This class contains a few useful extenders for the ListBox
     3     /// </summary>
     4     public class ListBoxExtenders : DependencyObject
     5     {
     6         #region Properties
     7 
     8         public static readonly DependencyProperty AutoScrollToCurrentItemProperty = DependencyProperty.RegisterAttached("AutoScrollToCurrentItem",
     9             typeof(bool), typeof(ListBoxExtenders), new UIPropertyMetadata(default(bool), OnAutoScrollToCurrentItemChanged));
    10 
    11         /// <summary>
    12         /// Returns the value of the AutoScrollToCurrentItemProperty
    13         /// </summary>
    14         /// <param name="obj">The dependency-object whichs value should be returned</param>
    15         /// <returns>The value of the given property</returns>
    16         public static bool GetAutoScrollToCurrentItem(DependencyObject obj)
    17         {
    18             return (bool)obj.GetValue(AutoScrollToCurrentItemProperty);
    19         }
    20 
    21         /// <summary>
    22         /// Sets the value of the AutoScrollToCurrentItemProperty
    23         /// </summary>
    24         /// <param name="obj">The dependency-object whichs value should be set</param>
    25         /// <param name="value">The value which should be assigned to the AutoScrollToCurrentItemProperty</param>
    26         public static void SetAutoScrollToCurrentItem(DependencyObject obj, bool value)
    27         {
    28             obj.SetValue(AutoScrollToCurrentItemProperty, value);
    29         }
    30 
    31         #endregion
    32 
    33         #region Events
    34 
    35         /// <summary>
    36         /// This method will be called when the AutoScrollToCurrentItem
    37         /// property was changed
    38         /// </summary>
    39         /// <param name="sender">The sender (the ListBox)</param>
    40         /// <param name="e">Some additional information</param>
    41         public static void OnAutoScrollToCurrentItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    42         {
    43             var listBox = sender as ListBox;
    44             if ((listBox == null) || (listBox.Items == null))
    45                 return;
    46 
    47             var enable = (bool)e.NewValue;
    48             var autoScrollToCurrentItemWorker = new EventHandler((_1, _2) => OnAutoScrollToCurrentItem(listBox, listBox.Items.CurrentPosition));
    49 
    50             if (enable)
    51                 listBox.Items.CurrentChanged += autoScrollToCurrentItemWorker;
    52             else
    53                 listBox.Items.CurrentChanged -= autoScrollToCurrentItemWorker;
    54         }
    55 
    56         /// <summary>
    57         /// This method will be called when the ListBox should
    58         /// be scrolled to the given index
    59         /// </summary>
    60         /// <param name="listBox">The ListBox which should be scrolled</param>
    61         /// <param name="index">The index of the item to which it should be scrolled</param>
    62         public static void OnAutoScrollToCurrentItem(ListBox listBox, int index)
    63         {
    64             if (listBox != null && listBox.Items != null && listBox.Items.Count > index && index >= 0)
    65                 listBox.ScrollIntoView(listBox.Items[index]);
    66         }
    67 
    68         #endregion
    69
  • 相关阅读:
    PLC 控制系统资源
    .net core 控制台程序生成EXE
    EF Core 日志跟踪sql语句
    Oracle 设置自启动
    数据库批量插入数据
    字符串面试题系列之六:在字符串中删除特定的字符
    字符串面试题系列之五:删除字符串空格
    字符串面试题系列之四:字符串匹配二
    字符串面试题系列之三:左旋转字符串
    字符串面试题系列之二:连续最长数字串
  • 原文地址:https://www.cnblogs.com/TianFang/p/2669680.html
Copyright © 2011-2022 走看看