Přizpůsobení zobrazení ListView

Download Sample Stažení ukázky

ListView Xamarin.Forms je zobrazení, které zobrazuje kolekci dat jako svislý seznam. Tento článek ukazuje, jak vytvořit vlastní renderer, který zapouzdřuje ovládací prvky seznamu specifické pro platformu a nativní rozložení buněk, což umožňuje větší kontrolu nad výkonem řízení nativních seznamů.

Každé Xamarin.Forms zobrazení má doprovodný renderer pro každou platformu, která vytvoří instanci nativního ovládacího prvku. ListView Při vykreslení Xamarin.Forms aplikací se v iOSu ListViewRenderer vytvoří instance třídy, která následně vytvoří instanci nativního UITableView ovládacího prvku. Na platformě Android třída ListViewRenderer vytvoří instanci nativního ListView ovládacího prvku. V Univerzální platforma Windows (UPW) třída ListViewRenderer vytvoří instanci nativního ListView ovládacího prvku. Další informace o rendereru a nativních tříd ovládacích prvcích, které Xamarin.Forms řídí mapování, naleznete v tématu Renderer Základní třídy a nativní ovládací prvky.

Následující diagram znázorňuje vztah mezi ovládacím ListView prvku a odpovídajícími nativními ovládacími prvky, které ho implementují:

Relationship Between the ListView Control and the Implementing Native Controls

Proces vykreslování můžete využít k implementaci přizpůsobení specifických pro platformu vytvořením vlastního rendereru pro každou platformu ListView . Postup je následující:

  1. VytvořteXamarin.Forms vlastní ovládací prvek.
  2. Využití vlastního ovládacího prvku z Xamarin.Forms.
  3. Vytvořte vlastní renderer pro ovládací prvek na každé platformě.

Jednotlivé položky budou nyní popsány a implementují NativeListView renderer, který využívá ovládací prvky seznamu specifické pro platformu a nativní rozložení buněk. Tento scénář je užitečný při přenosu existující nativní aplikace, která obsahuje seznam a kód buňky, které je možné znovu použít. Kromě toho umožňuje podrobné přizpůsobení funkcí ovládacích prvků seznamu, které můžou ovlivnit výkon, jako je virtualizace dat.

Vytvoření vlastního ovládacího prvku ListView

Vlastní ListView ovládací prvek lze vytvořit podtřídou ListView třídy, jak je znázorněno v následujícím příkladu kódu:

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

Vytvoří se NativeListView v projektu knihovny .NET Standard a definuje rozhraní API pro vlastní ovládací prvek. Tento ovládací prvek zveřejňuje Items vlastnost, která se používá k naplnění ListView daty a která mohou být vázána na účely zobrazení. Také zveřejňuje ItemSelected událost, která se aktivuje při každém výběru položky v ovládacím prvku nativního seznamu specifického pro platformu. Další informace o datové vazbě najdete v tématu Základy datových vazeb.

Využívání vlastního ovládacího prvku

Na NativeListView vlastní ovládací prvek lze odkazovat v xaml v projektu knihovny .NET Standard deklarováním oboru názvů pro jeho umístění a použitím předpony oboru názvů v ovládacím prvku. Následující příklad kódu ukazuje, jak NativeListView může vlastní ovládací prvek využívat stránka XAML:

<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>

Předponu local oboru názvů lze pojmenovat cokoli. clr-namespace Hodnoty ale assembly musí odpovídat podrobnostem vlastního ovládacího prvku. Jakmile je obor názvů deklarován, předpona se použije k odkazování na vlastní ovládací prvek.

Následující příklad kódu ukazuje, jak NativeListView může vlastní ovládací prvek využívat stránka jazyka C#:

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

Vlastní NativeListView ovládací prvek používá vlastní vykreslovací moduly specifické pro platformu k zobrazení seznamu dat, která jsou naplněna vlastností Items . Každý řádek v seznamu obsahuje tři položky dat – název, kategorii a název souboru obrázku. Rozložení jednotlivých řádků v seznamu je definováno vlastním vykreslovacím modulem pro konkrétní platformu.

Poznámka:

Vzhledem k tomu, že NativeListView vlastní ovládací prvek bude vykreslen pomocí ovládacích prvků seznamu specifických pro platformu, které zahrnují možnost posouvání, by vlastní ovládací prvek neměl být hostován v posouvatelných ovládacích prvcích rozložení, jako je například ScrollView.

