Silverlight 4 Beta içerisinde Combobox'taki minik yenilik!

0 dakikada yazıldı

6153 defa okundu

Düzenle

Silverlight 4 ile beraber gelen yenilikleri incelemeye başlayacağımız bu
makalede değinmek istediğimiz nokta biraz veri uygulamalarına yönelik
olacak. Aslına bakıldığında söz konusu yenilik çok basit gibi gözükse de
günü birlik silverlight veri uygulamaları geliştirme sürecinde sürekli
karşılaştığımız ve canımızı sıkan önemli bir sorunu çözmek için çok
değerli. Gelin hep beraber neden bahsettiğime bir göz atalım.

Bir DataGrid içerisinde Combobox sorunsalı!

Bunun neresi sorun yaratıyor diyebilirsiniz. Fakat veritabanında
tuttuğumuz değerleri entity tasarımımıza doğrudan yansıttığımızda (ki
Visual Studio içerisinde çoğu RAD (Rapid Application Development) aracı
bunu öngörür) Silverlight tarafındaki DataBing mekanizmalarında kıl bir
durum ile karşılaşıyorduk. Senaryoyu cümlelerle tanımlamak yerine örnek
üzerinden gitmeyi daha uygun görüyorum ;)

Örnek veritabanı tasarımı.
Örnek veritabanı tasarımı.

Yukarıda gördüğünüz şekilde veritabanımızda iki tablo var. Bu tablolar
birbirine TipID üzerinden bağlı. Yani her Insan'ın bir tipi var ve bu
tipler de ayrı bir tabloda FK (Foreign Key) ile bağlı. Malum bu tasarım
bizim veritabanı tasarımlarımızın en ufak yapıtaşını temsil edebilir.
Böyle minik bir tasarımdan yola çıkarak doğrudan Entity'leri yaratarak
ilerlediğimizde bakalım Silverlight içerisinde bir DataGrid ile
insanları nasıl gösterebileceğiz?

[VB]

<ServiceContract(Namespace:="")>

<AspNetCompatibilityRequirements(RequirementsMode:=AspNetCompatibilityRequirementsMode.Allowed)>

Public Class Service1

 

    Dim Veri As New
DataClasses1DataContext

 

    <OperationContract()>

    Public Function InsanlarGetir() As List(Of
Insanlar)

        Return (From inc In Veri.Insanlars).ToList

    End Function

 

    <OperationContract()>

    Public Function TiplerGetir() As List(Of
Tipler)

        Return (From inc In Veri.Tiplers).ToList

    End Function

 

End Class

Yukarıda gördüğünüz servis içerisindeki iki metod bize tüm insanları ve
tipleri basit bir şekilde döndürecek. Bu metodları kullanarak
Silverlight tarafından gerekli veriyi sunucudan çekeren Gridimize
bağlamaya çalışacağız.

[VB]

Partial Public Class MainPage

    Inherits UserControl

 

    Public Sub New()

        InitializeComponent()

    End Sub

 

    WithEvents Servis As New
ServiceReference1.Service1Client

 

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

        Servis.InsanlarGetirAsync()

    End Sub

 

    Private Sub Servis_InsanlarGetirCompleted(ByVal sender As Object,
ByVal e As ServiceReference1.InsanlarGetirCompletedEventArgs) Handles Servis.InsanlarGetirCompleted

        myGrid.ItemsSource = e.Result

    End Sub

End Class

Gördüğünüz en basit hali ile web servisimizden Insanlar listesini alarak
Grid'e bağladık. Grid içerisinde kolonları da tabi özelleştirmiş olmamız
şart ki böylece uygun bir Combobox'ı da Grid'in bir kolonuna
yerleştirebilelim.

[XAML]

        <my:DataGrid
x
:Name="myGrid"
AutoGenerateColumns
="False">

            <my:DataGrid.Columns>

                <my:DataGridTextColumn Binding="{Binding
Adi
}"/>

                <my:DataGridTextColumn Binding="{Binding
Adi
}"/>

                <my:DataGridTemplateColumn>

                    <my:DataGridTemplateColumn.CellTemplate>

                        <DataTemplate>

                            <ComboBox
 />

                        </DataTemplate>

                    </my:DataGridTemplateColumn.CellTemplate>

                </my:DataGridTemplateColumn>

            </my:DataGrid.Columns>

        </my:DataGrid>

Artık Grid'imiz de hazır durumda. Geriye birkaç eksik kaldı. Combobox'ı
nasıl dolduracağız? Combobox'ı dolduracak olan veriye Grid'e gelmiyor.
Grid'e sadece Insanlar listesi geliyor ve bu liste içerisinde de sadece
seçili Tipler'in ID bilgileri bulunuyor. Bu durumda ortak bir yerlerden
tüm Tip listesini çekip Combobox'lara vermek zorundayız. Bunun için
harici bir veri kaynağı yaratalım.

