XAML in Xamarin.Forms 基礎篇 電子書

XAML in Xamarin.Forms 基礎篇 電子書
XAML in Xamarin.Forms 基礎篇 電子書

Xamarin.Forms 快速入門 電子書

Xamarin.Forms 快速入門 電子書
Xamarin.Forms 快速入門 電子書

2016/11/15

Xamarin.Forms 水平捲動式的 ListView

在 Xamarin.Forms 所提供的內建控制項中,僅能夠提供垂直捲動的方式來呈現出清單資料,不過,有很多時候,我們需要使用能夠水平捲動的 ListView 功能,這個時候,Xamarin.Forms內建的控制項就無法幫助您做到這樣的需求。
不過,在網路上找到一篇文章
他提供了相當不錯的水平捲動的客製化控制項的原始碼,我在這裡進行一些簡化與修正,做到如下圖的效果;不過,若您對這樣的Custom Control 有興趣的話,您首先需要對於 BindableProperty 要有所認識與知道如何實作。

專案原始碼

客製化水平捲動 ListView 控制項

其實,這個客製化控制項相當的簡單,那就是繼承了 ScrollView 這個 Xamarin.Forms 的控制項,並且設定使用的是水平捲動的方式,而要顯示的內容,則是透過了一個可綁定屬性 ItemsSourceProperty 讓開發者可以在 XAML 中來綁定這個屬性,設定要顯示的資料清單來源。在這個 ItemsSourceProperty 可綁定屬性中,會訂閱 INotifyCollectionChanged 這個事件,其用於告知接收程式發生動態變更,例如當加入和移除項目時,或重新整理整份清單時。因此,當資料有異動的時候,就會同步將 ScrollView 內的 StackLayout 裡面的項目,進行新增或者刪除。
這個客製化控制項,可以使用 DataTemplate 來在 XAML 中自行擴充與定義想要每筆資料長成甚麼樣子。
原作者的有些內容,在這裡我將其移除,因為,我用不到,有興趣的人,可以自行參考原作者的文章。
原始碼如下
    public class HorizontalListView : ScrollView
    {
        readonly StackLayout _imageStack;

        public HorizontalListView()
        {
            this.Orientation = ScrollOrientation.Horizontal;

            _imageStack = new StackLayout
            {
                Orientation = StackOrientation.Horizontal
            };

            this.Content = _imageStack;
        }

        public IList<View> Children
        {
            get
            {
                return _imageStack.Children;
            }
        }

        // http://dotnetbyexample.blogspot.tw/2016/03/xamarin-form-21-upgradesome-surprises.html
        public static readonly BindableProperty ItemsSourceProperty =
            BindableProperty.Create(nameof(ItemsSource), // 屬性名稱 
                typeof(IList),  // 回傳類型
                typeof(HorizontalListView),  // 宣告類型
                default(IList), // 預設值 
                BindingMode.TwoWay,  // 預設資料繫結模式
                propertyChanging: (bindableObject, oldValue, newValue) =>
                {
                    ((HorizontalListView)bindableObject).ItemsSourceChanging();
                },
                propertyChanged: (bindableObject, oldValue, newValue) =>
                {
                    ((HorizontalListView)bindableObject).ItemsSourceChanged(bindableObject, oldValue as IList, newValue as IList);
                }
       );

        public IList ItemsSource
        {
            get
            {
                return (IList)GetValue(ItemsSourceProperty);
            }
            set
            {

                SetValue(ItemsSourceProperty, value);
            }
        }

        void ItemsSourceChanging()
        {
            if (ItemsSource == null)
                return;
        }

        void ItemsSourceChanged(BindableObject bindable, IList oldValue, IList newValue)
        {
            if (ItemsSource == null)
                return;

            var notifyCollection = newValue as INotifyCollectionChanged;
            if (notifyCollection != null)
            {
                notifyCollection.CollectionChanged += (sender, args) =>
                {
                    if (args.NewItems != null)
                    {
                        foreach (var newItem in args.NewItems)
                        {

                            var view = (View)ItemTemplate.CreateContent();
                            var bindableObject = view as BindableObject;
                            if (bindableObject != null)
                                bindableObject.BindingContext = newItem;
                            _imageStack.Children.Add(view);
                        }
                    }
                    if (args.OldItems != null)
                    {
                        // not supported
                        _imageStack.Children.RemoveAt(args.OldStartingIndex);
                    }
                };
            }

        }

        public DataTemplate ItemTemplate
        {
            get;
            set;
        }

    }

