XAML in Xamarin.Forms 基礎篇 電子書

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

Xamarin.Forms 快速入門 電子書

Xamarin.Forms 快速入門 電子書
Xamarin.Forms 快速入門 電子書
顯示具有 MVVM 標籤的文章。 顯示所有文章
顯示具有 MVVM 標籤的文章。 顯示所有文章

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;
                  }
              });

執行結果的畫面