共用方式為


自訂 ListView

Xamarin.Forms ListView 是將數據收集顯示為垂直清單的檢視。 本文示範如何建立自訂轉譯器,其會封裝平台特定清單控制項和原生資料格配置,讓您對原生清單控制效能擁有更多掌控權。

每個 Xamarin.Forms 檢視都有每個平臺的隨附轉譯器,可建立原生控件的實例。 ListView當應用程式轉Xamarin.Forms譯 時,會在iOS ListViewRenderer 中具現化 類別,進而具現化原生UITableView控件。 在 Android 平台上,ListViewRenderer 類別會具現化原生 ListView 控制項。 在通用 Windows 平台 (UWP) 上,ListViewRenderer 類別會具現化原生 ListView 控制項。 如需控件對應之轉譯器和原生控件類別 Xamarin.Forms 的詳細資訊,請參閱 轉譯器基類和原生控件

下圖說明 ListView 控制項和實作其之對應原生控制項間的關聯性:

ListView 控制項與實作原生控制項之間的關聯性

您可在每個平台上建立 ListView 的自訂轉譯器,利用轉譯程序實作平台特定的自訂。 執行這項作業的程序如下:

  1. 建立Xamarin.Forms自定義控件。
  2. 從Xamarin.Forms取用自定義控制件。
  3. 在每個平台上建立控制項的自訂轉譯器。

現在可以依次討論每個項目,以實作利用平台特定清單控制項和原生資料格配置的 NativeListView 轉譯器。 在移植包含清單和可重複使用之資料格程式碼的現有原生應用程式時,此案例可提供協助。 此外,也允許您精細自訂可能會影響效能的清單控制項功能,例如資料虛擬化。

建立自訂 ListView 控制項

您可以將 ListView 類別子類別化以建立自訂 ListView 控制項,如下列程式碼範例所示:

public class NativeListView : ListView
{
  public static readonly BindableProperty ItemsProperty =
    BindableProperty.Create ("Items", typeof(IEnumerable<DataSource>), typeof(NativeListView), new List<DataSource> ());

  public IEnumerable<DataSource> Items {
    get { return (IEnumerable<DataSource>)GetValue (ItemsProperty); }
    set { SetValue (ItemsProperty, value); }
  }

  public event EventHandler<SelectedItemChangedEventArgs> ItemSelected;

  public void NotifyItemSelected (object item)
  {
    if (ItemSelected != null) {
      ItemSelected (this, new SelectedItemChangedEventArgs (item));
    }
  }
}

NativeListView 會在 .NET Standard 程式庫專案中建立,並定義自訂控制項的 API。 此控制項會公開 Items 屬性用於將資料填入 ListView,且可以是用於顯示用途的資料繫結。 其也會公開 ItemSelected 事件;在平台特定的原生清單控制項中選取項目時,就會觸發該事件。 如需有關資料繫結的詳細資訊,請參閱資料繫結基本概念

使用自訂控制項

您可以宣告控制項的位置命名空間並使用控制項上的命名空間前置詞,在 .NET Standard 程式庫專案中的 XAML 參考 NativeListView 自訂控制項。 下列程式碼範例示範 XAML 頁面如何使用 NativeListView 自訂控制項:

<ContentPage ...
    xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer"
    ...>
    ...
    <ContentPage.Content>
          <Grid>
            <Grid.RowDefinitions>
              <RowDefinition Height="Auto"/>
              <RowDefinition Height="*" />
            </Grid.RowDefinitions>
          <Label Text="{x:Static local:App.Description}" HorizontalTextAlignment="Center" />
            <local:NativeListView Grid.Row="1" x:Name="nativeListView" ItemSelected="OnItemSelected" VerticalOptions="FillAndExpand" />
          </Grid>
      </ContentPage.Content>
</ContentPage>

local 命名空間前置詞沒有命名限制。 不過,clr-namespaceassembly 值必須符合自訂控制項的詳細資料。 一旦宣告命名空間,即會使用前置詞來參考自訂控制項。

下列程式碼範例示範 C# 頁面如何使用 NativeListView 自訂控制項:

public class MainPageCS : ContentPage
{
    NativeListView nativeListView;

