Silverlight 2.0 içerisinde AutoCompleteBox kullanımı.

0 dakikada yazıldı

5911 defa okundu

Düzenle

AutoComplete işlevselliği AJAX günlerinden alışık olduğumuz bir sistem.
Herhangi bir TextBox'a kullanıcı yazı yazarken aynı anda uygun
alternatifleri göstermek ve aslında arka planda bir arama sistemi kurmak
gibi işlemleri uzun zamandır farklı arayüz araçları kullansak da bir
şekilde programcılar olarak hazırlayabiliyoruz. Silverlight tarafında
ise Silverlight'ın görsel gücünden de faydalanarak çok ilginç çözümler
üretmek mümkün. Silverlight dünyasında AutoComplete altyapılarını
incelerken Silverlight Toolkit içerisindeki AutoCompleteBox
kontrolünü kullanacağız.

Not: Silverlight Toolkit'i kullanabilmeniz için
CodePlex
üzerindeki adresten kütüphaneyi indirerek içerisindeki
Microsoft.Windows.Controls.dll dosyasını projenize referans
olarak eklemelisiniz.

En hızlı şekilde AutoCompleteBox kullanımı..

AutoCompleteBox'ın kullanımı aslında çok basit. Hemen bir String
Array veya List yaratarak AutoCompleteBox'a bind etmeniz yeterli
olacaktır. Tüm filtreleme ve AutoComplete işlemleri otomatik olarak
yapılacaktır.