[VB]

Public Class TipListesi

    Property Tipler As New
System.Collections.ObjectModel.ObservableCollection(Of ServiceReference1.Tipler)

 

    Dim Servis As New
ServiceReference1.Service1Client

 

    Sub New()

        AddHandler
Servis.TiplerGetirCompleted, Sub(sender As Object,
e As ServiceReference1.TiplerGetirCompletedEventArgs)

                                                    Tipler = e.Result

                                                End Sub

        Servis.TiplerGetirAsync()

    End Sub

 

End Class

 

Yukarıdaki sınıfımızı XAML içerisinden veri kaynağı olarak kullanacağız.
Sınıf içerisinde listemizi sınıfın bir kopyası alındığı anda
dolduruyoruz. Doldurma işlemi için de tabi ki yine web servisimizdeki
Tiplere ait metodu çağırmak durumundayız.

[XAML]

<UserControl
xmlns:my="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"


x:Class="SilverlightApplication4.MainPage"

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

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

   xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

  
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

   mc:Ignorable="d"

   d:DesignHeight="300" d:DesignWidth="400"

            xmlns:daron="clr-namespace:SilverlightApplication4">

    <UserControl.Resources>

        <daron:TipListesi
x
:Name="TumTipler"
/>

    </UserControl.Resources>

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

        <my:DataGrid x:Name="myGrid" AutoGenerateColumns="False">

            <my:DataGrid.Columns>

                <my:DataGridTextColumn Binding="{Binding Adi}"/>

                <my:DataGridTextColumn Binding="{Binding Adi}"/>

                <my:DataGridTemplateColumn>

                    <my:DataGridTemplateColumn.CellTemplate>

                       
<DataTemplate
>

                            <ComboBox
DisplayMemberPath
="Tip" ItemsSource="{Binding
Source
={StaticResource
TumTipler
}, Path=Tipler}"
/>

                        </DataTemplate>

                    </my:DataGridTemplateColumn.CellTemplate>

                </my:DataGridTemplateColumn>

            </my:DataGrid.Columns>

        </my:DataGrid>

    </Grid>

</UserControl>

Yukarıdaki XAML dosyasında özellikle değişen renkli kısımlara dikkat
etmekte fayda var. İlk olarak arka planda yarattığımız sınıfımızı XAML'a
alabilmek için namespace tanımını yapıyoruz sonrasında da sınıfın bir
kopyasını Local Resource olarak yaratıyoruz. Son olarak bu
StaticResource'u Combobox'ın ItemsSource'una bağlıyoruz.
DisplayMemberPath'i de Tip olarak set ettikten sonra herşey
bitti! Artık uygulamamızı çalıştırabiliriz. Her satır Grid içerisinde
gösterilirken Combobox'lar da Tipler ile doldurulmuş olacaktır.

Peki de her kayıt için seçili tip seçili gelecek mi?

İşte esas sorun burada başlıyor. Maalesef gelmeyecek! Bizim bir şekilde
her kayıt Grid içerisinde yaratılırken elimizde olan TipID ile Combobox
içerisindeki nesnelerden birini bağlamamız gerek. Böylece Combobox
içerisinden uygun kayıt otomatik olarak seçili gelmeli! Hatta TwoWay
Binding kullanarak Combobox içerisinde bir seçim değişikliği olduğunda
bunu ana kayıt listemize de yansıtabilmemiz şart.

Bu durumda bakıyoruz Combobox'ın bind edilebilecek ne özellikleri var!
Elde sadece SelectedItem bulunuyor! Şimdi ben bu SelectedItem'ı
nasıl TipID'ye bağlayacağım? TipID integer oysa SelectedItem
Tipler tipinde. İşte bu ikisini birbirine bağlamanın yolu bir
ValueConverter kullanmak!

[VB]

Imports System.Windows.Data

 

Public Class TipMatchConverter

    Implements IValueConverter

 

    Dim TumTipler As New
TipListesi

    Dim Tipler As System.Collections.ObjectModel.ObservableCollection(Of ServiceReference1.Tipler)

 

    Sub New()

        Tipler = TumTipler.Tipler

    End Sub

 

    Public Function Convert(ByVal value As Object,
ByVal targetType As System.Type, ByVal parameter As Object,
ByVal culture As System.Globalization.CultureInfo) As Object
Implements System.Windows.Data.IValueConverter.Convert

        Return (From inc In Tipler Where inc.ID = CInt(value)).SingleOrDefault

    End Function

 

    Public Function ConvertBack(ByVal value As Object,
ByVal targetType As System.Type, ByVal parameter As Object,
ByVal culture As System.Globalization.CultureInfo) As Object
Implements System.Windows.Data.IValueConverter.ConvertBack

        Return CType(value, ServiceReference1.Tipler).ID

    End Function

