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