Do každého projektu aplikace je teď možné přidat vlastní renderer, který vytvoří ovládací prvky seznamu specifické pro platformu a nativní rozložení buněk.

Vytvoření vlastního rendereru na jednotlivých platformách

Proces vytvoření vlastní třídy rendereru je následující:

  1. Vytvořte podtřídu ListViewRenderer třídy, která vykreslí vlastní ovládací prvek.
  2. Přepište metodu OnElementChanged , která vykreslí vlastní ovládací prvek a zapisuje logiku pro přizpůsobení. Tato metoda se volá při vytvoření odpovídající Xamarin.FormsListView metody.
  3. Přidejte do vlastní třídy rendereru ExportRenderer atribut, který určí, že se použije k vykreslení vlastního Xamarin.Forms ovládacího prvku. Tento atribut se používá k registraci vlastního rendereru v Xamarin.Forms.

Poznámka:

V každém projektu platformy je volitelné zadat vlastní renderer. Pokud není zaregistrovaný vlastní renderer, použije se výchozí renderer základní třídy buňky.

Následující diagram znázorňuje zodpovědnosti jednotlivých projektů v ukázkové aplikaci spolu s relacemi mezi nimi:

NativeListView Custom Renderer Project Responsibilities

Vlastní NativeListView ovládací prvek se vykresluje pomocí tříd rendereru specifických pro platformu, které jsou odvozeny od třídy pro každou platformu ListViewRenderer . Výsledkem je vykreslení každého NativeListView vlastního ovládacího prvku pomocí ovládacích prvků seznamu specifických pro platformu a rozložení nativních buněk, jak je znázorněno na následujících snímcích obrazovky:

NativeListView on each Platform

Třída ListViewRenderer zveřejňuje metodu OnElementChanged , která je volána při Xamarin.Forms vytvoření vlastního ovládacího prvku pro vykreslení odpovídajícího nativního ovládacího prvku. Tato metoda přebírá ElementChangedEventArgs parametr, který obsahuje OldElement a NewElement vlastnosti. Tyto vlastnosti představují Xamarin.Forms prvek, ke kterému byl renderer připojen, a Xamarin.Forms prvek, ke kterému je renderer připojen, v uvedeném pořadí. V ukázkové aplikaci OldElement bude vlastnost a NewElement vlastnost bude obsahovat odkaz na NativeListViewnull instanci.

Přepsáná verze OnElementChanged metody v každé třídě rendereru specifické pro platformu je místo pro provedení přizpůsobení nativního ovládacího prvku. Typový odkaz na nativní ovládací prvek, který se používá na platformě, je přístupný prostřednictvím Control vlastnosti. Kromě toho lze prostřednictvím vlastnosti získat Element odkaz na Xamarin.Forms vykreslovaný ovládací prvek.

Při přihlášení k odběru obslužných rutin událostí v OnElementChanged metodě je potřeba věnovat pozornost, jak je znázorněno v následujícím příkladu kódu:

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

Nativní ovládací prvek by měl být nakonfigurovaný a obslužné rutiny událostí odebírané pouze při připojení vlastního rendereru k novému Xamarin.Forms prvku. Podobně by se měly všechny obslužné rutiny událostí, které byly přihlášeny k odběru, odhlásit pouze v případě, že je prvek, který renderer připojí ke změnám. Přijetí tohoto přístupu vám pomůže vytvořit vlastní renderer, který netrpí nevracením paměti.

Přepsáná verze OnElementPropertyChanged metody, v každé třídě rendereru specifické pro platformu, je místo, kde reagovat na změny vazby vlastností ve vlastním ovládacím Xamarin.Forms prvku. Kontrola změněné vlastnosti by měla být vždy provedena, protože toto přepsání lze volat mnohokrát.

Každá vlastní třída rendereru je zdobena ExportRenderer atributem, který registruje renderer s Xamarin.Forms. Atribut má dva parametry – název typu vykreslovaného vlastního Xamarin.Forms ovládacího prvku a název typu vlastního rendereru. Předpona assembly atributu určuje, že atribut se vztahuje na celé sestavení.

Následující části se týkají implementace jednotlivých vlastních tříd rendereru specifických pro platformu.

