CollectionView bileşenini kullanırken öğelerin değerlerine göre görünümlerini özelleştirmek çoğu durumda kolaydır. Gerekirse öğeler için de bir view model oluşturularak karmaşıklığın da biraz önüne geçilebilir. Ancak bazen öyle karmaşık görevler olur ki Xamarin.Forms’taki DataTemplateSelector sınıflarını kullanmak en doğrusudur. Bu çok daha iyi ve anlaşılır kodlar yazmanızı sağlar.

Örneğin bir mesajlaşma uygulaması yazdığımızı düşününelim. Bir görüşme sırasında kullanıcıların birbirlerine görüntü, ses ve yazı içeren mesajlar gönderebilmesini istiyoruz. Şimdi bu görüşmedeki mesajları göstermek için kullandığımız CollectionView bileşeninde üç farklı tür mesajı zaman sırasıyla listelemek zorundayız. Çünkü bu mesajlar birbirlerinden çok farklı yapıda olmalarına rağmen (ses, yazı, görüntü) aynı görüşmenin parçaları.

Sesli mesajda, bir oynat düğmesi, işlem çubuğu ve bir mikrofon ikonu olsun. Yazılı mesajda ise sadece mesajı içeren bir etiket bileşeni olması yeterlidir. Görüntülü mesajda ise örneğin birkaç tane imageview bileşeni ve varsa daha kaç görüntü olduğunu belirten bir etiket bileşeni olması güzel olur. Aşağıdaki ekran görüntüsüne bakınız. Bu görüntü CodeCanyon’da satılan ve DataTemplateSelector kullandığım “Messaging App UI Template for Xamarin Forms”a aittir.

Şimdi bu görünümlerin hepsini bir şablon görünümü ile gerçekleştirmeye çalışmanın nasıl karmaşık bir hal alabileceğini hayal edebilirsiniz. Burada izlenmesi gereken yol, her mesaj tipini ayrı ayrı ele alan üç şablon görünüm oluşturmak ve mesajın tipine göre ilgili şablonu seçen bir DataTemplateSelector sınıfı yazmaktır.

Şimdi buraya kadar anlattıklarımızı gerçekleştiren bir örnek yapalım. Örneğimizi basit tutacağız. Sesli mesajlar için sadece bir oynat düğmesi, görüntülü mesajlar için bir tane imageview, yazılı mesajlar için de bir tane etiket bileşeni gösterelim. Model sınıflarımızla başlayalım. Tüm mesaj sınıflarını türeteceğimiz bir üst sınıf yazalım. Her mesajın bir gönderim zamanı olacağı için bu alanı üst sınıfımıza ekleyelim.

Models/Message.cs
using System;

namespace TemplateSelectorSample.Models
{
    public abstract class Message
    {
        public DateTime DateUtc { get; set; }
    }
}

Şimdi yazılı mesajları temsil edecek bir sınıf oluşturalım. Message sınıfından türettiğimiz bu sınıfa, yazı için string bir alan eklememiz yeterlidir.

Models/TextMessage.cs
namespace TemplateSelectorSample.Models
{
    public class TextMessage: Message
    {
        public string Text { get; set; }
    }
}

Görüntülü mesajları temsil edecek sınıfımız aşağıdadır. Görüntünün referansını (kaynak adını) tutacak bir string alan ekliyoruz.

Models/ImageMessage.cs
namespace TemplateSelectorSample.Models
{
    public class ImageMessage: Message
    {
        public string Image { get; set; }
    }
}

Son olarak sesli mesajları temsil edecek bir model sınıfı ekliyoruz. ve modellerimiz bitiyor. Bu basit örnekte herhangi bir ses dosyası çalmayı planlamadığımız için bu sınıfa bir alan eklemeye ihtiyacımız yok. Yine de şablon seçicimizin ses mesajlarını ayırt edebilmesi için ayrı bir sınıf tarafından temsil edilmesine ihtiyacımız var.

Models/VoiceMessage.cs
namespace TemplateSelectorSample.Models
{
    public class VoiceMessage: Message
    {
    }
}

Görünümleri oluşturalım. Yazılı mesaj ile başlayalım. Bu görünümde mesajı göstermek için bir etiket bileşeni ve ayraç olarak kullandığımız bir ContentView bileşeni var.

TileViews/TextMessageTile.xaml
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="TemplateSelectorSample.TileViews.TextMessageTile">
  <ContentView.Content>
      <StackLayout>
          <Label Text="{Binding Text}" />
           <ContentView HeightRequest="1" BackgroundColor="Gray"/>
        </StackLayout>
  </ContentView.Content>
</ContentView>

Görüntülü mesaj için oluşturduğumuz görünüm etiket yerine bir image bileşeni içermektedir.

TileViews/ImageMessageTile.xaml
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="TemplateSelectorSample.TileViews.ImageMessageTile">
  <ContentView.Content>
      <StackLayout>
            <Image Source="{Binding Image}"
                 HeightRequest="150"
                 WidthRequest="150"/>
          
            <ContentView HeightRequest="1" BackgroundColor="Gray"/>
        </StackLayout>
  </ContentView.Content>
</ContentView>

Sesli mesajlar için oluşturduğumuz görünüm aşağıdadır. Mesajı dinlemek için basabileceğiniz bir oynat düğmesi ve yine bir ayraç vardır.