[VB]

    Private Sub Page_Loaded(ByVal sender As Object,
ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded

        Dim Liste As New
List(Of String)

        Liste.Add("ASP.NET")

        Liste.Add("AJAX")

        Liste.Add("Silverlight")

        Liste.Add("WPF")

        AutoComplete1.ItemsSource = Liste

    End Sub

[C#]

        void Page_Loaded(object sender, RoutedEventArgs e)

        {

                List<String> Liste = new List<string>();

                Liste.Add("ASP.NET");

                Liste.Add("AJAX");

                Liste.Add("Silverlight");

                Liste.Add("WPF");

                AutoComplete1.ItemsSource = Liste;

        }

Yukarıdaki kod ile hazırladığımız basit bir uygulamanın aşağıda da XAML
kodunu inceleyebilirsiniz.

[XAML]

<UserControl
x
:Class="SilverlightApplication3.Page"

  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  Width="400"
Height
="300" xmlns:controls="clr-namespace:Microsoft.Windows.Controls;assembly=Microsoft.Windows.Controls">

    <Grid
x
:Name="LayoutRoot"
Background
="White">

      <controls:AutoCompleteBox Height="24"
Margin
="24,24,144,0" VerticalAlignment="Top"
x
:Name="AutoComplete1"/>

    </Grid>

</UserControl>

Basit bir AutoComplete örneği.
Basit bir AutoComplete örneği.

AutoCompleteBox'ın özellikleri.

IsTextCompletionEnabled - Bu özellik açık olduğunda kullanıcı yazı
yazdıkça sadece alternatifler gösterilmez, ek olarak en yakın alternatif
sanki yazılmış gibi TextBox içerisinde de gösterilir. Varsayılan değeri
True şeklindedir.

SelectedItem - Eğer kullanıcı AutoCompleteBox'ın açılan ListBox
kısmından bir öğe seçerse SelectedItemChanged çalışır ve SelectedItem
geriye bir Item döndürür. Kullanıcı ListBox içerisinde yer alan bir
Item'ın metnini elle yazmışsa SelectedItem geriye değer
döndürmeyecektir.

SearchMode - AutoComplete işlemi yapılırken kaynak veride ne şekilde
arama yapılacağına karar veren SearchMode özelliği varsayılan değeri
olan StartsWith ile gelir. İsterseniz Contains seçeneğini
seçerek doğrudan kaynak verinin içindeki tüm metinlerin içinde arama
yapılmasını da sağlayabilirsiniz. Eğer kendi arama sisteminizi entegre
edecekseniz None seçeneğini seçmeniz gerekecektir.

MinimumPopulateDelay - Kullanıcı yazı yazarken ne kadar süre sonra
alternatiflerin gösterileceğini belirler. Eğer veri kaynağı istemci
tarafında bir Silverlight değişkeni ise varsayılan değer olan 0 ile
herhangi bir sorun yaşamazsınız. Fakat alternatifleri sunucudan her
seferinde çekiyorsanız buradaki bekleme süresini uzatmakta büyük fayda
olacaktır.

MinimumPrefixLength - Alternatifler gösterilmeden önce kaç
karakterlik verinin girilmiş olması gerektiğine dair ayar bu özellik
üzerinden yapılabilir.

Kendi filtreleme mekanizmamızı yazalım.

Bir AutoCompleteBox'ı aslında iki şekilde veri bağlamış olabiliriz.
Bunlardan birincisi yazımızın başındaki gibi basit bir metin dizisini
AutoCompleteBox'a aktarmak ikincisi ise kendi tanımladığımız nesnelerin
bir dizisini bağlamak. Gelin her iki durumda da filtreleme işlemlerini
nasıl özelleştirebileceğimize bakalım.

Bir önceki örneğimizin üzerinden yola devam edersek zaten hali hazırda
bir String listesini alıp AutoCompleteBox'ımıza bağlamıştık.
AutoCompleteBox'ın TextFilter özelliğini değiştirirek filtreleme
işleminin tam olarak nasıl yapılacağına karar verebiliriz. Bizim
yapacağımız örnekte kullanıcının yazdığı metin ile başlayan değil de
biten öğeleri göstermeye çalışacağız.

[VB]

    Function Filtreleme(ByVal Search As String,
ByVal item As String)

        If
item.ToString.EndsWith(Search) Then

            Return True

        Else

            Return False

        End If

    End Function

[C#]

        public bool Filtreleme(string Search, string item)

        {

            if
(item.ToString().EndsWith(Search)) {

                return true;

            }

            else {

                return false;

            }

        }

Yukarıdaki fonksiyonumuzu filtreleme işlemlerini yapmak için
kullanacağız. Gelen iki parametreden ilki kullanıcının TextBox içerisine
yazdığı metin, ikincisi ise o an fonksiyonumuza aktarılan ana kaynak
veriden gelen bir Item. Burada kafalar biraz karışabilir o nedenle biraz
daha detaya inelim. AutoCompleteBox filtreleme işlemini yaparken
elindeki verinin içindeki her bir öğeyi tek tek bizim filtreleme
fonksiyonumuza verecek ve söz konusu öğenin gösterilip
gösterilmeyeceğine dair bir cevap bekleyecek. Yani eğer veri kaynağında
10 adet öğe varsa bu fonksiyon 10 defa çağrılacak.

[VB]

    Private Sub Page_Loaded(ByVal sender As Object,
ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded

        Dim Liste As New
List(Of String)

        Liste.Add("ASP.NET")

        Liste.Add("AJAX")

        Liste.Add("Silverlight")

        Liste.Add("WPF")

 

**        AutoComplete1.TextFilter =** New
Microsoft.Windows.Controls.AutoCompleteSearchPredicate(Of
String
)(AddressOf
Filtreleme)

        AutoComplete1.ItemsSource = Liste

    End Sub

[C#]

        void Page_Loaded(object sender, RoutedEventArgs e)

        {

                List<String> Liste = new List<string>();

                Liste.Add("ASP.NET");

                Liste.Add("AJAX");

                Liste.Add("Silverlight");

                Liste.Add("WPF");

 

**                AutoComplete1.TextFilter =** new Microsoft.Windows.Controls.AutoCompleteSearchPredicate<string>(Filtreleme);

                AutoComplete1.ItemsSource = Liste;

        }

Hazırladığımız filtreleme fonksiyonunu tabi ki AutoCompleteBox'ın
TextFilter'ına da aktarmamız gerek. Yukarıdaki kodlardaki kalın
satırlarda söz konusu aktarma işleminin nasıl yapıldığını
inceleyebilirsiniz. TextFilter bizden bir
AutoCompleteSearchPredicate istiyor, biz de kendisine istediğini
veriyoruz :)

Peki ya AutoCompleteBox'a kendi yarattığımız nesne türlerinden bir liste
bağlamış olsaydık bu filtreleme işlemini nasıl yapacaktık. Böyle bir
durumda TextFilter yerine ItemFilter'ı kullanmak zorunda
kalacaktık. Gelin ilk olarak örneğimizde ilerleyebilmek için Kitap
adında kendi sınıfımızı tanımlayalım.

[VB]

    Public Class Kitap

 

        Private PAdi As String

        Public Property Adi() As String

            Get

                Return PAdi

            End Get

            Set(ByVal value As String)

                PAdi = value

            End Set

        End Property

 

        Private PFiyat As Double

        Public Property Fiyat() As Double

            Get

                Return PFiyat

            End Get

            Set(ByVal value As Double)

                PFiyat = value

            End Set

        End Property

 

    End Class

[C#]

        public class Kitap

        {

            public string Adi { get; set;
}

            public double Fiyat { get; set;
}

        }

Şimdi bu sınıflar üzerinden yola çıkarak Silverlight tarafında birkaç
kitap yaratıp AutoCompleteBox'larımıza DataBind edeceğiz.

[VB]

    Private Sub Page_Loaded(ByVal sender As Object,
ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded

        Dim Liste As New
List(Of Kitap)

        Liste.Add(New Kitap With {.Adi = "ASP.NET AJAX", .Fiyat = 25})

        Liste.Add(New Kitap With {.Adi = "ADO.NET", .Fiyat = 35})

        Liste.Add(New Kitap With {.Adi = "WPF", .Fiyat = 15})

        Liste.Add(New Kitap With {.Adi = "Silverlight", .Fiyat = 25})

 

        AutoComplete1.ItemFilter = New
Microsoft.Windows.Controls.AutoCompleteSearchPredicate(Of
Object
)(AddressOf
Filtreleme)

 

        AutoComplete1.ItemsSource = Liste

    End Sub

[C#]

        void Page_Loaded(object sender, RoutedEventArgs e)

        {

 

                  List<Kitap> Liste = new List<Kitap>();

        Liste.Add(new Kitap  {Adi = "ASP.NET AJAX", Fiyat = 25});

        Liste.Add(new Kitap  {Adi = "ADO.NET", Fiyat = 35});

        Liste.Add(new Kitap  {Adi = "WPF", Fiyat = 15});

        Liste.Add(new Kitap  {Adi = "Silverlight", Fiyat = 25});

 

        AutoComplete1.ItemFilter = new
Microsoft.Windows.Controls.AutoCompleteSearchPredicate<Object>(Filtreleme);

 

        AutoComplete1.ItemsSource = Liste;

        }

Kodumuz içerisinde çok büyük değişiklikler yok. Bu sefer bir String
listesi yerine Kitap listesi yaratıyor ve AutoCompleteBox'ın
ItemsSource'una eşitliyoruz. Filtreleme işlemi için yine
Filtreleme adında bir metod kullanacağımız için ItemFilter
özeliğine de söz konusu metodu bağlıyoruz. Aradaki en önemli fark
elimizdeki Predicate'in artık Object türünü taşıyor olması.

[VB]

    Function Filtreleme(ByVal Search As String,
ByVal item As
Object
)

        If CType(item, Kitap).Fiyat < CInt(Search) Then

            Return True

        Else

            Return False

        End If

    End Function

[C#]

        public bool Filtreleme(string Search, object item)

        {

            if (((Kitap)item).Fiyat < int.Parse(Search)) {

                return true;

            }

            else {

                return false;

            }

        }

Filtreleme metodumuzun ikinci parametresi artık Object tipinde. Biz
aslında buradaki değişkenin bir Kitap nesnesi olduğunu biliyoruz
çünkü AutoCompleteBox tek tek kendisine verilen öğeleri bu
filtreleme fonksiyonuna iletecektir. Bu nedenle elimizde gelen objeyi
Kitap tipine cast edip bu sefer de kitapların fiyatları üzerinden
filtreleme yapıyoruz. Kullanıcıların TextBox içerisinde sayısal bir
fiyat gireceğini ve bu fiyatın altındaki kitapların listeleneceğini
varsayalım.

Bu şekilde uygulamamızı çalıştırırsak ufak bir karışıklık olacaktır.
AutoCompleteBox'a bağladığımız veri Kitap'lardan oluşuyor. Peki
AutoCompleteBox bu Kitap nesnelerinin hangi özelliğini ekrana nasıl
getirecek? Örneğin kitabın adını mı yoksa fiyatını mı gösterecek
AutoComplete esnasında? İşte bu noktada da bizim XAML tarafına geçip
birkaç işlem yapmamız gerek.

[XAML]

<UserControl
x
:Class="SilverlightApplication3.Page"

  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  Width="400"
Height
="300" xmlns:controls="clr-namespace:Microsoft.Windows.Controls;assembly=Microsoft.Windows.Controls">

  <UserControl.Resources>

    <DataTemplate x:Key="DataTemplate1">

      <Grid>

        <TextBlock
HorizontalAlignment
="Left" VerticalAlignment="Top"
Text
="{Binding
Path
=Fiyat}" TextWrapping="Wrap"/>

      </Grid>

    </DataTemplate>

  </UserControl.Resources>

    <Grid
x
:Name="LayoutRoot"
Background
="White">

      <controls:AutoCompleteBox
Height
="24" Margin="24,24,144,0"
VerticalAlignment
="Top" x:Name="AutoComplete1"
ItemTemplate
="{StaticResource DataTemplate1}"/>

    </Grid>

</UserControl>

Artık bizim AutoCompleteBox'ımızın ItemTemplate'ini değiştirmemiz ve
özelleştirmemiz gerekiyor. Böylece tam olarak gelen veriden hangi
öğelerin nerelerde nasıl gösterileceğine karar verebiliriz. Yukarıdaki
kod içerisinde AutoCompleteBox'ın ItemTemplate özelliğinin
değiştirildiğini görebilirsiniz. DataTemplate1 adında yarattığımız
DataTemplate'i UserControl.Resources içerisine koyup
kullanabiliyoruz. DataTemplate içerisinde ise bir Grid ve TextBlock
bulunuyor. Söz konusu TextBlock doğrudan Fiyat adında bir
Field'e DataBind edilmiş durumda. Hatırlarsanız bizim Kitap
sınıfımızın Fiyat adında bir Property'si vardı. Böylece
AutoCompleteBox'a kendisine verilen verilerden her birinin Fiyat
özelliğinin DataTemplate içerisindeki bu TextBlock'un Text'ine
bağlanmasını sağladık. Tüm bu işlemleri Visual Studio içerisinde XAML
yazarak yapabileceğiniz gibi isterseniz Expression Blend'in arayüzünden
de tabi ki daha rahatlıkla yapabilirsiniz. Tek yapmanız gereken
AutoCompleteBox'a sağ tuş tıklayıp gelen menüden "Edit Other Templates /
Edit ItemTemplate" demek. Böylece tasarım modunda Blend içerisinde de
DataTemplate'in görselliğini değiştirebilirsiniz.

Artık filtreleme işlemlerini bitirdik, filtreleme esnasından Kitapların
sadece fiyatlarının gözükmesini de sağladık. Fakat neden sadece
fiyatları gözüksün ki? Kullanıcı TextBox içerisinde 25 yazdığınzda
25YTL'den ucuz kitapların hem adı hem fiyatı yazsa? Ve bunların
arasından bir kitap seçilince fiyatı otomatik olarak TextBlock içerisine
yerleşse daha güzel olmaz mı? AutoCompleteBox'ın açılan kısmının
görselliğini ne de olsa yukarıda değiştirmiştik, gelin şimdi ikinci bir
TextBlock ekleyerek onu da Kitap nesnelerinin Adi özelliğine
bağlayalım.

[XAML]

    <DataTemplate
x
:Key="DataTemplate1">

      <StackPanel
HorizontalAlignment
="Stretch"
VerticalAlignment
="Stretch"
Background
="{x:Null}"
Orientation
="Horizontal">

        <TextBlock
Text
="{Binding
Path
=Adi}" Foreground="#FFFF0000"
Height
="Auto" Width="Auto"/>

        <TextBlock
Text
="{Binding
Path
=Fiyat}" Foreground="#FF838383"
Height
="Auto" Width="Auto"/>

        <TextBlock
Text
="YTL" TextWrapping="Wrap"
Foreground
="#FF838383" Height="20"
Width
="20"/>

      </StackPanel>

    </DataTemplate>

Gördüğünüz gibi DataTemplate'i değiştirerek aslında işimizi çözmüş
olduk. Geriye tek bir sorun kalıyor. Kullanıcı herhangi bir Item'ı
ListBox içerisinden seçtiğinde TextBox'ın içine ne yazılacak?
Kitap nesnesinin Adi mı? yoksa Fiyatı mı? Tabi ki bizim
örneğimizde Kitap nesnesinin fiyatı yazılacak aksi halde filtreleme
mekanizmamızla çakışacaktır.

[VB]

  Public Class Kitap

 

        Private PAdi As String

        Public Property Adi() As String

            Get

                Return PAdi

            End Get

            Set(ByVal value As String)

                PAdi = value

            End Set

        End Property

 

        Private PFiyat As Double

        Public Property Fiyat() As Double

            Get

                Return PFiyat

            End Get

            Set(ByVal value As Double)

                PFiyat = value

            End Set

        End Property

 

**       ** Public Overrides
Function
ToString()
As
String

**           ** Return Me.Fiyat

**       ** End Function

 

    End Class

[C#]

        public class Kitap

        {

            public string Adi { get; set;
}

            public double Fiyat { get; set;
}

 

**           ** public override string ToString()

**            {**

**               ** return this.Fiyat.ToString();

**            }**

        }

Bu da ne şimdi? dediğinizi duyar gibiyim. Maalesef AutoCompleteBox'ın
ListBox'tan seçili öğelerin hangi özelliklerinin alınacağına dair
bir ayarı yok. Aslında arka planda AutoCompleteBox kendi içerisinde bir
öğe seçildiğinde o öğeyi ToString() metodu ile alıp TextBox
içerisine yerleştiriyor. Bu durumda bizim de ToString metodunu
Override etmemiz her şeyi çözecektir. Artık herhangi bir Kitap
nesnesi üzerinden ToString çalıştırılırsa kitabın fiyat bilgisi
verilecek.

Özelleştirilmiş bir AutoCompleteBox örneği.
Özelleştirilmiş bir AutoCompleteBox örneği.

Hepinize kolay gelsin.