Vytvoření vlastního rendereru v iOSu

Následující příklad kódu ukazuje vlastní renderer pro platformu 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);
            }
        }
    }
}

Ovládací UITableView prvek je nakonfigurován vytvořením instance NativeiOSListViewSource třídy za předpokladu, že vlastní renderer je připojen k novému Xamarin.Forms prvku. Tato třída poskytuje data ovládacímu UITableView prvku přepsáním RowsInSection a GetCell metodami z UITableViewSource třídy a zveřejněním Items vlastnosti, která obsahuje seznam dat, která se mají zobrazit. Třída také poskytuje přepsání RowSelected metody, která vyvolá ItemSelected událost poskytovanou vlastním ovládacím NativeListView prvku. Další informace o přepsání metody naleznete v části Podtřídy UITableViewSource. Metoda GetCell vrátí UITableCellView data naplněná daty pro každý řádek v seznamu a je zobrazena v následujícím příkladu kódu:

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

Tato metoda vytvoří NativeiOSListViewCell instanci pro každý řádek dat, který se zobrazí na obrazovce. Instance NativeiOSCell definuje rozložení jednotlivých buněk a dat buňky. Když buňka zmizí z obrazovky kvůli posouvání, bude buňka k dispozici pro opakované použití. Tím se zabrání plýtvání paměti tím, že zajistíte, aby se na obrazovce zobrazovaly pouze NativeiOSCell instance dat, a ne všechna data v seznamu. Další informace o opětovném použití buňky naleznete v tématu Opětovné použití buňky. Metoda GetCell také přečte ImageFilename vlastnost každého řádku dat, za předpokladu, že existuje, a přečte image a uloží ji jako UIImage instanci před aktualizací NativeiOSListViewCell instance s daty (název, kategorie a obrázek) řádku.

Třída NativeiOSListViewCell definuje rozložení pro každou buňku a je znázorněna v následujícím příkladu kódu:

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

Tato třída definuje ovládací prvky použité k vykreslení obsahu buňky a jejich rozložení. Konstruktor NativeiOSListViewCell vytvoří instance UILabel a UIImageView ovládací prvky a inicializuje jejich vzhled. Tyto ovládací prvky slouží k zobrazení dat jednotlivých řádků s metodou použitou UpdateCell k nastavení těchto dat v UILabel instancích a UIImageView instancích. Umístění těchto instancí je nastaveno přepsánou LayoutSubviews metodou zadáním jejich souřadnic v buňce.

Reakce na změnu vlastnosti vlastního ovládacího prvku

Pokud se NativeListView.Items vlastnost změní z důvodu přidání nebo odebrání položek ze seznamu, vlastní renderer musí reagovat zobrazením změn. Toho lze dosáhnout přepsáním OnElementPropertyChanged metody, která je znázorněna v následujícím příkladu kódu:

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

Metoda vytvoří novou instanci NativeiOSListViewSource třídy, která poskytuje data ovládacímu UITableView prvku za předpokladu, že se změnila vlastnost bindable NativeListView.Items .

Vytvoření vlastního rendereru v Androidu

Následující příklad kódu ukazuje vlastní renderer pro platformu 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]);
        }
    }
}

ListView Nativní ovládací prvek je nakonfigurován za předpokladu, že vlastní renderer je připojen k novému Xamarin.Forms prvku. Tato konfigurace zahrnuje vytvoření instance NativeAndroidListViewAdapter třídy, která poskytuje data nativnímu ListView ovládacímu prvku, a registraci obslužné rutiny události ke zpracování ItemClick události. Tato obslužná rutina pak vyvolá ItemSelected událost poskytovanou vlastním ovládacím NativeListView prvku. Událost ItemClick se odhlásí, pokud Xamarin.Forms je prvek, který renderer připojí ke změnám.

Odvozuje NativeAndroidListViewAdapter z BaseAdapter třídy a zveřejňuje Items vlastnost, která obsahuje seznam dat, která mají být zobrazena, a také přepsání Count, , GetViewGetItemIda this[int] metody. Další informace o těchto přepsání metody naleznete v tématu Implementace ListAdapter. Metoda GetView vrátí zobrazení pro každý řádek naplněný daty a je znázorněn v následujícím příkladu kódu:

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