    public MainPageCS()
    {
        nativeListView = new NativeListView
        {
            Items = DataSource.GetList(),
            VerticalOptions = LayoutOptions.FillAndExpand
        };

        switch (Device.RuntimePlatform)
        {
            case Device.iOS:
                Padding = new Thickness(0, 20, 0, 0);
                break;
            case Device.Android:
            case Device.UWP:
                Padding = new Thickness(0);
                break;
        }

        Content = new Grid
        {
            RowDefinitions = {
                new RowDefinition { Height = GridLength.Auto },
                new RowDefinition { Height = new GridLength (1, GridUnitType.Star) }
            },
            Children = {
                new Label { Text = App.Description, HorizontalTextAlignment = TextAlignment.Center },
                nativeListView
            }
        };
        nativeListView.ItemSelected += OnItemSelected;
    }
    ...
}

NativeListView 自訂控制項使用平台特定的自訂轉譯器來顯示資料清單,該資料清單會透過 Items 屬性填入。 清單中的每個資料列都包含三個資料項目 – 名稱、類別和影像檔案名稱。 清單中每個資料列的配置,由平台特定的自訂轉譯器定義。

注意

因為 NativeListView 自訂控制項會使用包含捲動功能的平台特定清單控制項來轉譯,因此自訂控制項不應裝載於可捲動的配置控制項中 (例如 ScrollView)。

自訂轉譯器現在可以新增至每個應用程式專案,來建立平台特定的清單控制項和原生資料格配置。

在每個平台上建立自訂轉譯器

建立自訂轉譯器類別的流程如下:

  1. 建立轉譯自訂控制項之 ListViewRenderer 類別的子類別。
  2. 覆寫轉譯自訂控制項的 OnElementChanged 方法,並撰寫自訂方法的邏輯。 建立對應的 Xamarin.FormsListView 時會呼叫這個方法。
  3. ExportRenderer將屬性新增至自定義轉譯器類別,以指定將用來轉Xamarin.Forms譯自定義控件。 這個屬性是用來向 Xamarin.Forms註冊自定義轉譯器。

注意

您可以選擇在每個平台專案中提供自訂轉譯器。 如果自訂轉譯器尚未註冊,則會使用資料格基底類別的預設轉譯器。

下圖說明應用程式範例中每個專案的責任,以及這些專案之間的關聯性:

NativeListView 自訂轉譯器專案責任

NativeListView 自訂控制項是由平台特定轉譯器類別轉譯,其全部衍生自各平台的 ListViewRenderer 類別。 這會導致每個 NativeListView 自訂控制項都使用平台特定的清單控制項轉譯,如下列螢幕擷取畫面所示:

每個平台上的 NativeListView

類別 ListViewRendererOnElementChanged 公開 方法,這個方法會在建立自定義控件以轉譯對應的原生控件時 Xamarin.Forms 呼叫。 此方法會接受 ElementChangedEventArgs 參數,其中包含 OldElementNewElement 屬性。 這些屬性代表Xamarin.Forms轉譯器附加的專案,以及Xamarin.Forms轉譯器分別附加至的專案。 在範例應用程式中,OldElement 屬性會是 null,而 NewElement 屬性會包含 NativeListView 執行個體的參考。

在每個平台特定轉譯器類別中,OnElementChanged 方法的覆寫版本是執行原生控制項自訂的位置。 平台上所使用的原生控制項具型別參考可透過 Control 屬性存取。 此外,可以透過 屬性取得所轉譯之控件的Element參考Xamarin.Forms。

OnElementChanged 方法中訂閱事件處理常式時必須留意,如下列程式碼範例中所示:

protected override void OnElementChanged (ElementChangedEventArgs<Xamarin.Forms.ListView> e)
{
  base.OnElementChanged (e);

  if (e.OldElement != null) {
    // Unsubscribe from event handlers and cleanup any resources
  }

  if (e.NewElement != null) {
    // Configure the native control and subscribe to event handlers
  }
}

只有在自定義轉譯器附加至新 Xamarin.Forms 元素時,才應該設定原生控件,並訂閱事件處理程式。 同樣地,您應該只在轉譯器附加到的項目變更時,才取消訂閱任何已訂閱的事件處理常式。 採用這個方法將有助於建立不會發生記憶體流失的自訂轉譯器。

