本篇之因此起這樣一個名字,是由於重點並不是如何自定義控件,不涉及建立CustomControl和UserControl使用的Template和XAML概念。而是經過繼承的方法來擴展一個現有的類,在繼承的子類中增長屬性和擴展行爲。git
咱們在《UWP開發入門(七)——下拉刷新》中提到過嵌套ScrollViewer的實現思路,本篇咱們對ListView的第一個擴展行爲,便是摒棄嵌套的作法,而是經過訪問ListView內部的ScrollViewer控件,來監聽ViewChanged事件。github
訪問ListView內部的ScrollViewer,一定離不開VisualTreeHelper類中的如下兩個方法: app
public static DependencyObject GetChild(DependencyObject reference, System.Int32 childIndex); public static System.Int32 GetChildrenCount(DependencyObject reference);
能夠將這兩個方法進一步組合獲得:async
static T FindFirstChild<T>(FrameworkElement element) where T : FrameworkElement { int childrenCount = VisualTreeHelper.GetChildrenCount(element); var children = new FrameworkElement[childrenCount]; for (int i = 0; i < childrenCount; i++) { var child = VisualTreeHelper.GetChild(element, i) as FrameworkElement; children[i] = child; if (child is T) return (T)child; } for (int i = 0; i < childrenCount; i++) if (children[i] != null) { var subChild = FindFirstChild<T>(children[i]); if (subChild != null) return subChild; } return null; }
該方法經過遞歸來遍歷FrameworkElement內部的元素,並返回第一個符合類型的元素。ListViewEx的第一個擴展以下:this
public class ListViewEx : ListView, INotifyPropertyChanged { private ScrollViewer _scrollViewer; public event EventHandler LoadHistoryEvent; public event PropertyChangedEventHandler PropertyChanged; protected void OnProperyChanged([CallerMemberName] string name = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); } public ListViewEx() { this.Loaded += ListViewEx_Loaded; this.Unloaded += ListViewEx_Unloaded; } private void ListViewEx_Unloaded(object sender, RoutedEventArgs e) { this.Unloaded -= ListViewEx_Unloaded; if (_scrollViewer != null) { _scrollViewer.ViewChanged -= Sv_ViewChanged; } } private void ListViewEx_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e) { this.Loaded -= ListViewEx_Loaded; _scrollViewer = FindFirstChild<ScrollViewer>(this); if (_scrollViewer != null) { _scrollViewer.ViewChanged += Sv_ViewChanged; } } private async void Sv_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e) { if (e.IsIntermediate == false && _scrollViewer.VerticalOffset < 1) { _scrollViewer.ChangeView(null, 50, null); await Task.Delay(10); LoadHistoryEvent?.Invoke(this, EventArgs.Empty); } }
嗯嗯,能夠看到優雅的 -= event和恰到好處的null check,啊啊!忍不住想點個贊!在ViewChanged事件監測到滾動條到達頂部後,果斷觸發ListViewEx內定義的LoadHistoryEvent來通知更新數據。spa
本篇若是僅增長一個LoadHistoryEvent,又會被人非議是在補完前篇,一篇拆成兩篇寫……那好吧,咱們再給ListViewEx增長第二個擴展GoBottomVisiblity屬性,顧名思義便是ListViewEx向上滾動幾屏之後,顯示一個GoBottom的按鈕。code
在ListViewEx的類中,首先定義屬性GoBottomVisibility,而後一樣是在ViewChanged事件中,計算是否顯示GoBottom按鈕。blog
public Visibility GoBottomVisiblity { get { return _goBottomVisiblity; } set { _goBottomVisiblity = value; this.OnProperyChanged(); } } private void Sv_ViewChanged2(object sender, ScrollViewerViewChangedEventArgs e) { if (e.IsIntermediate == false) { CheckGoBottomVisibility(); } } private void CheckGoBottomVisibility() { if (_scrollViewer.VerticalOffset + _scrollViewer.ViewportHeight < _scrollViewer.ScrollableHeight) { GoBottomVisiblity = Visibility.Visible; } else { GoBottomVisiblity = Visibility.Collapsed; } }
代碼無法所有貼上來,一個是太長了顯得囉嗦,二是會被管理員說沒內涵從首頁刪掉……繼承
大致上本篇就是給ListView擴展了LoadHistoryEvent事件和GoBottomVisibility屬性。最後說說怎麼用,XAML裏使用ListViewEx代替默認的ListView,會發現多出一個LoadHistoryEvent,掛上加載數據的事件就OK了。而後在列表的下部畫一個三角箭頭,Visibility綁定到ListViewEx的GoBottomVisibility屬性上就收工了。遞歸
<Grid> <local:ListViewEx x:Name="listViewEx" ItemsSource="{x:Bind Items}" LoadHistoryEvent="ListViewEx_LoadHistoryEvent"></local:ListViewEx> <Grid Margin="10" VerticalAlignment="Bottom" HorizontalAlignment="Center" Tapped="Grid_Tapped" Visibility="{x:Bind listViewEx.GoBottomVisiblity,Mode=OneWay}"> <Ellipse Fill="LightGray" Width="30" Height="30"></Ellipse> <Polyline Stroke="White" Points="10,10 15,20 20,10"></Polyline> </Grid> </Grid>
完整代碼放在GayHub上,地址:https://github.com/manupstairs/UWPSamples
很是感謝各位捧場,可以點開頁面看到這裏,拜謝了!Orz