End Class

Hazırladığımız bu Converter yapısını da SelectedItem'ın
bindinginde artık rahatlıkla kullanabiliriz.

[XAML]

<UserControl
xmlns:my="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"


x:Class="SilverlightApplication4.MainPage"

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

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

   xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

  
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

   mc:Ignorable="d"

   d:DesignHeight="300" d:DesignWidth="400"

            xmlns:daron="clr-namespace:SilverlightApplication4">

    <UserControl.Resources>

        <daron:TipListesi
x
:Name="TumTipler"
/>

        <daron:TipMatchConverter x:Name="TipMatchConverter" />

   
</UserControl.Resources
>

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

        <my:DataGrid x:Name="myGrid" AutoGenerateColumns="False">

            <my:DataGrid.Columns>

                <my:DataGridTextColumn Binding="{Binding Adi}"/>

                <my:DataGridTextColumn Binding="{Binding Adi}"/>

                <my:DataGridTemplateColumn>

                    <my:DataGridTemplateColumn.CellTemplate>

                        <DataTemplate>

                            <ComboBox DisplayMemberPath="Tip"
ItemsSource="{Binding Source={StaticResource TumTipler}, Path=Tipler}"

                                    
SelectedItem
="{Binding
TipID
, Converter={StaticResource
TipMatchConverter
}, Mode=TwoWay}"/>

                        </DataTemplate>

                    </my:DataGridTemplateColumn.CellTemplate>

                </my:DataGridTemplateColumn>

            </my:DataGrid.Columns>

        </my:DataGrid>

    </Grid>

</UserControl>

Kendimizi hamallık yapmış gibi hissetmemiz normal :) Çünkü biraz öyle
oldu. Fakat yapacak birşey yok. Silverlight 3.0'da uygulayabileceğiniz
en temiz çözümlerden biri bu. Bu arada unutmadan belirtiyim aslında
Converter içerisinde servisten verinin kesinlikle gelip gelmediğini de
kontrol etmenizde fayda var. Kullanıcının bağlantı hızına göre sorunlar
yaşanabilir. Tavsiyem Tipler'i global bir yerlerde tutup oradan
kullanmanız olabilir.

Peki Silverlight 4.0 ile ne oldu?

Silverlight 4.0 ile gelen özellikle çok basit :) Aslında bu basit
özelliğin değerini bilelim diye yukarıdaki senaryoyu özellikle anlatmak
istedim. Silverlight 4.0 ile Combobox'lara SelectedValue geliyor! :D
Oley!

Böylece rahatlıkla Combobox'ın SelectedValuePath'ini ID olarak
ayarlayıp Tipler nesnesindeki ID kolonunun DataGrid'e gelen
Insanlar nesnesindeki TipID ile SelectedValue üzerinden bind
edilmesini sağlayabiliyoruz.

[XAML]

<UserControl
xmlns:my="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"


x:Class="SilverlightApplication4.MainPage"

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

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

   xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

  
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

   mc:Ignorable="d"

   d:DesignHeight="300" d:DesignWidth="400"

            xmlns:daron="clr-namespace:SilverlightApplication4">

    <UserControl.Resources>

        <daron:TipListesi x:Name="TumTipler" />

    </UserControl.Resources>

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

        <my:DataGrid x:Name="myGrid" AutoGenerateColumns="False">

            <my:DataGrid.Columns>

                <my:DataGridTextColumn Binding="{Binding Adi}"/>

                <my:DataGridTextColumn Binding="{Binding Soyadi}"/>

                <my:DataGridTemplateColumn>

                    <my:DataGridTemplateColumn.CellTemplate>

                       
<DataTemplate
>

                            <ComboBox DisplayMemberPath="Tip"
ItemsSource="{Binding Source={StaticResource TumTipler}, Path=Tipler}"

                                   
SelectedValue
="{Binding
TipID
, Mode=TwoWay}"
SelectedValuePath
="ID" />

                        </DataTemplate>

                    </my:DataGridTemplateColumn.CellTemplate>

                </my:DataGridTemplateColumn>

            </my:DataGrid.Columns>

        </my:DataGrid>

    </Grid>

</UserControl>

İşte gördüğünüz üzere :) bu kadar basit görülebilecek bir değişiklik
aslında ne kadar da hayat kurtarıcı olabililyor. Silverlight 4.0 release
olsa da hemen şu dertlerden kurtulsak :)

Hepinize kolay gelsin.