XAML in Xamarin.Forms 基礎篇 電子書

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

Xamarin.Forms 快速入門 電子書

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

2017/05/05

ListView 的顯示紀錄客製化與互動應用

我們使用 Xamarin.Forms 進行對於 ListView 的使用,很多人都會覺得有些好奇,究竟相關 ListView 上的不同操作與應用,該如何撰寫 View / ViewModel 的 XAML / C# 程式碼呢?
我撰寫的一個範例專案(原始碼位於 https://github.com/vulcanlee/xamarin-forms-develop-notes-example/tree/master/XFListShowImg ),用來說明底下需求:
  1. 在Listview新增判斷邏輯,假如是值True,呈現某張圖,若是False,則不秀圖出來
  2. 當使用者點選某個紀錄上的 BoxView 控制項的時候,若該圖片沒有顯示出來,則需要修正,經圖片顯示出來。
這是一個上過課的學員提問的問題,我使用底下範例專案做出說明。

ListView 的 XAML

由於,我們需要將集合資料使用 ListView 顯示出來,當然,我們需要孰悉 ListView 的各種屬性的操作;在這裡,我們需要使用 ListView 的這些屬性
  • ItemsSource : 將會綁訂到 ListView 上的 ObservableCollection<T> 的 C# 屬性,只要我們有增/修/刪 這個 ObservableCollection<T> 的物件內容,ListView 則會自動進行更新。
  • SelectedItem : 當使用者點選某個紀錄項目的時候,當時點選的紀錄項目,將會透過資料綁定方法,把當時點選的紀錄物件值,綁定到 ViewModel 上的指定屬性物件上。
  • HasUnevenRows : 這個屬性用來指定,每個紀錄可以不用具有相同的高度。

每筆紀錄的顯示內容 ViewCell

要設計每筆紀錄在 ListView 上要呈現甚麼樣貌,我們就需要使用 ViewCell 來宣告;在底下,列出了這個 ViewCell 的完整定義。
我們使用了 Grid 將 ViewCell 切割成為 2 Rows X 2 Columns,並且將 Label / BoxView / Image 這三個控制項,使用Grid附加屬性,指定這些控制項是要位於 Grid 的哪個格子上。
在這些控制項上,要顯示的內容或者顏色,將會透過資料綁定的方式,指定到當時紀錄的ViewModel 中的屬性上,在這裡要特別注意到,此時,ViewCell 的 BindingContext 物件,不是整個 ListView 要顯示的集合物件,而是,要顯示的那筆紀錄的物件。
關於圖片在某些情境(在這裡是依據 ViewModel 內的某個屬性值),不需要顯示出來;這樣的需求有很多種作法,也需要是開發環境的各種條件而定,在這裡,我們使用了 IsVisible="{Binding ShowImage}" 這樣的方式來處理,只要該記錄在 ViewModel 內的 ShowImage 的值為 False 的布林值,他就不會顯示在螢幕上,反之,就會顯示在螢幕上。
                    <ViewCell>
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="*"/>
                                <RowDefinition Height="*"/>
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="100"/>
                            </Grid.ColumnDefinitions>

                            <Label 
                                Grid.Row="0" Grid.Column="0"
                                Text="{Binding Number}"/>
                            <BoxView
                                Grid.Row="1" Grid.Column="0"
                                Color="{Binding BoxColor}">
                            </BoxView>
                            <Image
                                Grid.Row="0" Grid.Column="1"
                                Grid.RowSpan="2"
                                HorizontalOptions="Fill" VerticalOptions="Fill"
                                Aspect="AspectFill"
                                Source="{Binding ImageUrl}"
                                IsVisible="{Binding ShowImage}"/>
                        </Grid>
                    </ViewCell>

如何指定 ViewCell 內的命令,要綁定到 ViewModel 中的命令物件

我們這裡有個需求,那就是,使用者可以點選 BoxView 這個控制項,接著,我們需要判斷這個紀錄的圖片是否沒有顯示出來,若沒有,我們須將這個圖片切換成為可以顯示的模式。
我們想要將這個手勢操作命令定義在頁面用的 ViewModel 上,而不是在每筆紀錄的 ViewModel 上。
為了解決這個問題,我們需要在設定綁定命令的時候,變更當時的 BindingContext 的來源,在這裡,我們使用了底下方法:
我們設定了 BoxView.GestureRecognizers 的屬性,加入了 TapGestureRecognizer 這個物件;不過,當要使用 TapGestureRecognizer 的 Command 屬性的時候,我們使用了 Source 這個屬性,指定此次綁定的 BindingContext 的來源,是這個頁面的 ViewModel,接著,我們使用了 Path 這個屬性,指定了要綁定的命令路徑。
其中,x:Reference ThisPage 的 ThisPage 指的就是這個頁面,因為,我們在頁面上,設定了 x:Name="ThisPage" 這個延伸標記屬性設定。
                            <BoxView
                                Grid.Row="1" Grid.Column="0"
                                Color="{Binding BoxColor}">
                                <BoxView.GestureRecognizers>
                                    <TapGestureRecognizer 
                                        Command="{Binding Path=BindingContext.TapBoxCommand, Source={x:Reference ThisPage}}"
                                        CommandParameter="{Binding}"/>
                                </BoxView.GestureRecognizers>
                            </BoxView>

ViewModel 內的程式碼

在這裡,我們使用了 PropertyChanged.Fody 這個套件,因此,簡化了整個 ViewModel 的程式碼數量,關於要綁定到 View 中的各個屬性,其定義如下。
在這裡,要綁定到 ListView 上的集合資料,需要使用 ObservableCollection<MyItem> 這個型別,而命令的部分,我們使用了 Prism 提供的 DelegateCommand<MyItem> 的命令型別,這個泛型命令型別,可以讓我們接收到,來自 XAML 的 CommandParameter 屬性所綁定的內容,在這個應用範例中,CommandParameter 將會綁定當時所顯示的紀錄物件。
若您不想使用 DelegateCommand<MyItem> 泛型命令型別,您也可以使用 DelegateCommand 這個型別,不過,要取得使用者當時所點選的物件,您可以使用 SelectedMyItem 這個物件來取得當時點選的紀錄物件。
        public MyItem SelectedMyItem { get; set; }
        public ObservableCollection<MyItem> MyItems { get; set; } = new ObservableCollection<MyItem>();

        public DelegateCommand<MyItem> TapBoxCommand { get; set; }
當使用點選某筆紀錄後,將會執行底下程式碼:
            TapBoxCommand = new DelegateCommand<MyItem>(x =>
              {
                  if(x.ShowImage == false)
                  {
                      x.ShowImage = true;
                      x.BoxColor = Color.BlueViolet;
                  }
              });

執行結果的畫面

沒有留言:

張貼留言