如何使用客製化水平捲動 ListView 控制項

由於這個是自訂控制項,因此,想要在頁面中使用,當然必須先要自訂一個命名空間,在這裡,定義了一個 xmlns:Controls="clr-namespace:XFHorListView.Controls" 指向了這個自訂控制項。
這個客製化水平捲動 ListView 控制項支援了 DataTemplate 功能,可以讓開發者自行透過 XAML 來描述資料需要以甚麼方式來呈現;另外,在這個控制項內,若想要設定當使用者點選某個項目的時候,需要執行 ViewModel 內的某個 DelegatedCommand 命令,可以在 DataTemplate 內,設定 View.GestureRecognizers 使用 TapGestureRecognizer 來觸發 ViewModel 內的命令;在範例中,所要觸發的命令將會定義在頁面 ViewModel 內,因此,若要綁定這個命令,需要在進行資料綁定的時候,重新指定綁定資料來源:
Command="{Binding Path=BindingContext.使用者點選Command, Source={x:Reference ThisPage}}"
在這裡,當要進行資料繫結的時候,會重新設定綁定資料來源為根結點,也就是這個頁面,接著使用 Path 來指定要綁定的來源是頁面的 BindingContext 下的使用者點選Command
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
             prism:ViewModelLocator.AutowireViewModel="True"
             xmlns:Controls="clr-namespace:XFHorListView.Controls"
             xmlns:behaviors="clr-namespace:Behaviors;assembly=Behaviors"
             x:Class="XFHorListView.Views.MainPage"
             Title="MainPage"
             x:Name="ThisPage">

    <StackLayout HorizontalOptions="Center" VerticalOptions="Center">
        <Label Text="{Binding Title}" />
        <Controls:HorizontalListView
                ItemsSource="{Binding MyDatas}"
            HorizontalOptions="Fill"
                >
            <Controls:HorizontalListView.ItemTemplate>
                <DataTemplate>
                    <Grid BackgroundColor="Transparent">
                        <Image
                            Source="{Binding ImageUrl}"
                            Aspect="AspectFit">
                            <Image.GestureRecognizers>
                                <TapGestureRecognizer
                                    Command="{Binding Path=BindingContext.使用者點選Command, Source={x:Reference ThisPage}}"
                                    CommandParameter="{Binding}" />
                            </Image.GestureRecognizers>
                        </Image>
                        <BoxView 
                            HeightRequest="30"
                            VerticalOptions="End"
                            Color="{Binding Color}" />
                        <Label
                            Margin="0,0,0,10"
                            VerticalOptions="End"
                            TextColor="White"
                            Text="{Binding Title}" />
                    </Grid>
                </DataTemplate>
            </Controls:HorizontalListView.ItemTemplate>
        </Controls:HorizontalListView>
        <ListView
            ItemsSource="{Binding MyDatas}"
            SelectedItem="{Binding 使用者點選項目}"
            >
            <ListView.Behaviors>
                <behaviors:EventHandlerBehavior EventName="ItemTapped">
                    <behaviors:InvokeCommandAction Command="{Binding ListView使用者點選Command}"  />
                </behaviors:EventHandlerBehavior>
            </ListView.Behaviors>
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="*" />
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="100" />
                                <ColumnDefinition Width="*" />
                            </Grid.ColumnDefinitions>
                            <Image 
                                Grid.Column="0"
                                Source="{Binding ImageUrl}" Aspect="AspectFit" />
                            <BoxView 
                                Grid.Column="1"
                                Color="{Binding Color}" />
                            <Label 
                                Grid.Column="1"
                                TextColor="White"
                                Text="{Binding Title}" />
                        </Grid>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
</ContentPage>

沒有留言:

張貼留言