在每個平臺特定轉譯器類別中,方法的 OnElementPropertyChanged 覆寫版本是回應自定義控件上 Xamarin.Forms 可系結屬性變更的位置。 因為此覆寫會呼叫多次,所以請一律檢查變更的屬性。

每個自定義轉譯器類別都會以 ExportRenderer 向 註冊轉譯器 Xamarin.Forms的屬性裝飾。 屬性會採用兩個參數 :要轉譯之 Xamarin.Forms 自定義控件的類型名稱,以及自定義轉譯器的型別名稱。 屬性的 assembly 前置詞會指定套用至整個組件的屬性。

下列各節會討論每個平台特定自訂轉譯器類別的實作。

在 iOS 上建立自訂轉譯器

下列程式碼範例示範適用於 iOS 平台的自訂轉譯器:

[assembly: ExportRenderer (typeof(NativeListView), typeof(NativeiOSListViewRenderer))]
namespace CustomRenderer.iOS
{
    public class NativeiOSListViewRenderer : ListViewRenderer
    {
        protected override void OnElementChanged (ElementChangedEventArgs<Xamarin.Forms.ListView> e)
        {
            base.OnElementChanged (e);

            if (e.OldElement != null) {
                // Unsubscribe
            }

            if (e.NewElement != null) {
                Control.Source = new NativeiOSListViewSource (e.NewElement as NativeListView);
            }
        }
    }
}

如果自定義轉譯器附加至新Xamarin.Forms元素,控件UITableView是藉由建立 類別的NativeiOSListViewSource實例來設定。 此類別藉由從 UITableViewSource 類別覆寫 RowsInSectionGetCell 方法,以及公開包含要顯示之資料清單的 Items 屬性,來提供資料給 UITableView 控制項。 此類別也會透過叫用由 NativeListView 自訂控制項提供的 ItemSelected 事件,來提供 RowSelected 方法覆寫。 如需方法覆寫的詳細資訊,請參閱子類別化 UITableViewSourceGetCell 方法會傳回以清單中每個資料列之資料填入的 UITableCellView,如下列程式碼範例所示:

public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
{
  // request a recycled cell to save memory
  NativeiOSListViewCell cell = tableView.DequeueReusableCell (cellIdentifier) as NativeiOSListViewCell;

  // if there are no cells to reuse, create a new one
  if (cell == null) {
    cell = new NativeiOSListViewCell (cellIdentifier);
  }

  if (String.IsNullOrWhiteSpace (tableItems [indexPath.Row].ImageFilename)) {
    cell.UpdateCell (tableItems [indexPath.Row].Name
      , tableItems [indexPath.Row].Category
      , null);
  } else {
    cell.UpdateCell (tableItems [indexPath.Row].Name
      , tableItems [indexPath.Row].Category
      , UIImage.FromFile ("Images/" + tableItems [indexPath.Row].ImageFilename + ".jpg"));
  }

  return cell;
}

此方法會為將顯示於螢幕上的每個資料列建立 NativeiOSListViewCell 執行個體。 NativeiOSCell 執行個體會定義每個資料格的配置,以及資料格的資料。 當資料格因捲動而從畫面消失時,資料格將可重複使用。 這可藉由確保僅有資料的 NativeiOSCell 執行個體顯示在畫面上 (而非清單中的所有資料) 來避免浪費記憶體。 如需資料格重複使用的詳細資訊,請參閱資料格重複使用GetCell 方法也會讀取每個資料列的 ImageFilename 屬性 (如存在),並讀取映像且將儲存為 UIImage 執行個體,然後使用資料列的資料 (名稱、類別和映像) 來更新 NativeiOSListViewCell 執行個體。

NativeiOSListViewCell 類別定義每個資料格的配置,如下列程式碼範例所示:

public class NativeiOSListViewCell : UITableViewCell
{
  UILabel headingLabel, subheadingLabel;
  UIImageView imageView;

  public NativeiOSListViewCell (NSString cellId) : base (UITableViewCellStyle.Default, cellId)
  {
    SelectionStyle = UITableViewCellSelectionStyle.Gray;

    ContentView.BackgroundColor = UIColor.FromRGB (218, 255, 127);

    imageView = new UIImageView ();

    headingLabel = new UILabel () {
      Font = UIFont.FromName ("Cochin-BoldItalic", 22f),
      TextColor = UIColor.FromRGB (127, 51, 0),
      BackgroundColor = UIColor.Clear
    };

    subheadingLabel = new UILabel () {
      Font = UIFont.FromName ("AmericanTypewriter", 12f),
      TextColor = UIColor.FromRGB (38, 127, 0),
      TextAlignment = UITextAlignment.Center,
      BackgroundColor = UIColor.Clear
    };

    ContentView.Add (headingLabel);
    ContentView.Add (subheadingLabel);
    ContentView.Add (imageView);
  }

  public void UpdateCell (string caption, string subtitle, UIImage image)
  {
    headingLabel.Text = caption;
    subheadingLabel.Text = subtitle;
    imageView.Image = image;
  }

  public override void LayoutSubviews ()
  {
    base.LayoutSubviews ();

    headingLabel.Frame = new CoreGraphics.CGRect (5, 4, ContentView.Bounds.Width - 63, 25);
    subheadingLabel.Frame = new CoreGraphics.CGRect (100, 18, 100, 20);
    imageView.Frame = new CoreGraphics.CGRect (ContentView.Bounds.Width - 63, 5, 33, 33);
  }
}

這個類別會定義用來轉譯資料格內容的控制項及其配置。 NativeiOSListViewCell 建構函式會建立 UILabel 的執行個體和 UIImageView 控制項,並初始化其外觀。 這些控制項會用於顯示每個資料列的資料,UpdateCell 方法會用於在 UILabelUIImageView 執行個體上設定這項資料。 這些執行個體的位置,會由覆寫的 LayoutSubviews 方法透過指定其資料格內的座標來設定。

回應自訂控制項上的屬性變更

如果 NativeListView.Items 屬性變更,由於正在新增項目至清單或從清單中移除項目,因此自訂轉譯器必須透過顯示變更來回應。 這可藉由覆寫 OnElementPropertyChanged 方法來完成,如下列程式碼範例所示:

protected override void OnElementPropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
  base.OnElementPropertyChanged (sender, e);

  if (e.PropertyName == NativeListView.ItemsProperty.PropertyName) {
    Control.Source = new NativeiOSListViewSource (Element as NativeListView);
  }
}

此方法會建立 NativeiOSListViewSource 類別的新執行個體,該類別會提供資料給 UITableView 控制項,前提是可繫結的 NativeListView.Items 屬性已變更。

在 Android 上建立自訂轉譯器

下列程式碼範例示範適用於 Android 平台的自訂轉譯器:

[assembly: ExportRenderer(typeof(NativeListView), typeof(NativeAndroidListViewRenderer))]
namespace CustomRenderer.Droid
{
    public class NativeAndroidListViewRenderer : ListViewRenderer
    {
        Context _context;

        public NativeAndroidListViewRenderer(Context context) : base(context)
        {
            _context = context;
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.ListView> e)
        {
            base.OnElementChanged(e);

            if (e.OldElement != null)
            {
                // unsubscribe
                Control.ItemClick -= OnItemClick;
            }

            if (e.NewElement != null)
            {
                // subscribe
                Control.Adapter = new NativeAndroidListViewAdapter(_context as Android.App.Activity, e.NewElement as NativeListView);
                Control.ItemClick += OnItemClick;
            }
        }
        ...

        void OnItemClick(object sender, Android.Widget.AdapterView.ItemClickEventArgs e)
        {
            ((NativeListView)Element).NotifyItemSelected(((NativeListView)Element).Items.ToList()[e.Position - 1]);
        }
    }
}

如果自定義轉譯器附加至新Xamarin.Forms元素,則會設定原生ListView控件。 此組態包含建立提供資料給原生 ListView 控制項的 NativeAndroidListViewAdapter 類別執行個體,並註冊事件處理常式來處理 ItemClick 事件。 接著,此處理常式將會叫用由 NativeListView 自訂控制項所提供的 ItemSelected 事件。 如果轉譯器附加至變更的專案,Xamarin.Forms事件ItemClick就會取消訂閱。

NativeAndroidListViewAdapter 衍生自 BaseAdapter 類別並會公開 Items 屬性,該屬性包含要顯示的資料清單,且會覆寫 CountGetViewGetItemIdthis[int] 方法。 如需這些方法覆寫的詳細資訊,請參閱實作 ListAdapterGetView 方法會傳回每資料列的檢視,其由資料填入,如下列程式碼範例所示:

public override View GetView (int position, View convertView, ViewGroup parent)
{
  var item = tableItems [position];

  var view = convertView;
  if (view == null) {
    // no view to re-use, create new
    view = context.LayoutInflater.Inflate (Resource.Layout.NativeAndroidListViewCell, null);
  }
  view.FindViewById<TextView> (Resource.Id.Text1).Text = item.Name;
  view.FindViewById<TextView> (Resource.Id.Text2).Text = item.Category;

  // grab the old image and dispose of it
  if (view.FindViewById<ImageView> (Resource.Id.Image).Drawable != null) {
    using (var image = view.FindViewById<ImageView> (Resource.Id.Image).Drawable as BitmapDrawable) {
      if (image != null) {
        if (image.Bitmap != null) {
          //image.Bitmap.Recycle ();
          image.Bitmap.Dispose ();
        }
      }
    }
  }

  // If a new image is required, display it
  if (!String.IsNullOrWhiteSpace (item.ImageFilename)) {
    context.Resources.GetBitmapAsync (item.ImageFilename).ContinueWith ((t) => {
      var bitmap = t.Result;
      if (bitmap != null) {
        view.FindViewById<ImageView> (Resource.Id.Image).SetImageBitmap (bitmap);
        bitmap.Dispose ();
      }
    }, TaskScheduler.FromCurrentSynchronizationContext ());
  } else {
    // clear the image
    view.FindViewById<ImageView> (Resource.Id.Image).SetImageBitmap (null);
  }

  return view;
}

會呼叫 GetView 方法以傳回清單中每個資料列要轉譯的資料格作為 View。 會為將顯示於畫面上的每個資料列建立 View 執行個體,並在配置檔案中定義 View 執行個體的外觀。 當資料格因捲動而從畫面消失時,資料格將可重複使用。 這可藉由確保僅有資料的 View 執行個體顯示在畫面上 (而非清單中的所有資料) 來避免浪費記憶體。 如需檢視重複使用的詳細資訊,請參閱資料列檢視重複使用

GetView 方法也會將資料填入 View 執行個體,資料包括從 ImageFilename 屬性中指定的檔案名稱來讀取映像資料。

由原生 ListView 顯示之每個資料格的配置,都由 NativeAndroidListViewCell.axml 配置檔案定義,該檔案會由 LayoutInflater.Inflate 方法擴大。 下列程式碼範例示範配置定義:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:padding="8dp"
    android:background="@drawable/CustomSelector">
    <LinearLayout
        android:id="@+id/Text"
        android:orientation="vertical"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingLeft="10dip">
        <TextView
            android:id="@+id/Text1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="#FF7F3300"
            android:textSize="20dip"
            android:textStyle="italic" />
        <TextView
            android:id="@+id/Text2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="14dip"
            android:textColor="#FF267F00"
            android:paddingLeft="100dip" />
    </LinearLayout>
    <ImageView
        android:id="@+id/Image"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:padding="5dp"
        android:src="@drawable/icon"
        android:layout_alignParentRight="true" />
</RelativeLayout>

此配置指定顯示資料格內容所用的兩個 TextView 控制項和一個 ImageView 控制項。 兩個 TextView 控制項在 LinearLayout 控制項內是垂直方向,而且所有控制項都包含在 RelativeLayout 內。

回應自訂控制項上的屬性變更

如果 NativeListView.Items 屬性變更,由於正在新增項目至清單或從清單中移除項目,因此自訂轉譯器必須透過顯示變更來回應。 這可藉由覆寫 OnElementPropertyChanged 方法來完成,如下列程式碼範例所示:

protected override void OnElementPropertyChanged (object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
  base.OnElementPropertyChanged (sender, e);

  if (e.PropertyName == NativeListView.ItemsProperty.PropertyName) {
    Control.Adapter = new NativeAndroidListViewAdapter (_context as Android.App.Activity, Element as NativeListView);
  }
}

此方法會建立 NativeAndroidListViewAdapter 類別的新執行個體,該類別會提供資料給原生 ListView 控制項,前提是可繫結的 NativeListView.Items 屬性已變更。

在 UWP 上建立自訂轉譯器

下列程式碼範例示範適用於 UWP 的自訂轉譯器:

[assembly: ExportRenderer(typeof(NativeListView), typeof(NativeUWPListViewRenderer))]
namespace CustomRenderer.UWP
{
    public class NativeUWPListViewRenderer : ListViewRenderer
    {
        ListView listView;

        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.ListView> e)
        {
            base.OnElementChanged(e);

            listView = Control as ListView;

            if (e.OldElement != null)
            {
                // Unsubscribe
                listView.SelectionChanged -= OnSelectedItemChanged;
            }

            if (e.NewElement != null)
            {
                listView.SelectionMode = ListViewSelectionMode.Single;
                listView.IsItemClickEnabled = false;
                listView.ItemsSource = ((NativeListView)e.NewElement).Items;             
                listView.ItemTemplate = App.Current.Resources["ListViewItemTemplate"] as Windows.UI.Xaml.DataTemplate;
                // Subscribe
                listView.SelectionChanged += OnSelectedItemChanged;
            }  
        }

        void OnSelectedItemChanged(object sender, SelectionChangedEventArgs e)
        {
            ((NativeListView)Element).NotifyItemSelected(listView.SelectedItem);
        }
    }
}

如果自定義轉譯器附加至新Xamarin.Forms元素,則會設定原生ListView控件。 此組態包含設定原生 ListView 控制項將如何回應所選取的項目、填入由控制項顯示的資料、定義每個資料格的外觀,並註冊事件處理常式來處理 SelectionChanged 事件。 接著,此處理常式將會叫用由 NativeListView 自訂控制項所提供的 ItemSelected 事件。 如果轉譯器附加至變更的專案,Xamarin.Forms事件SelectionChanged就會取消訂閱。

每個原生 ListView 資料格的外觀和內容,由名為 ListViewItemTemplateDataTemplate 所定義。 DataTemplate 儲存在應用程式層級的資源字典中,如下列程式碼範例所示:

<DataTemplate x:Key="ListViewItemTemplate">
    <Grid Background="#DAFF7F">
        <Grid.Resources>
            <local:ConcatImageExtensionConverter x:Name="ConcatImageExtensionConverter" />
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="0.40*" />
            <ColumnDefinition Width="0.40*"/>
            <ColumnDefinition Width="0.20*" />
        </Grid.ColumnDefinitions>
        <TextBlock Grid.ColumnSpan="2" Foreground="#7F3300" FontStyle="Italic" FontSize="22" VerticalAlignment="Top" Text="{Binding Name}" />
        <TextBlock Grid.RowSpan="2" Grid.Column="1" Foreground="#267F00" FontWeight="Bold" FontSize="12" VerticalAlignment="Bottom" Text="{Binding Category}" />
        <Image Grid.RowSpan="2" Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Center" Source="{Binding ImageFilename, Converter={StaticResource ConcatImageExtensionConverter}}" Width="50" Height="50" />
        <Line Grid.Row="1" Grid.ColumnSpan="3" X1="0" X2="1" Margin="30,20,0,0" StrokeThickness="1" Stroke="LightGray" Stretch="Fill" VerticalAlignment="Bottom" />
    </Grid>
</DataTemplate>

DataTemplate 指定顯示資料格內容及其配置和外觀所用的控制項。 透過資料繫結使用兩個 TextBlock 控制項和一個 Image 控制項來顯示資料格的內容。 此外,使用 ConcatImageExtensionConverter 的執行個體將 .jpg 副檔名串連到每個影像檔案名稱。 這可確保 Image 控制項在設定 Source 屬性後,可以載入及轉譯影像。

回應自訂控制項上的屬性變更

如果 NativeListView.Items 屬性變更,由於正在新增項目至清單或從清單中移除項目,因此自訂轉譯器必須透過顯示變更來回應。 這可藉由覆寫 OnElementPropertyChanged 方法來完成,如下列程式碼範例所示:

protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
    base.OnElementPropertyChanged(sender, e);

    if (e.PropertyName == NativeListView.ItemsProperty.PropertyName)
    {
        listView.ItemsSource = ((NativeListView)Element).Items;
    }
}

方法會使用變更的資料重新填入原生 ListView 控制項,前提是可繫結的 NativeListView.Items 屬性已變更。

摘要

本文示範了如何建立自訂轉譯器,其會封裝平台特定清單控制項和原生資料格配置,讓您對原生清單控制效能擁有更多掌控權。