Metoda GetView je volána k vrácení buňky, která se má vykreslit, jako a Viewpro každý řádek dat v seznamu. Vytvoří View instanci pro každý řádek dat, která se zobrazí na obrazovce, s vzhledem View instance, která je definována v souboru rozložení. Když buňka zmizí z obrazovky kvůli posouvání, bude buňka k dispozici pro opakované použití. Tím se zabrání plýtvání paměti tím, že zajistíte, aby se na obrazovce zobrazovaly pouze View instance dat, a ne všechna data v seznamu. Další informace o opětovném použití zobrazení najdete v tématu Opětovné použití zobrazení řádků.

Metoda GetView také naplní View instanci daty, včetně čtení dat obrázku z názvu souboru zadaného ImageFilename ve vlastnosti.

Rozložení každé buňky dispayed podle nativní ListView je definováno v NativeAndroidListViewCell.axml souboru rozložení, který je nafoukl metodou LayoutInflater.Inflate . Následující příklad kódu ukazuje definici rozložení:

<?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>

Toto rozložení určuje, že se k zobrazení obsahu buňky používají dva TextView ovládací prvky a ImageView ovládací prvek. Tyto dva TextView ovládací prvky jsou svisle orientované v LinearLayout rámci ovládacího prvku, přičemž všechny ovládací prvky obsažené v objektu RelativeLayout.

Reakce na změnu vlastnosti vlastního ovládacího prvku

Pokud se NativeListView.Items vlastnost změní z důvodu přidání nebo odebrání položek ze seznamu, vlastní renderer musí reagovat zobrazením změn. Toho lze dosáhnout přepsáním OnElementPropertyChanged metody, která je znázorněna v následujícím příkladu kódu:

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

Metoda vytvoří novou instanci NativeAndroidListViewAdapter třídy, která poskytuje data nativnímu ListView ovládacímu prvku za předpokladu, že se změnila vlastnost bindable NativeListView.Items .

Vytvoření vlastního rendereru v UPW

Následující příklad kódu ukazuje vlastní renderer pro UPW:

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

ListView Nativní ovládací prvek je nakonfigurován za předpokladu, že vlastní renderer je připojen k novému Xamarin.Forms prvku. Tato konfigurace zahrnuje nastavení, jakým způsobem bude nativní ListView ovládací prvek reagovat na vybrané položky, naplnění dat zobrazených ovládacím prvku, definování vzhledu a obsahu každé buňky a registraci obslužné rutiny události ke zpracování SelectionChanged události. Tato obslužná rutina pak vyvolá ItemSelected událost poskytovanou vlastním ovládacím NativeListView prvku. Událost SelectionChanged se odhlásí, pokud Xamarin.Forms je prvek, který renderer připojí ke změnám.

Vzhled a obsah každé nativní ListView buňky jsou definovány DataTemplate názvem ListViewItemTemplate. Toto DataTemplate je uloženo ve slovníku prostředků na úrovni aplikace a je znázorněno v následujícím příkladu kódu:

<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>

Určuje DataTemplate ovládací prvky použité k zobrazení obsahu buňky a jejich rozložení a vzhledu. Dva TextBlock ovládací prvky a Image ovládací prvek slouží k zobrazení obsahu buňky prostřednictvím datové vazby. Kromě toho se instance souboru ConcatImageExtensionConverter používá ke zřetězení přípony .jpg souboru pro každý název souboru obrázku. Tím se zajistí, že Image ovládací prvek může načíst a vykreslit obrázek, když je nastavena Source jeho vlastnost.

Reakce na změnu vlastnosti vlastního ovládacího prvku

Pokud se NativeListView.Items vlastnost změní z důvodu přidání nebo odebrání položek ze seznamu, vlastní renderer musí reagovat zobrazením změn. Toho lze dosáhnout přepsáním OnElementPropertyChanged metody, která je znázorněna v následujícím příkladu kódu:

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

Metoda znovu naplní nativní ListView ovládací prvek změněnými daty za předpokladu, že se změnila vlastnost bindable NativeListView.Items .

Shrnutí

Tento článek ukázal, jak vytvořit vlastní renderer, který zapouzdřuje ovládací prvky seznamu specifické pro platformu a nativní rozložení buněk, což umožňuje větší kontrolu nad výkonem řízení nativních seznamů.