TileViews/VoiceMessageTile.xaml
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="TemplateSelectorSample.TileViews.VoiceMessageTile">
  <ContentView.Content>
      <StackLayout>
            <Button Text="Play"/>
            <ContentView HeightRequest="1" BackgroundColor="Gray"/>
        </StackLayout>
  </ContentView.Content>
</ContentView>

Şimdi Şablon seçici sınıfımızı hazırlayalım. Bu sınıf mesajın tipine bakarak CollectionView bileşenine hangi öğenin hangi görünümü kullanması gerektiğini bildirecek. Şablon seçici sınıflar, DataTemplateSelector sınıfından türetilmeli ve OnSelectTemplate metodunu override etmelidir. CollectionView bileşeni öğeleri listelerken bu metodu her bir öğe için çağırır. İlgili öğe metoda parametre olarak geçirilir. Metot ta öğenin türüne veya değerine göre hangi DataTemplate’i döndüreceğine karar verir. CollectionView öğeyi gösterirken bu metottan dönen şablonu kullanır. Şablon seçici sınıfımıza, döndürebileceği şablonların atanabilmesi için DataTemplate türünde birer özellik eklenir.

Selectors/MessageTemplateSelector.cs
using TemplateSelectorSample.Models;
using Xamarin.Forms;

namespace TemplateSelectorSample.Selectors
{
    public class MessageTemplateSelector: DataTemplateSelector
    {
        public DataTemplate TextMessageTemplate { get; set; }

        public DataTemplate VoiceMessageTemplate { get; set; }

        public DataTemplate ImageMessageTemplate { get; set; }

        protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
        {
            if (item is TextMessage)
                return TextMessageTemplate;
            else if (item is VoiceMessage)
                return VoiceMessageTemplate;
            else 
                return ImageMessageTemplate;
        }
    }
}

Artık şablon seçici sınıfımızı kullanabiliriz. Önce mesajlar için hazırladığımız görünümleri kaynak olarak ekliyoruz. Sonra şablon seçicimizi de kaynak olarak eklerken, görünümlere verdiğimiz kaynak adlarını seçicimizin ilgili parametrelerine atıyoruz. MainPage.xaml dosyasının ilgili kısmı aşağıdadır.

MainPage.xaml
...
    <ContentPage.Resources>

        <DataTemplate x:Key="TextMessageTemplate">
            <tile:TextMessageTile/>
        </DataTemplate>

        <DataTemplate x:Key="VoiceMessageTemplate">
            <tile:VoiceMessageTile/>
        </DataTemplate>

        <DataTemplate x:Key="ImageMessageTemplate">
            <tile:ImageMessageTile/>
        </DataTemplate>

        <selector:MessageTemplateSelector x:Key="MessageTemplateSelector"
                TextMessageTemplate="{StaticResource TextMessageTemplate}"
                VoiceMessageTemplate="{StaticResource VoiceMessageTemplate}"
                ImageMessageTemplate="{StaticResource ImageMessageTemplate}"/>

    </ContentPage.Resources>
...

Şimdi CollevtionView bileşenin ItemTemplate özelliğine kaynak olarak eklediğimiz seçicimizi atayabiliriz. Gördüğünüz gibi yazdığımız seçici sınıfımızı sanki bir DataTemplate gibi CollectionView bileşeninin, ItemTemplate özelliğine atayabiliyoruz. Yani bir DataTemplateSelector aynı zamanda bir DataTemplate’dir ve CollectionView bileşeninin ItemTemplate özelliğine atanır.

MainPage.xaml
...
<CollectionView ItemsSource="{Binding Messages}"
        ItemTemplate="{StaticResource MessageTemplateSelector}"    
        Margin="16">
...

Sayfamızın ViewModel’ini hazırlayabiliriz. Messages kolleksiyonuna test için her mesaj türünden bir tane girelim.

ViewModels/MainViewModel.cs
using System;
using System.Collections.ObjectModel;
using TemplateSelectorSample.Models;

namespace TemplateSelectorSample.ViewModels
{
    public class MainViewModel: BaseViewModel
    {
        public ObservableCollection<Message> Messages { get; }

        public MainViewModel()
        {
            Messages = new ObservableCollection<Message>()
            {
                new TextMessage
                {
                    DateUtc = new DateTime(2023,1,1,9,1,0),
                    Text = "Sed vel vehicula nibh, eget iaculis sapien."
                },
                new ImageMessage
                {
                    DateUtc = new DateTime(2023,1,1,9,2,0),
                    Image = "img01"
                },
                new VoiceMessage
                {
                    DateUtc = new DateTime(2023,1,1,9,3,0)
                }
            };

        }
    }
}

Nihayet projeyi çalıştırarak test edebiliriz. Messages kolleksiyonundaki her bir öğe ilişkili olduğu şablon görünümü kullanılarak gösterilecektir. Aşağıdaki ekran görüntüsüne bakınız.

Bu projenin kaynak kodlarını aşağıdaki bağlantıdan indirebilirsiniz.

https://zeymur.com/samples/xforms/TemplateSelectorSample.zip

Bir sonraki gönderimde bağlı veri çeviricilerini (Binding Value Converter) işlemeyi planlıyorum.

By zeymur

Leave a Reply