解決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

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM