Ana Sayfa | English Blog | Seminer TV | Dil Cookie Sil  Blog'u Mail ile takip et!       
Daron Yöndem - Silverlight 3.0
bir yazılımcının tasarıları...
 Tuesday, July 20, 2010

Sanırım aradan epey zaman geçti fakat :) geçenlerde misafir olarak bulunduğum bir şirkette aşağıdaki ipucunu paylaştığımda "Neden bloga yazmıyorsunuz bunları?" tepkisini alınca :) aklımda kenara not almıştım. "Bunu bloga yazmam lazım" şeklinde :) ve zamanı geldi. Biliyorsunuz Silverlight'ın bir OutOfBrowser modu söz konusu ve özellikle "Elevated Trust"ın da gelmesi ile epey popüler olmaya başladı bu özellik. Peki bu şekilde OOB modunda çalışan uygulamalar geliştirirken uygulamamızı nasıl debug edeceğiz? Sonuçta F5'e bastığımızda projemiz hem bir web sunucu üzerinden çalıştırılıyor ve uygulama windows modunda açılamıyor.

Visual Studio 2010 ile beraber bu soruna da bir çözüm geldi. Artık Silverlight projenizi OOB moduna geçebilecek şekilde ayarladıktan sonra Silverlight projenize sağ tıklayıp "Properties" komutunu verdiğinizde "Debug" tabında özel bir ayar bulabilirsiniz.

Out Of Browser modunda debug seçeneği.
Out Of Browser modunda debug seçeneği.

Yukarıdaki ekran görüntüsünde de gördüğünüz üzere Debug ayarlarında eğer "Out-of-browser application" seçeneğini işaretlerseniz artık Visual Studio 2010 içerisinde F5'e bastığınızda Silverlight uygulamanız doğrudan Desktop uygulaması gibi açılacaktır ve rahatlıkla her tür debugging işlemini yapabileceksiniz.

Hepinize kolay gelsin ;)

Tuesday, July 20, 2010 9:10:36 AM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Silverlight 3.0 | Silverlight 4 | Visual Studio 2010  | 
 Saturday, April 03, 2010

Geçen haftalarda Microsoft ofisinde iş ortakları için özel olarak düzenlenen bir seri seminerin son parçasını da sizlerle paylaşıyorum. Bu defa Expression Studio'da Design ve Blend, Sketchflow taraflarına değiniyoruz.

Yukarıdaki videoyu bilgisayarına indirmek isteyenler blogumun SeminerTV bölümünden faydalanabilirler.

İyi seyirler...

Saturday, April 03, 2010 10:40:48 AM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Expression Blend | Expression Design | Expression Studio | Silverlight 3.0 | WPF  | 
 Thursday, April 01, 2010

Her sene 1 Nisan MVP'liğimin bittiği gün :) Şaka gibi değil mi? :) Biliyorsunuz MVP ünvanı her seferinde senelik olan Microsoft tarafından verilen bir ünvan bu nedenle bir bitiş tarihi var ve bu nedenle de her sene yenilenmesi gerekiyor :) Eh benimki bugün yenilendi. 2010 yılında da "Silverlight MVP"si olarak var olmaya devam edeceğim. Bu gaz ile önümüzdeki dönemde blogda ekstra hareketlilik göreceğinizden de emin olabilirsiniz :)

Hepinize bu dönemde gösterdiğiniz destek için ayrıca çok teşekkürler. Bilgiyi paylaşmak ancak paylaşabileceğiniz insanlar oldukça zevkli ve güzel. 2010'da da daha yapacak çok işimiz var ;) ve güzel sürprizlerim var ;)

Görüşmek üzere.

Thursday, April 01, 2010 6:58:01 AM (GTB Standard Time, UTC+02:00)  #    Comments [3]   Silverlight | Silverlight 2.0 | Silverlight 3.0 | Silverlight 4  | 
 Saturday, February 13, 2010

Bu görsel dersimizde Silverlight ile sunucuya FileUpload işlemi yapmanın yollarından birini inceliyoruz.

Yukarıdaki videoyu bilgisayarına indirmek isteyenler blogumun SeminerTV bölümünden faydalanabilirler.

İyi seyirler...

Saturday, February 13, 2010 5:35:43 PM (GTB Standard Time, UTC+02:00)  #    Comments [11]   Silverlight 2.0 | Silverlight 3.0 | Silverlight 4  | 
 Saturday, February 06, 2010

MS Days BulgariaHerkese selamlar! :) Neden bilmiyorum ama selam vererek yazıya başlayasım geldi. Geçenlerde Bulgaristan'da bir üniversiteden mail almıştım ve "Ne kadar Türk var orada?" diye sorduğumda aldığım cevap beni epey şaşırtmıştı. Sanırım bizim buradan Bulgaristan'a okumaya giden epey gencimiz var :) İşte bu duyuruyu da onlar için bloğuma yazma kararı aldım. Mart sonunda 29Mart-1 Nisan arasında üç gün boyunca Bulgaristan'da Sofya'da olacağım. Microsoft'un “MS Days” adındaki konferansında iki oturumum var.

Oturumlardan biri hem WPF hem de Silverlight tarafında yani web ve windows'ta Multitouch programlama ile ilgili. Diğeri ise Silverlight 3 ile beraber gelen ve 4'teki yeni özelliklerle daha da kuvvetlenen Silverlight'ın "Out-Of-Browser" modunu içeriyor. OOB modu ile web uygulamalarının kontrolünü ve veri senkronizasyonunu inceleyeceğiz. Multitouch oturumu lvl200 iken OOB lvl300'e yaklaşacak diyebilirim.

Özetle, Bulgaristan'daki tüm arkadaşları tabi olanakları elveriyorsa kendi ülkelerinde yapılan bu aktiviteye bekliyorum ;) Kayıt için msbgregistration.com adresini ziyaret edebilirsiniz. Ayrıca facebook sayfası da burada. Genelde Bulgarca birşeyler yazılı, kötü birşeyler yazılırsa bana haber verin :P

Unutmadan! Bulgaristan'daki Türk MVP'lerimizden sevgili Coşkun Sunalı'nın da aktivitede bir oturumu var ;) Coşkun'un oturumunu da takvime almayı unutmayın bence ;)

Saturday, February 06, 2010 2:32:24 PM (GTB Standard Time, UTC+02:00)  #    Comments [7]   Silverlight 3.0 | Silverlight 4  | 
 Sunday, November 15, 2009

Hatırlarsanız Berlin, TechEd Europe ziyaretimi sizlerle blogumdan paylaşırken TechEd Online için de bir "Multitouch in Silverlight" röportajı verdiğimden bahsetmiştim. Söz konusu röportaj TechEd Online sitesinde yayınlandı! Ben de sizlerle paylaşmak istiyorum ;)

Sunday, November 15, 2009 4:46:59 AM (GTB Standard Time, UTC+02:00)  #    Comments [3]   Silverlight 3.0  | 
 Tuesday, October 27, 2009

Bugün ÇOK yoğun bir gündü :) hem benim için hem de İzmir için :) Sabah 9'da Ege Üniversitesi Bilgisayar Mühendisliğin'de Silverlight Workshop ile maratona başladık. Hemen genel Silverlight konseptine baktıktan sonra veri erişiminden tutun animasyonlara kadar geniş bir alanda gezdik diyebilirim. Workshop'a öğlen arası verdiğimizde ise ben Windows 7 Akademik Lansman'da konuşmak üzere başka bir binaya doğru koşmak durumunda kaldım :) Tamam itiraf ediyorum, arabayla gittik.

Silverlight Workshop, Ege Üniversitesi
Silverlight Workshop, Ege Üniversitesi

Windows 7 Akademik Lansmanların üçüncü ayağı olan İzmir ayağını yine Ege Üniversitesi'nde gerçekleştirdik. Lansman 500 kişiye yakın katılım ile bence muhteşemdi. Özellikle çekilişte hediye bir arkadaşına çıkmasına rağmen arkadaşından çok sevinen :) kardeşimiz beni gerçekten çok güldür. Bir an kazanan kendisi sandım.

Windows 7 Akademik Lansman, İzmir, Ege Üniversitesi
Windows 7 Akademik Lansman, İzmir, Ege Üniversitesi

Buradan özellikle Ege'deki tüm MSP'lerimize çok çok teşekkür ediyorum. Farklı üniversitelerden katılım ile lansmanın başarılı olmasında büyük faydaları var. Hepinize çok teşekkürler!

Tam akşam oluyor, hem Workshop hem Lansman bitti derken :) son bir mini seminer daha yapmak üzere İzmir Ekonomi Üniversitesi'ni ziyaret ettim. Bana bir ders saatini ayırıp beni misafir eden sevgili Çoşkun Atay hocama buradan çok teşekkür ediyorum. Hızlı bir akşam oturumu ile öğrencilerle Windows 7, Multitouch, WPF, Silverlight gibi konularda sohbet etme şansım oldu. Umarım ileride daha teknik ağırlıklı etkinlikler de yapma şansımız olacak.

Gün yoğun olduğu kadar da eğlenceliydi benim için. Gün boyunca bana gülümseyen ;) selamını esirgemeyen herkese ÇOK teşekkürler. Tekrar görüşmek üzere İzmir ;)

Tuesday, October 27, 2009 9:51:44 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Silverlight 3.0 | Windows 7  | 
 Friday, October 23, 2009

İTÜ'ye gitmeyeli epey olmuş :) En son iki sene önce gitmişim. Bugün IEEE organizasyonu ile İTÜ'yü tekrar ziyaret etme şansım oldu. Elektrik-Elektronik fakültesinde yaptığımız iki seminerde kullanıcı deneyiminin önemine değindikten sonra bir de işin teknik tarafına geçip WPF ve Silverlight'a göz attık. Oturumların süreleri toplam üç saat olunca süre bana az geldi ve biraz hızlı anlatmaya başladım. Umarım :) dinleyici arkadaşlar ne anlattığımı anlayabilmiştir.

İTÜ, IEEE, Silverlight ve WPF Seminerlerim
İTÜ, IEEE, Silverlight ve WPF Seminerlerim

Organizasyonda emeğinden dolayı özellikle Hüseyin Armağan Güler'e çok teşekkürler. Bu kadar ilgili bir kitleye ulaşabilmiş olmak konsept olarak kısmen uzak bir fakültede bence büyük bir başarı. Katılan herkese de ayrıca teşekkür ediyorum. Akşamın sekizine kadar benle kaldınız ve hatta çıkışta bile zor ayrıldık :) Umarım hepinize için faydalı bir seminer olmuştur.

Thursday, October 22, 2009 11:23:59 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Silverlight 3.0 | WPF  | 
 Thursday, October 22, 2009

Silverlight ile RSS kullanımı konusunda bolca soru alıyorum. Bu görsel dersimizde Silverlight ile bir RSS kaynağının kullanımını ayrıca sunucu taraflı RSS kaynağı üretmenin de kolay bir yolunu beraber inceliyoruz.

Yukarıdaki videoyu bilgisayarına indirmek blogumun SeminerTV bölümünden faydalanabilirler.

İyi seyirler...

Thursday, October 22, 2009 4:29:06 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Görsel Ders | Silverlight 3.0  | 
 Wednesday, October 21, 2009

Bugün Samsun 19 Mayıs Üniversitesi'ndeydim. Bilişim günleri etkinliğinin ikinci gününde Silverligjt, WPF ve LINQ üzerine oturumlarım oldu. Uzun süredir Samsun'da bu gibi bir aktivitenin yapılmamış olduğu bilgisini öğrencilerden alırken aslında ben de çaktırmadan ilk Samsun ziyaretimi yapmış oldum. Kimse üstüne alınmazım ama :) deniz kenarı sevdam doğduğum şehir İstanbul'dan kaynaklanıyor olsa gerek.

19 Mayıs Üniversitesi Bilişim Kulübü ile hatıra fotoğrafı.
19 Mayıs Üniversitesi Bilişim Kulübü ile hatıra fotoğrafı.

Etkinliğe tahmin ettiğimin üzerinde bir katılım oldu. Sonrasında tam günlük seminer serisine dayanamayanlar da olmadı değil :) Hatta aldığım duyumlara göre ben varmadan önce "O kadar saat nasıl anlatacak" şeklinde sorular geziyormuş kulaktan kulağa :) Belli ki beni tanımıyorlar :) Şaka bir yana özellikle etkinlikte organizason için tüm Bilişim Kulübü'ne, hocalarımıza ve sevgili MSP Hacer Kübra Köse'ye çok teşekkür ediyorum. Benim için büyük bir zevkti. Umarım herkes için faydalı bir gün olmuştur.

Tuesday, October 20, 2009 11:16:07 PM (GTB Standard Time, UTC+02:00)  #    Comments [10]   LINQ | Silverlight 3.0 | WPF  | 
 Thursday, October 08, 2009

Günümüzde artık tarayıcı içi iş uygulamalarında uygulama geliştirme platformu olarak Silverlight dışında pek birşey düşünülemiyor. Fakat bu manzara içerisinde de tabi ki daha üçüncü sürümünde bir ürün olarak Silverlight'ın eksikleri var. Bu eksiklerin bazıları maalesef Silverlight yapısı gereği içerisine oturduğu mimari rolle de alakalı olabiliyor. Çok basit bir örnek olarak veri erişim modelini ele alabiliriz. Bugün Silverlight ile bir uygulama geliştirmek isteyen herhangi bir iş uygulaması geliştirme ekibinin alışması gereken ilk şey servis yönelimli mimari. Buraya kadar herşey aslında çok güzel fakat en basit CRUD (Create, Read, Update; Delete) operasyonları için bile birer servis hazırlayıp, bu servislerin referanslarını alıp SL tarafında tek tek kullanıyor olmak bazı durumlarda rahatsız edici şekilde uygulama geliştirme sürecini uzatabiliyor da.

.NET RIA Services!

İş uygulamalarınızın çapına/büyüklüğüne göre .NET RIA Services dertlerinize çare olabilir. Peki nasıl? Aslında yapmak istediğimiz şey bir şekilde http:80 üzerinden CRUD operasyonlarını yapabilmek. Hemen belki de aklınıza ADO.NET Data Services gelecektir. Kesinlikle doğru yoldasınız. Aslında ADO.NET Data Services ile sunucu tarafında ciddi bir gelişme olmuştu ve artık URL üzerinden sorgularımızı atabiliyor, sonrasında da XML olarak cevabı alabiliyorduk. Fakat hala bazı sorunlar vardı, çünkü ADO.NET Data Services'ın kullanımı Silverlight tarafında pek de kolay değildi. Bu gelişmeleri takiben .NET RIA Services aslında ADO.NET Data Services üzerine oturarak bu altyapının Silverlight ile rahatlıkla konuşabilmesini sağladı. Validasyon kodlarının hem sunucu hem istemci tarafında iki defa yazılması gibi sorunları belirli senaryolarda gidererek .NET RIA Services şu anda July Preview sürümü ile indirilebilir durumda. Daha Release olmamış (yayınlanmamış) bir ürün olduğunun altını çizerek devam edelim.

RIA Services'ın development sürecinde bir çok avantajı var. Bunlardan ilki artık sunucu tarafındaki uygulamanız ile istemci tarafındaki uygulamanızın tek bir uygulama gibi gözükmesi. İlk bakışta çok garip geleceğinin farkındayım fakat artık istemci tarafındaki Silverlight uygulamanızdan sunucuya anında ulaşabiliyorsunuz, herhangi bir şekilde service reference eklemeniz vs gerekmiyor. Tabi tüm bu işlemleri biz yapmıyorsak bizim yerimize birileri yapıyor demektir ki bu durumda Visual Studio yardımımıza koşuyor ve otomatik olarak sunucu ile istemci taraflarını birbiri ile konuşturacak ek kodları yaratıyor. Ayrıca RIA Services tarafında da bulunan bazı sınıflar tabi ki kullanılıyor. Validasyonla ilgili tüm sunucu taraflı tanımlamalar otomatik olarak istemci tarafına da derleme zamanında ekleniyor. Tüm bu kolaylıkları görmek için gelin yeni bir Silverlight projesi yaratalım.

RIA Services kullanacağız.
RIA Services kullanacağız.

Yeni bir Silverlight projesi yaratırken karşınıza çıkan ilk ekranda projeye bir ASP.NET Web Sitesi eklediğimiz gibi bir de "Enable .NET RIA Services" checkbox'ını işaretlememiz gerekiyor. Böylece gerekli referanslar alınacak ve ayarlar yapılacaktır. Yapılan ayarlardan ve altyapının çalışma şeklinden detaylı olarak bahsedeceğiz. Yeni projeyi yarattıktan sonra hızlı bir şekilde projenize bir "LINQ2SQL" dosyası ekleyip veritabanından da istediğiniz bir tabloyu kullanabilirsiniz. Tabi tüm bunları ASP.NET tarafında yapıyoruz. Sunucu tarafında DAL (Data Access Layer) olarak LINQ2SQL'imiz hazır olduğuna göre bir sonraki adımda servis katmanını hazırlamamız gerekecek. Yani bir şekilde bu veritabanındaki tabloları Silverlight tarafına aktaracak olan katmanı yaratmalıyız. Bunun için ASP.NET tarafında projenize "Domain Service Class" türünde yeni bir dosya ekleyebilirsiniz.

DomainServiceClass ile sunucudan istemciye bağlantı sağlıyoruz.
DomainServiceClass ile sunucudan istemciye bağlantı sağlıyoruz.

Domain Service Class eklediğiniz anda karşınıza yukarıdaki ekran gelecektir. Burada "Enable Client Access" zaten seçili olacaktır. Eğer bu seçenecek seçili olmaz ise tabi ki Silverlight'ın bu servise ulaşması mümkün olmaz. Sonrasında hemen alt basamakta kullanacağınız DAL'ı seçiyorsunuz. Bizim projemizde bir LINQ2SQL dosyası bulunduğuna göre onu seçebiliriz. Seçtiğiniz veri kaynağına göre Entity'lerin listesi alt tarafa gelecektir. Buradan da hangi Entity'leri istemciye açmak istiyorsanız onu seçebilirsiniz. Eğer bu Entity'ler aracılığı ile veri üzerinde değişiklik yapılacaksa "Enable Editin" checkbox'ını işaretlemeyi de unutmamanızda fayda var. Son olarak en altta yer alan "Generate associated classes for metadata" kısmı ise her sınıfın propertylerine metadata tanımlanabilmesini sağlayacak ek sınıfları yaratacak kodun otomatik olarak yaratılıp yaratılmaması ile ilgili. Bu konuya ileride detaylıca değineceğiz fakat şimdilik bilmemiz gereken şey şu; eğer sunucu taraflı validasyon kodları yaazacaksan ve bu kodların otomatik olarak istemciye de taşınmasını istiyorsak kesinlikle bu seçenek işaretli kalmalı.

[VB]

<EnableClientAccess()>  _

Public Class DomainService1

    Inherits LinqToSqlDomainService(Of DataClasses1DataContext)

 

    Public Function GetInsans() As IQueryable(Of Insan)

        Return Me.Context.Insans

    End Function

 

    Public Sub InsertInsan(ByVal insan As Insan)

        Me.Context.Insans.InsertOnSubmit(insan)

    End Sub

 

    Public Sub UpdateInsan(ByVal currentInsan As Insan)

        Me.Context.Insans.Attach(currentInsan, Me.ChangeSet.GetOriginal(currentInsan))

    End Sub

 

    Public Sub DeleteInsan(ByVal insan As Insan)

        Me.Context.Insans.Attach(insan)

        Me.Context.Insans.DeleteOnSubmit(insan)

    End Sub

End Class

Domain Service Class'ı eklediğiniz gibi projenizde DomainService1.vb/cs adında bir dosya göreceksiniz. Bu dosya içerisinde kodlar yukarıdaki gibi olacaktır. Görüldüğü üzere yaratılan DomainService doğrudan LinqToSqlDomainService'den türetilmiş hatta türetilirken de DAL olarak LINQ2SQL sınıfımızın gösterilmiş. DomainService içerisinde Insert, Update, Delete işlemlerini yapan metodlar ve bu metodlar içerisinde de aslında LINQ2SQL Context'i kullanan kodlar var. GetInsans adındaki metod ise doğrudan SQL'deki tüm insanları döndürüyor.

Bu noktaya kadar yaptığımız şey iki tıklama ile DAL kısmını çözmek sonrasında da servis katmanı için de bir DomainService eklemek oldu. Daha bir satır kod bile yazmadık. Şimdi ilginç bir sürpriz ile karşılaşmak için Silverlight tarafına geçip Page.XAML arkasındaki VB/CS dosyasını açıyoruz.

Sunucu ve istemci arası iletişim.
Sunucu ve istemci arası iletişim.

Yukarıda gördüğünüz manzaradan da anlaşılabileceği üzere bir anda sunucu tarafındaki ASP.NET projemizin namespace'ine istemcide ulaşır hale geldik. Bunun çalışabilmesi için herhangi bir şekilde web servisi eklememiz vs gerekmedi. Aynı şekilde bu namespace altından sunucu tarafındaki DomainService'e de rahatlıkla ulaşabileceğiz hatta sunucu tarafında yapılan değişiklikler de istemci tarafında development zamanında Visual Studio tarafından yansıtılacak. Peki basit bir şekilde sunucudan tüm insanların bilgilerini çekmek istersek ne yapabiliriz? Deneme amaçlı olarak XAML tarafında sayfanıza adı myGrid olan bir Grid yerleştirdikten sonra aşağıdaki kod örneğinden faydalanabiliriz.

[C#]

        void MainPage_Loaded(object sender, RoutedEventArgs e)

        {

            DomainService1 Servis = new DomainService1();

            System.Windows.Ria.Data.LoadOperation Yukleme = Servis.Load(Servis.GetInsansQuery());

            Yukleme.Completed += new EventHandler(Yukleme_Completed);

        }

 

        void Yukleme_Completed(object sender, EventArgs e)

        {

            myGrid.ItemsSource = ((System.Windows.Ria.Data.LoadOperation<Insan>)sender).Entities;

        }

Örneğimizde hemen UserControl load olduğu gibi DomainService1 adında, aslında sunucu tarafındaki yarattığımız DomainService nesnemizden bir kopya alıyoruz. Ne de olsa bu nesne otomatik olarak istemci tarafına taşındı. Not olarak bu nesneyi SilverlightApplication1.Web altında bulabileceğinizi o nedenle using ile söz konusu sınıfı eklemeyi unutmamakta fayda var. Söz konusu DomainService'den bir kopya aldıktan sonra yükleme işlemini başlatmak üzere bir LoadOperation nesnesi tanımlıyoruz. Bu LoadOperation nesnesine de Servis üzerinden bir Load operasyonu yaratım aktarmamız gerekiyor. Bizim zaten tüm insanların bilgisini getirecek olan sunucu tarafında bir sorgumuz vardı. Onu kullanabilmek için doğrudan Servis üzerinden GetInsansQuery dememiz yeterli oluyor. Aslına bakarsanız GetInsansQuery bize sorgulanabilir bir insan listesi tanımı getirmekle mükellef. Bir sonraki adımda biraz daha detaylara gireceğiz.  Yükleme işlemimiz hazır olduğuna göre hemen yüklememizin Completed event'ını da yakalamakta fayda var. Completed event'ının sender'ı malum bizim LoadOperation'ın ta kendisi. Geriye Insan nesnesi döneceğini bildiğimiz için uygun casting işlemini de yapıp hemen operasyon tarafından döndürülen Entities dizisini alıp gridimize bağlayabiliyoruz.

[C#]

        void MainPage_Loaded(object sender, RoutedEventArgs e)

        {

            DomainService1 Servis = new DomainService1();

            System.Windows.Ria.Data.LoadOperation Yukleme = Servis.Load(from inc in Servis.GetInsansQuery()

                                                                        where inc.Adi.Contains("A") select inc);

            Yukleme.Completed += new EventHandler(Yukleme_Completed);

        }

 

        void Yukleme_Completed(object sender, EventArgs e)

        {

            myGrid.ItemsSource = ((System.Windows.Ria.Data.LoadOperation<Insan>)sender).Entities;

        }

RIA Services'ın esas esnek taraflarından biri de doğrudan istemciden istediğimiz sorguyu sunucuya gönderebiliyor olmamız. Tabi bu noktada itiraf etmem gerek ki kullanılabilecek keyword'lerde ADO.NET Data Services mimarisi gereği bazı sınırlar var fakat genel kullanımda pek sorun yaşanacağını sanmıyorum. Eğer çok kompleks sorgular düşünüyorsanız bunları sunucu tarafında ayrı metodlar olarak tanımlamak gerekecektir. Fakat basit bir where sorgusu gibi sorgularınızın çoğunu yukarıdaki şekilde doğrudan istemciden sunucuya gönderebilirsiniz.

Yukarıdaki kod içerisinde değişen tek şey bizim LoadOperation'ın yaratılırken aldığı parametre. Artık sadece bir GetInsansQuery değil de söz konusu Query ile dönen nesne tanımını tekrar sorgulayan bir yapı tanımlıyoruz. Burada aklınıza takılabilecek nokta; "Acaba tüm veriyi istemciye alıp orada mı sorguluyor?" olabilir. Tabi ki hayır! :)

Sorgumuz nereye nasıl gidiyor?
Sorgumuz nereye nasıl gidiyor?

Ekran görüntüsünde inceleyebileceğiniz rapor hazırladığımız Silverlight uygulaması çalıştırıldığında sunucumuza giden istekleri listeliyor. İsteklerin en sonunda ClientBin altında DataService.axd adında bir yere talep gönderildiğini görebiliyoruz. Bu talebin tam yolunu incelersek aslında bizim sorgunun da orada bulunduğunu görebilirsiniz. Bu da şu anlama geliyor; sorgu doğrudan sunucuya URL üzerinden gönderilmiş durumda.

SQL Profile'a baktık, sorgu orada!
SQL Profile'a baktık, sorgu orada!

Aynı şekilde SQL Profile ile SQL'e giden isteklere baktığımızda da bizim taaa :) Silverlight'tan gönderdiğimiz LINQ sorgusunun ASP.NET tarafından da algılanıp bir SQL sorgusuna çevrilerek parametremiz ile SQL'e gönderildiğini görebiliyoruz. Sanırım yeterince başarılı :)

Insert, Delete, Update nasıl yapılır?

.NET RIA Services tarafındaki operasyonların çoğu LINQ2SQL operasyonlarına benziyor. Yeni bir kayıt eklemek için ilk olarak söz konusu kayda ait Entity'den bir kopya alarak veriyi doldurmanız sonrasında da DomainService üzerinden uygun listeye eklemeniz yeterli.

[C#]

            DomainService1 Servis = new DomainService1();

            Servis.Insans.Add(new Insan() { Adi = "Denek", Soyadi = "denek2", Dogum=DateTime.Now, Tip=1 });

            Servis.SubmitChanges();

Son satırda ayrıca SubmitChages metodunu çağırmamız gerekli. Aksi halde yaptığımız değişiklikler sunucu tarafına gönderilmeyecektir. Delete işleminde ise Servis'in bizden istediği silinecek nesnenin bir referansı oluyor. Bu durumda örneğimizdeki Grid'de seçili nesneyi rahatlıkla silinmek üzere aktarabiliriz.

[C#]

        DomainService1 Servis = new DomainService1();

 

        void btnTikla_Click(object sender, RoutedEventArgs e)

        {

            Servis.Insans.Remove((Insan)myGrid.SelectedItem);

            Servis.SubmitChanges();

        }

Kod örneğindeki gibi servis üzerinden Insans listesinden Grid'deki seçili Insan nesnesini kaldırdıktan sonra tekrar SubmitChanges ile gerekli değişikliklerin sunucu tarafına yansıtılmasını sağlıyoruz. Aynı şekilde Entity'ler üzerinde yaptığımız değişiklikler de sunucu tarafına birer Update komutu ile yansıtılacaktır.

[C#]

        DomainService1 Servis = new DomainService1();

 

        void btnTikla_Click(object sender, RoutedEventArgs e)

        {

            Insan Degisecek = (Insan)myGrid.SelectedItem;

            Degisecek.Adi = "Deneme83";

            Servis.SubmitChanges();

        }

Yukarıdaki kod Grid'de seçili nesneyi alıp adını değiştirip değişikliklerin sunucu tarafına gönderilmesi için de SubmitChanges metodunu çağırarak işlemi tamamlıyor. Zaten Grid içerisinde bir nesne aldığımız için bu değişiklik görsel olarak kullanıcıya da anında yansıyacaktır. Bir diğer güzellik ise aslında Grid'imize aktardığımız verinin zaten servisimiz ile sürekli bağlantı içerisinde olması. Yani biz ilk aşamada verimizi Grid'e bağladıktan sonra kullanıcının yaptığı tüm değişiklikleri otomatik olarak sunucuya göndermek istersek aslında sadece servisin SubmitChanges metodunu çağırmamız yeterli olacaktır. Böylece zaten tüm değişikliker sunucuya gönderilecektir.

Önemli Uyarı!

Silverlight ile uygulama geliştirmeyi çok kolaylaştıran bir yapı olarak RIA Services eminim ki sizleri heyecanlandıracaktır fakat unutmamak gerek ki ürün daha yayınlanmış değil ve Preview aşamasında. Çıkacak yeni Preview'larda çok şey değişebilir. O nedenle bu sistem üzerine yapacağınız yatırımlarda dikkatli olup bu risklerin bilincinde olmak önemli.

Hepinize kolay gelsin.

Thursday, October 08, 2009 12:19:39 PM (GTB Standard Time, UTC+02:00)  #    Comments [2]   Silverlight 3.0  | 
 Sunday, September 13, 2009

DeepZoom Composer gün geçtikçe daha da gelişiyor ve bir yazılımcı / tasarımcı aracı olmaktan çıkıp artık son kullanıcıların da kullanabileceği bir araç halini alıyor. Bu görsel derste DeepZoom Composer'ın son haline göz atıyor ve yenilikleri inceliyoruz. Umarım faydalı olur.

Yukarıdaki videoyu bilgisayarına indirmek isteyenler nedirtv.com'u ziyaret edebilirler.

İyi seyirler...

Saturday, September 12, 2009 11:29:23 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Görsel Ders | Silverlight 3.0  | 
 Saturday, September 12, 2009

Geçenlerde çok güzel bir şekilde tasarlanmış bir Silverlight uygulamasında eski klasik tarz bir MessageBox gördüm ve hemen "Aman tanrım!" tepkisini vermekten kendimi alamadım. Oysa aslında ne kadar da kolay özel bir MessageBox yaratıp projelerimizde kullanmak. Bu videomuzda Silverlight içerisinde ChildWindow kontrolünü alarak bir MessageBox'a çeviriyoruz. Umarım faydalı olur.

Yukarıdaki videoyu bilgisayarına indirmek isteyenler nedirtv.com'u ziyaret edebilirler.

İyi seyirler...

Friday, September 11, 2009 11:27:19 PM (GTB Standard Time, UTC+02:00)  #    Comments [6]   Expression Blend | Görsel Ders | Silverlight 3.0  | 
 Friday, September 11, 2009

Bir süre önce aldığım bir mail beni biraz şaşırtmıştı. Aldığım mailde Silverlight ile basit bir SlideShow uygulamasının nasıl yapılabileceği soruluyordu. Şaşırmammın nedeni ise aslında bu gibi bir uygulamanın yapılabilmesi için herşeyi blogda anlatmış olduğumu düşünmemdi. Oysa baktım ki ufak bir nokta eksik ve bunun üzerine aşağıdaki videoyu çekmeye başladım. Videomuzda Silverlight ile basit bir SlideShow uygulaması geliştiriyoruz. Umarım faydalı olur.

Yukarıdaki videoyu bilgisayarına indirmek isteyenler nedirtv.com'u ziyaret edebilirler.

İyi seyirler...

Thursday, September 10, 2009 11:23:54 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Görsel Ders | Silverlight 3.0  | 
 Wednesday, September 09, 2009

Tasarımcılar ile yazılımcılar arasındaki iletişim sorunları çözmek projelerin işleyişindeki en önemli noktalardan biri. Expression Blend 3 ve Visual Studio'nun kullanımı ile bir tasarımcı ile yazılımcının sorunsuz olarak beraber çalışmaları mümkün. Sizler için hazırladığım bu videoda Expression Blend 3'ün data bağlama özelliklerini inceliyoruz.

Yukarıdaki videoyu bilgisayarına indirmek isteyenler nedirtv.com'u ziyaret edebilirler.

İyi seyirler...

Tuesday, September 08, 2009 11:20:43 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Expression Blend | Görsel Ders | Silverlight 3.0 | WPF  | 
 Tuesday, September 08, 2009

Expression Blend 3 ile beraber gelen ilginç özelliklerden biri de SketchFlow. Sketchflow hakkında sizler için hazırladığım bu videoyu umarım beğenirsiniz.

Yukarıdaki videoyu bilgisayarına indirmek isteyenler nedirtv.com'u ziyaret edebilirler.

İyi seyirler...

Monday, September 07, 2009 11:18:02 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Expression Blend | Silverlight 3.0 | WPF  | 
 Sunday, September 06, 2009

Neredeyse her tür yazılım projesinde prototip geliştirmek çoğu zaman başvurduğumuz bir süreç oluyor. Bazen bu prototipleri sadece yapacağımız projenin fizibilitesini ölçmek amacı ile geliştirsek de bazen de amacımız müşterimize gelecekte elde edeceği ürünü kabaca göstermek de olabiliyor. İşin en sıkıcı tarafı ise çoğu zaman bu prototiplerin teklif aşamasında kullanılması nedeniyle aslında kuyuya atılan bir taştan farklı olmamaları. Yani prototipiniz karşılığında eğer hedef projeyi teklifiniz ile alamazsanız hiçbir ücret alamayabilirsiniz. Sektörde karşılaşabileceğiniz farklı senaryolara değinmek yerine işin biraz daha teknik kısmına geçip bu süreçlerde işimizi kolaylaştırmak adına neler yapabiliriz ve bu çerçevede Expression Blend bize nasıl yardımcı olabilir sorularına cevap vereceğiz.

Sketchflow

Expression Blend 3 ile beraber SketchFlow denilen bir mekanizma, proje şablonu geliyor. Sketchflow'un amacı prototip geliştirmeyi kolaylaştırmak ve olabildiğince bu süreçlerde müşteri ile interaktif bir sistem sağlamak. Sketchflow projelerini hem WPF hem de Silverlight tarafında kullanabilirsiniz. Her iki altyapıda da aynı şekilde çalışıyor. Hemen Expression Blend ile yeni bir Silverlight Sketchflow projesi yaratalım.

İlk Sketchflow projemizi yaratıyoruz.
İlk Sketchflow projemizi yaratıyoruz.

Normal bir Blend projesine göre Sketchflow projeleri Blend ile açıldığında ekranda iki farklı bölüm daha oluşacaktır. Bu bölümlerden birinin adı "Map" diğeri ise "Sketchflow Animation" şeklinde. Sketcthflow'un mantığında prototip geliştirmek olduğu için geliştireceğiniz prototip uygulamanın haritası tabi ki çok önemli. Uygulamada hangi sayfalar olacak? Hangi sayfadan hangi sayfaya geçişler yapılacak? gibi tüm soruları harita üzerinde gerekli kontrolleri yarattıkça cevaplamış oluyorsunuz.

SkethFlow projelerinde Map üzerinde çalışmak.

Skethflow ile bir prototip geliştirirken tüm projenizin bir haritasını yarattığınızı düşünebilirsiniz. Her ekranınız için haritada bir sayfa yaratmanız gerekiyor. Sayfalara farklı isimler verebilirsiniz. Herhangi bir sayfanın fare ile üzerine geldiğinde yukarıdaki görselde görebileceğiniz gibi bir menü gözükecektir. Bu menüde en sağdaki düğmeye bastığınızda söz konusu sayfanın harita üzerindeki rengini değiştirebilirsiniz. Böylece harita çok daha anlamlı ve okunaklı olacaktır. Diğer yandan haritadaki sayfaları birbirlerine uygun şekilde bağlayarak haritadınızı tamamlamak da çok önemli. Bunu yapmak için ister sayfaları tek tek yaratıp sonra menülerindeki soldan ikinci düğmeyi kullanarak sayfaları birbirlerine bağlayın ister herhangi bir sayfanın altındaki menüden soldan ilk düğmeyi kullanarak söz konusu sayfaya bağlantılı yeni bir sayfa yaratın, seçim sizin. Son olarak her sayfanın menüsünde soldan üçüncü düğme ise birden çok sayfada kullanılabilecek Component'ler oluşturmanızı sağlayacaktır. Bu konuyu da ileride detaylıca inceleyeceğiz.

Harita üzerinde yaptığınız her işlem otomatik olarak projenize yansıyacaktır. Harita içerisinde oluşturduğunuz sayfalar aslında projenizdeki ayrı bir XAML olarak saklanacaktır. Bu XAML sayfalarını ayrı ayrı açıp her zamanki gibi Blend içerisinde tasarımınızı yapabilirsiniz.

Tasarım yaparkan kullandığınız stillere dikkat!

Unutmayın ki bir prototip tasarlıyoruz. Prototipte amacımız ürünü müşteriye beğendirmekten öte aslında ileride ne gibi bir ürünle karşılaşacağına dair fikir vermek. Eğer projenin tüm görsel tasarımını da yapmak zorunda kalırsak işimiz ciddi zorlaşabilir. Oysa dediğimiz gibi yapmak istediğimiz şey sadece "nasıl birşey olacak?" kısmını müşteriye göstermek ve görsel detaylara takılmalarını olabildiğince engellemek.

Bunun için Sketchflow projelerinde kullanabileceğiniz özel olarak stillerle değiştirilmiş kontroller bulunuyor. "Asset Library" altında "Styles" tabına göz atarsanız normal projelerde bulunmayan SketchStyles seçeneğini bulacaksınız.

Sketchflow'a özel SketchStyles.
Sketchflow'a özel SketchStyles.

Yukarıdaki ekran görüntüsünde de görebileceğiniz üzere SketchStyles kontrollerini kullandığımızda zaten kontrollerin tasarımları gereği müşterimize "bu bir konsept tasarımdır" mesajını vermiş oluyoruz. Kimse bu şekilde hazırlanmış bir ekran görüntüsünün tasarımını eleştirmeyecek ve herkes tasarımın konsept olarak geliştirildiğini anlayacaktır. Böylece bir dertten daha kurtularak prototipimizi geliştirmeye devam edebiliriz.

Navigasyon

Ekranlarınızı tek tek tasarladıkça ekranlar arası geçişlerin de nasıl yapılabileceği önem kazanacaktır. Bir prototip ürettiğimize göre kod ile sayfalar arası geçiş yapacak değiliz. Konunun çok daha basit bir çözümü olsa gerek. Herhangi bir kontrol üzerine ekranda sağ fare tuşu ile tıklarsanız karşınıza gelen menünün en altında Navigate komutunu bulabilirsiniz.

Navigasyonun kolay yolu.
Navigasyonun kolay yolu.

Gördüğünüz gibi içerisinde olduğumuz Giris sayfasında gidebileceğimiz tüm seçenekler Navigate To menüsünde bulunuyor. Şu an için uygulamamızın haritasında Giris sayfası ile Sonuc sayfası birbiri ile ilişkilendirimiş değil. Yine de Navigate To menüsünden eğer Sonuc sayfasını seçerseniz uygulama haritanız da bu bilgiye göre yenilenecek ve Giris ile Sonuc sayfaları birbiri ile ilişkilendirilecektir.

Not: SketchFlow projelerinde Blend Behavior'larını kullanabilirsiniz. Bu şekilde çok daha zengin işlevsellikteki prototipleri tek satır bile kod yazmadan yaratabilirsiniz.

Animasyonlar?

SketchFlow projelerinizde isterseniz StoryBoard'lar kullanabilirsiniz fakat genelde prototip üretirken çok kompleks animasyonlar yerine genel anlamda ekranla ilgili animasyonları üretmek daha mantıklı olacaktır. Örneğin ekrandaki bir menü gözüküp kaybolacak veya soldan alışveriş sepeti gelecek gibi bir animasyon çok daha anlamlı olabilir. Bu gibi durumlarda StoryBoard'lara kıyasla daha kolay kullanılabilen SketchFlow Animation'larından faydalanabilirsiniz.

SketchFlow animasyonlarını yaratmanın yolu.

SkethFlow animasyonlarında tüm ekranın aslında birer State serisi olarak kaydedildiğini düşünebilirsiniz. Ekranın farklı durumları vardır ve bunlar ayrı ayrı kareler olarak kaydedilerek bu kareler arası geçiş süreleri, ivmeleri vs belirlenir. Sonrasında bu animasyon herhangi bir kontrol tarafından hızlıca tetiklenebilir.

SkethFlow animasyonlarını oynatırken.
SkethFlow animasyonlarını oynatırken.

Herhangi bir kontrole sağ tıkladıktan sonra gelen menüde "Play SkethFlow Animation" seçeneğini göreceksiniz. Bu seçenek altında projenizde bulunan animasyonların bir listesi bulunacaktır. İstediğinizi seçerek söz konusu kontrol tarafından animasyonun tetiklenmesini sağlayabilirsiniz.

Prototipi yayınlayalım!

Prototipiniz tamamen hazır olduktan sonra büyük ihtimal ile çalışmanızı müşteriniz ile paylaşmak isteyeceksiniz. Bu gibi bir durumda yardımızda "SketchFlow Player" yetişecek. SketchFlow projelerinin ayrı kenilerine özel player yazılımları var. Bu yazılımlar WPF tarafında ve Silverlight tarafında tamamen aynı gibi gözükse de aslında WPF'te bir EXE olarak Silverlight'ta ise bir web sitesi olarak karşımıza çıkıyorlar. Projenizi bitirdikten sonra Blend içerisinde F5'e basmanız yeterli olacaktır.

SketchFlow Player karşınızda.
SketchFlow Player karşınızda.

Bahsettiğimiz üzere SketchFlow player duruma göre bir Silverlight veya WPF uygulaması olabiliyor. Player içerisinde prototipinizdeki tüm sayfalar gösterilir. Sol üst "Navigate" ekranın sürekli olarak içerisinde bulunan sayfadan hangi sayfalara geçiş yapılabileceği gösterilir. Sol altta ise uygulamanın haritası görülebilir. Tabi ana ekranda ise prototipiniz gösterilir ve herşey tıklanabilir, kullanılabilir durumdadır. İşin en güzel tarafı ise müşteriniz bu player üzerinden projeyi incelerken yorumlar da yapabilir.

Müşteriden gelen yorumlar.
Müşteriden gelen yorumlar.

SketchFlow Player içerisinde rahatlıkla müşteriniz "Feedback" ekranını kullanarak doğrudan proje üzerine yorumlarını ekleyebilir. Bu yorumlar sonrasında Export düğmesi ile bir XML'e alınıp Blend içerisinde de açılabiliyor. "Window" menüsünden "Feedback" kısmını ekrana getirdikten sonra müşterinizden gelen XML'i ekleyerek doğrudan projeniz üzerinde görebilirsiniz.

Müşteri yorumları Blend içerisinde.
Müşteri yorumları Blend içerisinde.

Müşterim ekran görüntülerini teklifin içinde istiyor!

Biliyorum, bazı müşteriler illa ekran görüntülerini ve prototipi kağıt üzerinde görmek ister. Dokümanlamak isterler. Konumuz müşterinin haklı veya haksız olduğu olmadığına göre biz bu işi nasıl çözeriz ona bakalım. Aslında pek bakacak birşey de yok, çünkü işimiz çok kolay. Prototipiniz bittikten sonra "File" menüsünden "Export to Microsoft Word" komutunu verebilirsiniz.

Projemizin detayları bir Word dokümanında.
Projemizin detayları bir Word dokümanında.

Projenizdeki her ekran ayrı birer sayfada ekran görüntüsü ile beraber Word'e aktarılacaktır. Dokümanın ilk sayfasında hem projenizin haritası hem de iç sayfalara dair linkler ve indeks de otomatik olarak hazırlanacaktır. Eh, gönül daha ne ister?

Expression Blend içerisinde SketchFlow hangi amaçla kullanırsanız kullanın müşterilerinizle ilişkinizi kolaylaştıracağı veya bir adım ileriye taşıyacağı kesin.

Hepinize kolay gelsin.

Sunday, September 06, 2009 9:44:39 AM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Expression Blend | Silverlight 3.0 | WPF  | 
 Saturday, September 05, 2009

Silverlight projelerinizde web servisleri kullandığınızda standart olarak "Add Service Reference" komutunu kullanıyoruz. Bu komut ile yaratılan service proxy doğal olarak sürekli yaratıldığı andaki hedefine göre çalışmayı amaçlıyor. Bu noktada da projenin yayına alınması noktasında service reference'ınızı yeni adres ile update etmek zorunda kalabiliyorsunuz.

Bir diğer sorun ise birden çok alan adının yönlendirildiği web sitelerinde olabiliyor. Varsayalım ki hem x.com hem de y.com aslında aynı siteyi açıyorlar ve sizin SL uygulamanıza da service reference x.com adresi üzerinden eklendi. Bu durumda SL uygulamanız x.com'da rahatlıkla çalışırken y.com'da çalışmayacaktır çünkü y.com'daki SL uygulamanız hala servis için x.com'a gitmeye çalışacaktır ve bu da cross-domain request kuralları gereği mümkün olmayacaktır.

Cross-domain'e izin verebiliriz dediğinizi duyar gibiyim fakat aslında gerek yok. Hem bu şekilde farklı alan adlarında yayınlanan sitemizi hem de makinemizdeki test alanı ile production'taki alanı tanıyabilen bir Silverlight uygulaması çok daha iyi olur.

[VB]

    WithEvents RSS As RSSService.rssSoapClient

Diyelim ki yukarıdaki şekilde tanımladığınız bir web servisiniz var. Normalde bu servisi service referecen eklenirkenki adrese hedefleyecektir. Oysa bu web servisini yaratırken kullandığımız constructor'lar arasında bir de parametre alan constructorlar var. İşte tam da o noktaya dokunmamız gerek.

[VB]

        Dim uri As New Uri(App.Current.Host.Source, "../RSS.asmx")

        Dim binding As New BasicHttpBinding(BasicHttpSecurityMode.None)

        binding.MaxReceivedMessageSize = 10000000

        Dim endpoint As New EndpointAddress(uri)

        RSS = New RSSService.rssSoapClient(binding, endpoint)

Yukarıda gördüğünüz kodda hem bir Binding hem de endpoint tanımlıyoruz. Böylece yarattığımız servis hangi binding tipini ve hangi endpointi kullanacağını bizden parametre olarak alacak. Özellikle endpoint demek zaten ulaşmaya çalıştığımız servisin adresi demek.

İlk satırda Uri'mizi yaratırken App.Current.Host.Source ile XAP dosyasının yerinin tam web adresini almış oluyoruz. Bizim örneğimizde web servisi XAP dosyası ile aynı konumda değil bir üst klasörde yer aldığı için bir üst klasörü çıkıp web servisimize ulaşabiliriz.

Binding'imizi de standart olarak yarattıken sonra Endpoint yaratıp parametre olarak Uri'mizi veriyoruz. Son olarak eldeki binding ve endpointler ile servisimizin proxy'sinden bir Client nesnesi alıp proje içerisinde rahatlıkla kullanabiliriz. Böylelikle SL uygulaması her açıldığında bulunduğu adrese bakıp ona göre web servisine yönelip sizin Visual Studio içerisinde service referecen eklerken kullandığınız hedef adresin hiçbir önemi kalmayacaktır.

Hepinize kolay gelsin.

Saturday, September 05, 2009 12:27:06 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Silverlight 3.0  | 
 Friday, September 04, 2009

İster AJAX sitelerinde olsun ister Silverlight bir şekilde istemci tarafında sayfanın manipüle edildiği web uygulamalarındaki dertlerden biri URL'in değişmiyor olması ile beraber tarayıcı geçmişinin de çalışamaması. İlk bakışta belki büyük bir sorun gibi gözükmese de müşterilerinize teslim ettiğiniz bu gibi projelerin sonrasında aldığınız geri dönüşler emin olun sizi bu konuda ciddi şekilde şaşırtabilir.

Navigation API ile sorunlara çözüm....

Silverlight uygulamaları içerisindeki Navigation konseptini çözmeyi hedefleyen Navigation kontrolleri ve API aynı anda yukarıdak bahsettiğimiz URL'in duruma göre değişebilmesi ve hatta istemci taraflı URLReWriting yapılabilmesini de sağlıyor. Bu çerçevede yeni bir Silverlight projesi yaratarak adım adım ilerleyelim.

İlk olarak uygulamanızda Navigation API kullanacaksanız hemen System.Windows.Controls.Navigation assembly'sini referans olarak almanız gerekiyor. Bu assembly içerisinde kullanacağımız sınıflar ve kontroller bulunuyor. Navigation sistemine kendine has bir Frame ve Page yapısına sahip. Uygulamanızda navigasyon uygulamak istediğiniz ana bir sayfa belirledikten sonra bu sayfa içerisinde Frame kontrolünü yerleştiriyorsunuz. Sonrasında bu Frame içerisine farklı Page kontrollerini yükleyebilirsiniz. Aslında bu yapı bizim eski klasik HTML'den alıştığımız IFRAME'e çok benziyor tek farkı özünde tamamen Silvelright içerisinde bir yapı olması.

Yarattığımız yeni Silverlight uygulamasında gerekli DLL'leri referans aldıktan sonra ana sayfa olarak varsayılan ayarlarla gelen MainPage.xaml UserControl'ünü kullanacağız. Bu UserControl içerisine bir Frame kontrolü yerleştirmemiz gerekiyor.

[XAML]

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

            xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"

   mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">

    <Grid x:Name="LayoutRoot">

        <navigation:Frame x:Name="BirFrame" Source="/AnaSayfa.xaml">

        </navigation:Frame>

    </Grid>

</UserControl>

System.Windows.Controls namespace'in eklenen kontrollerden biri olan Frame kontrolü doğrudan System.Windows.Controls.Navigation assembly'si üzerinden geliyor. Gerekli XML namespace'lerini de Usercontrol çapında tanımladıktan sonra rahatlıkla söz konusu namespace üzerinden Frame kontrolünü yaratabiliyoruz. Tabi bu Frame'in ilk açılışta göstereceği Page'in adresini de Source olarak vermeniz gerekiyor.

Tüm bu hikaye içerisinde farkındaysanız bir UserControl bir Page diye iki farklı şeyden bahsediyorum. UserControl'ler bizim normal şartlarda kullandığımız hem ana sayfalarımız hem iç User Controllerimiz vs olabiliyor. Silverlight içerisinde ana uygulama görseli de sonuç olacak bir UserControl. Fakat söz konusu Navigation API olunca bir Frame içerisinde ancak Page kontrollerini gösterebiliyorsunuz. Page kontrollerinin özünde UserControl'lerden bir eksiği yok fazlası var. O nedenle bir UserControl içerisinde yapabildiğiniz herşeyi Page içerisinde yapabileceğinizi unutmayın.

Şimdi gelin bir de basit Page yaratıp adını AnaSayfa.xaml yapalım ki yukarıdaki uygulamamız rahatlıkla çalışsın. Projenizde "Add new item" dediğiniz "Silverlight Page" seçeneği ile karşılaşacaksınız.

[XAML]

<navigation:Page x:Class="SilverlightApplication4.AnaSayfa"

          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"

          xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"

          d:DesignWidth="640" d:DesignHeight="480"

          Title="AnaSayfa Page">

    <Grid x:Name="LayoutRoot">

 

    </Grid>

</navigation:Page>

Gördüğünüz gibi Page kontrolleri de yine System.Windows.Controls.Navigation altından geliyor. Bir Page kontrolünün Title özelliği uygulama tarayıcıda açıkken tarayıcının işletim sistemine ait üst çubuğunda gözükecek olan içeriği tanımlıyor. Böylece Frame içerisindeki Page'ler değiştikçe her Page kendi Title bilgisini tarayıcının çubuğuna aktarabiliyor. Sadece bu kadarla kalınmıyor aslında.

Hatırlarsanız ana sayfadaki Frame kontrolümüze Source olarak AnaSayfa.xaml demiştik. Buradan yola çıkarak Frame kontrolü ilk açıldığında Silverlight projeniz içerisinde AnaSayfa.xaml'ı yükleyecektir. Sonrasında başka bir sayfaya yönlendirme yapmak isterseniz bunu ana sayfanızdaki bir HyperlinkButton ile rahatlıkla yapabilirsiniz.

[XAML]

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

            xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"

   mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">

    <Grid x:Name="LayoutRoot">

        <StackPanel>

            <navigation:Frame Height="300" x:Name="BirFrame" Source="/AnaSayfa.xaml">

            </navigation:Frame>

            <HyperlinkButton x:Name="Link1" NavigateUri="/BaskaSayfa.xaml" TargetName="BirFrame" Content="TIKLA"/>

        </StackPanel>

    </Grid>

</UserControl>

Yukarıdaki örnekte gördüğünüz HyperlinkButton'un TargetName özelliği çok önemli. Bu özelliğe aktarılan isim bizim Frame kontrolünün ismi olduğu için kontrol içerisindeki linkin doğrudan Silverlight içerisindeki Frame'e yönlendirileceği anlaşılabiliyor. HyperlinkButton'ın NavigateUri özelliğinde ise Silvelright uygulaması içerisindeki bir diğer Page kontrolünün adresi bulunuyor. Bu adresler Silverlight uygulamasının rootuna göre veriliyor. Yukarıdaki HyperlinkButton'a tıklandığıda otomatik olarak hedef Page Frame içerisinde yüklenecektir.

Tüm bu işlemler gerçekleşirken Navigation API sizin yerinize tarayıcının adres çubuğunda da gerekli değişiklikleri otomatik olarak yapacaktır.

Uygulama ilk açıldığında adresi:
http://localhost:2593/SilverlightApplication4TestPage.html#/AnaSayfa.xaml

HyperlinkButton'a tıkladıktan sonraki adres:
http://localhost:2593/SilverlightApplication4TestPage.html#/BaskaSayfa.xaml

Yukarıdaki adresler arasındaki farklarda da görebileceğiniz üzere Frame içerisine yüklenen her kontrolün adresi tarayıcının da adres çubuğunda bulunuyor. Bu adresler birer Fragment olarak implemente edildiği için tarayıcı herhangi bir şekilde sayfaya refresh atmasa da bu değişiklikleri tarayıcı geçmişine kaydedebilecektir. Böylece rahatlıkla kullanıcılar tarayıcıların "İleri" ve "Geri" düğmeleri kullanarak uygulamamız içerisinde gezebilecekler. Bu sistemin bir diğer avantajı da kullanıcıların uygulamamızla ilgili farklı sayfaların linklerini alarak arkadaşlarına gönderebilecek olmaları. Bu adresler eğer kopyalanıp farklı bir makineden farklı bir tarayıcıda doğrudan çalıştırılırsa Navigation API uygun Page'i Frame içerisine otomatik olarak yükleyecektir.

Page'lere parametre nasıl göndeririz?

Belki de Page'leri Frame içerisine yüklerken parametreler göndermek isteyeceksiniz. Hatta belki de bir Page'in kendi içerisinde bulunduğu Frame'e başka bir Page'i yüklemesini söyleyebilmesini isteyeceksiniz. Biraz karışık oldu değil mi? Gelin tek tek bu soruları da cevaplayalım. İlk olarak ana sayfadan değil de bir Page'in kendi içinden navigasyon sağlamanın yoluna bakalım.

[VB]

    Private Sub btntikla_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btntikla.Click

        Me.NavigationService.Navigate(New Uri(String.Format("/Urun.xaml?ID={0}", 1), UriKind.Relative))

    End Sub

Yukarıdaki kod doğrudan Frame içerisinde bir Page'in içindeki düğmeye bağlı. Ana sayfada Frame'e ulaşarak "sen bu adresi aç" demek kolay fakat Frame içerisinde bir Page'i kendi sahibi olan Frame'e böyle bir komut gönderebilmesi için her Page içerisinde otomatik olarak yer alan NavigationService'i kullanmamız gerekiyor. NavigationService üzerinden çağırdığınız Navigate metoduna her zamanki gibi Silverlight uygulamanız içerisindeki bir başka Page'in adresini verebilirsiniz.

Yukarıda yönlendirme yaptığımız adrese dikkat ettiyseniz sonunda bir parametre var. Sanki normal bir web sitesinde yönlendirme yaparmış gibi QueryString üzerinden parametre gönderebiliyoruz. Tabi söz konusu Urun.Xaml adındaki Page'in de bu parametreyi okuyabilmesi gerekiyor.

[VB]

Partial Public Class Urun

    Inherits Page

 

    Public Sub New()

        InitializeComponent()

    End Sub

 

    'Executes when the user navigates to this page.

    Protected Overrides Sub OnNavigatedTo(ByVal e As System.Windows.Navigation.NavigationEventArgs)

        If Me.NavigationContext.QueryString.ContainsKey("ID") Then

            GelenParametre.Text = Me.NavigationContext.QueryString("ID")

        End If

    End Sub

 

End Class

Urun.xaml adındaki Page'imizin için otomatik olarak gelen OnNavigateTo metodu söz konusu Page ekrana geldiğinde çalışacaktır. Kontrol ekrana geldiğinde hemen gidip kendisine QueryString üzerinden bir ID gönderilip gönderilmediğini kontrol etmemiz gerek. Bunun için yine her Page'de bulunan NavigationContext'i kullanıyoruz.

NavigationContext'in altındaki QueryString nesnesinin ContainsKey özelliği ile ID parametresinin gelip gelmediğini kontrol ettikten sonra QueryString Dictionary'sine doğrudan elimizdeki key'i verip değeri alabiliyoruz. Böylece hazırladığınız bir Page'i Frame içerisine yüklerken ona bir parametre gönderip onun da parametreye uygun verileri göstermesini veya uygun işlemleri yapmasını sağlayabilirsiniz.

UriMapping işlemleri.

Şu ana kadar yaptığımız tüm işlemlerde verdiğimiz Frame adresleri otomatik olarak adres çubuğuna yazıldı. Bu durumun birçok avantajı var. Fakat ortada hoş olmayan bir konu var ki o da herşeyin alenen ve çirkin bir şekilde adres çubuğunda gözüküyor olması. Örneğin aşağıdaki adres bizim Urun.xaml'a parametre gönderdiğimize oluşan adres.

http://localhost:2593/SilverlightApplication4TestPage.html#/Urun.xaml?ID=1

Bu sorun tanıdık geliyor değil mi? Aslında biz bu sorunu yıllarda web uygulamalarında da yaşadık ve UrlReWriting kullanarak daha hoş URL'lere sahip olmayı öğrendik. İşte aynı sistemi Navigation API içerisinde de UriMapper kontrolleri ile sağlayabiliyorsunuz.

[XAML]

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

            xmlns:uriMapper="clr-namespace:System.Windows.Navigation;assembly=System.Windows.Controls.Navigation"

            xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"

   mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">

    <Grid x:Name="LayoutRoot">

        <StackPanel>

            <navigation:Frame Height="300" x:Name="BirFrame" Source="/AnaSayfa">

                <navigation:Frame.UriMapper>

                    <uriMapper:UriMapper>

                        <uriMapper:UriMapping Uri="" MappedUri="/AnaSayfa.xaml"/>

                        <uriMapper:UriMapping Uri="/{SayfaAdi}" MappedUri="/{SayfaAdi}.xaml"/>

                    </uriMapper:UriMapper>

                </navigation:Frame.UriMapper>

            </navigation:Frame>

            <HyperlinkButton x:Name="Link1" NavigateUri="/BaskaSayfa" TargetName="BirFrame" Content="TIKLA"/>

        </StackPanel>

    </Grid>

</UserControl>

uriMapper kontrolünü kullanabilmek için ayrı bir XAML namespace tanımlayarak bu sefer de System.Windows.Controls.Navigation'ı import etmemiz gerekiyor. Sonrasında uriMapper kontrolünü yaratarak doğrudan Frame'in UriMapper özelliğine verebiliyoruz. UriMapper kendi içerisinde UriMapping kontrolleri kullanıyor.  Her uriMapping'in bir Uri ve bir de MappedUri özelliği var.

[XAML]

                        <uriMapper:UriMapping Uri="" MappedUri="/AnaSayfa.xaml"/>

                        <uriMapper:UriMapping Uri="/{SayfaAdi}" MappedUri="/{SayfaAdi}.xaml"/>

Örneğimizdeki bu iki kodu incelersek ilkinde boş bir adres geldiğinde açılarak kontrolün adresine yönlendirildiğini görebiliriz. İkincisinde ise bir değişken kullanılmış durumda. SayfaAdi adını verdiğimiz bu değişkeni gelen adresten alıp sonuna .xaml uzantısını ekleyip yönlendiriyoruz. Böylece bir önceki adımdaki URL'lerimiz ile şu ankiler çok daha farklı olabiliyor.

Uygulama ilk açıldığında adresi:
http://localhost:2593/SilverlightApplication4TestPage.html#/AnaSayfa

HyperlinkButton'a tıkladıktan sonraki adres:
http://localhost:2593/SilverlightApplication4TestPage.html#/BaskaSayfa

Eğer kodumuzda dikkat ettiyseniz artık HyperlinkButton'un NavigateUri'sinde BaskaSayfa.xaml yazmıyor, doğrudan sadece BaskaSayfa yazıyor. Artık sondaki .xaml uzantıları Mapper tarafından halledilecek. Bu şekilde QueryString parametrelerini de map edebilirsiniz.

[XAML]

                    <uriMapper:UriMapper>

                        <uriMapper:UriMapping Uri="" MappedUri="/AnaSayfa.xaml"/>

                        <uriMapper:UriMapping Uri="/Urun/{ID}" MappedUri="/Urun.xaml?ID={ID}"/>

                        <uriMapper:UriMapping Uri="/{SayfaAdi}" MappedUri="/{SayfaAdi}.xaml"/>                       

                    </uriMapper:UriMapper>

Yukarıdaki ek Mapping sayesinde artık Urun/2 şeklinde gelen adresler doğrudan Urun.xaml?ID=2 şekline dönüştürülecek.

UriMapping öncesi:
http://localhost:2593/SilverlightApplication4TestPage.html#/Urun.xaml?ID=2

UriMapping sonrası:
http://localhost:2593/SilverlightApplication4TestPage.html#/Urun/2

Gördüğünüz gibi Naviation API gerçekten kuvvetli bir altyapı sunuyor. Tüm bu altyapının her noktasına ayrı ayrı müdahale edebiliyorsunuz. Mantık olarak çok yabancı olmadığımız bir kullanım şekli olduğu da kesin.

Hepinize kolay gelsin.

Friday, September 04, 2009 5:44:56 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Silverlight 3.0  | 
 Thursday, September 03, 2009

İster Silverlight tarafında olsun ister WPF teknolojinin ve araçların sürekli bahsedilen en önemli özelliklerinden biri tasarımcı ile yazılımcı arasındaki ilişkiyi düzenlediği yönünde. Tabi ki eskisine kıyasla çok sayıda artı özellik bu iki profil arasındaki "kavgaların" azalmasını sağlıyor fakat diğer yandan özellikle yazılımcının da Expression Blend uyumlu binding yapılabilir nesnelerini tanımlayabiliyor olması çok önemli. Tabi aynı derecede bir tasarımcının da Blend içerisinden yola çıkarak programcının arka planda kendisine sağladığı CLR nesnelerine ulaşabilmesi şart.

Tüm bu senaryo ile ilgili ufak bir örnek yapacağımız bu yazımızda ilk olarak bir programcı olarak tasarımcının uygulama tasarımında istediği gibi işlevsellikleri ayarlayabileceği ve binding'leri set ederek hangi datanın nerede gözükeceğine karar verebileceği bir altyapı oluşturacağız. Sonrasonda tasarımcı profili ile Expression Blend'e geçerek arka tarafta programcının hazırlamış olduğu nesneleri alıp uygulama arayüzünde istediğimiz yerlere yerleştireceğiz.

Visual Studio tarafından başlayalım!

Varsayalım ki elinizde bir Insan listesi var ve bunu bir şekilde tasarımcının ellerine aktarmak istiyorsunuz. Sizin bir programcı olarak yapmanız gereken bu listeyi bir veri kaynağından alıp (bu ister SQL ister başka bir kaynak olabilir) Expression Blend tarafında kullanılabilir hale getirmek. Yani verinizin User Interface tarafında kullanılabilmesini sağlamanız gerekiyor. İlk olarak gelin basit bir şekilde Insan nesnemizi tanımlayalım.

[VB]

Public Class Insan

 

    Private _Soyadi As String

    Public Property Soyadi() As String

        Get

            Return _Soyadi

        End Get

        Set(ByVal value As String)

            _Soyadi = value

        End Set

    End Property

 

 

    Private _Adi As String

    Public Property Adi() As String

        Get

            Return _Adi

        End Get

        Set(ByVal value As String)

            _Adi = value

        End Set

    End Property

 

End Class

Gördüğünüz gibi nesnemizin şimdilik iki basit özelliği var. Amacımız bu nesneden birden çok sayıda yaratıp bir liste olarak tasarımcıya aktarmak. Bunun için ayrı bir sınıf tanımlamamız uygun olur. Söz konusu sınıf içerisinde de ObservableCollection dönecek bir metod yer alacak.

[VB]

Public Class AllData

 

    Private _All As New System.Collections.ObjectModel.ObservableCollection(Of Insan)

    Public Property All() As System.Collections.ObjectModel.ObservableCollection(Of Insan)

        Get

            Return _All

        End Get

        Set(ByVal value As System.Collections.ObjectModel.ObservableCollection(Of Insan))

            _All = value

        End Set

    End Property

 

    Sub New()

        _All.Add(New Insan() With {.Adi = "1asdasd", .Soyadi = "2222"})

        _All.Add(New Insan() With {.Adi = "2asdasd", .Soyadi = "4222"})

        _All.Add(New Insan() With {.Adi = "3asdasd", .Soyadi = "5222"})

        _All.Add(New Insan() With {.Adi = "4asdasd", .Soyadi = "6222"})

    End Sub

 

End Class

AllData adındaki bu sınıfın içerisinde All adında da bir Property var. Bu property geriye bir ObservableCollection döndürüyor. Bildiğiniz üzere ObservableCollection'lar TwoWay binding destekleyen collection nesneleridir. Yani görsel arayüzdeki değer değişiklikleri de otomatik olarak arka plandaki nesneye yansır. Expression Blend'de bu nesne bind edildiğinde Blend nesneyi çağırmadan önce bir Instance yani kopyasını alacaktır. İşte tam da bu kopyayı yaratırken sınıfımızın constructor'ında datamızı ObservableCollection'ımıza ekleyebiliriz. Kod yönetimi açısından tüm bu kodları harici bir VB veya C# dosyasında yazmanızda fayda var.

İşimiz bu kadar artık bu veriyi gösterme işi tasarımcının işi. Expression Blend tarafına geçerek geri kalanı bir tasarımcı profili ile yapalım.

Expression Blend tarafına geçiyoruz!

Aynı projeyi Expression Blend ile açtıktan sonra sağ barda "Data" tabını bulabilirsiniz. Data tabında "Add Live Data Source" komutunu verdikten sonra "Define New Object Data Source" diyebilirsiniz.

Expression Blend içerisinde Data Source'umuzu tanımlıyoruz.
Expression Blend içerisinde Data Source'umuzu tanımlıyoruz.

Karşınızda çıkacak ekranda AllData adındaki sınıfımızı seçmeniz yeni DataSource'u yaratmanız için yeterli olacaktır. Artık sıra geldi bu DataSource içerisinde hangi Property'yi nasıl kullanacağınıza.

Nesnelerimiz Expression Blend tarafından algılandı.
Nesnelerimiz Expression Blend tarafından algılandı.

Artık Data tabındaki veri kaynağı ile ilgili tüm detayları görebiliriz. Tasarımcı olarak Data tabından veriyi sahneye koymak için birkaç yolumuz var. Bunlardan ilki liste görünüşünü kullanmak. Şu an yukarıdaki ekran görüntüsünde List Mode gözüküyor. Bu durumda ObservableCollection'ı alıp sahneye bıraktığınızda otomatik olarak bir ListBox yaratılacak ve veri ListBox'a bağlanacaktır.

Sürükle, bırak! Ve karşında ListBox!
Sürükle, bırak! Ve karşında ListBox!

Artık bu ListBox'ın ItemTemplate'inin vs tasarımını değiştirmek tabi ki makalemizin sınırları dışında. Fakat unutmamak gerek ki artık canlı veriye bağlı ve tamamiyle tasarımı özelleştirilebilir bir ListBox tasarımcımızın ellerinde. Ayrıca tek seçenek ListBox da değil. Eğer sahnede bir Grid veya başka bir kontrol olsaydı doğrudan Collection'ı onun üzerine sürükleyip bırakarak da Data Binding işlemi rahatlıkla yapılabilirdi.

Details Mode karşınızda.
Details Mode karşınızda.

List Mode'un yanı sıra Blend içerisinde kullanabileceğimiz bir diğer Data modu da Details Mode olarak karşımıza çıkıyor. Bu moddayken veri kaynağından nesneleri ancak Property'ler olarak sürükleyip bırakabilirsiniz çünkü artık amacınız bir liste göstermek değil. Artık amacımız nesnelerin detaylarınız göstermek. Tek tek her Property'yi sahnedek ayrı nesnelere Bind edebileceğiniz gibi nesnelerin yaratılmasını Blend'e de bırakabilirsiniz. Örneğin gelin hem Adi hem de Soyadi Property'lerini seçerek sahneye sürükleyip bırakalım.

Details Mode ile otomatik detay görünümü yaratabilirsiniz.
Details Mode ile otomatik detay görünümü yaratabilirsiniz.

Gördüğünüz gibi sürükle bırak sonrasında otomatik olarak bir Grid yaratılarak içerisinde hem Property isimlerini taşıyan hem de değerleri taşıyan birer TextBlock yerleştirildi. İşin en güzel tarafı binding işlemini aynı nesne kopyasına yaptığımız için ListBox içerisinde hangi nesne seçilirse onun detaylarının bu Grid içerisinde gösterilecek olması. Yani tüm işlevsellik tamamlanmış durumda!

Arka planda Blend neler yaptı?

Blend aslında basit bir şekilde Binding mekanizmalarının tasarımcılar tarafında rahatlıkla kullanılabilmesini sağlıyor. Maalesef şu an için bu mekanizmaları yazılımcılar olarak bizim elle yazmaktan başka şansımız ki. Visual Studio 2010 ile bu durumda toparlanacak ve bizler de tüm bu ayarları yapabileceğimiz menülere sahip olacağız.

XAML tarafına bir göz atacak olursak dikkatimizi çeken birkaç nokta olabilir.

[XAML]

<UserControl

   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" xmlns:local="clr-namespace:SilverlightApplication2" xmlns:dataFormToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit" x:Class="SilverlightApplication2.MainPage"

   d:DesignWidth="640" d:DesignHeight="480">

    <UserControl.Resources>

        <local:AllData x:Key="AllDataDataSource" d:IsDataSource="True"/>

        <DataTemplate x:Key="InsanTemplate">

            <StackPanel>

                <TextBlock Text="{Binding Adi}"/>

                <TextBlock Text="{Binding Soyadi}"/>

            </StackPanel>

        </DataTemplate>

    </UserControl.Resources>

    <Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource AllDataDataSource}}" >

        <ListBox x:Name="listBox" Margin="47,96,0,84" ItemTemplate="{StaticResource InsanTemplate}" ItemsSource="{Binding All}" HorizontalAlignment="Left" Width="200"/>

        <Grid DataContext="{Binding SelectedItem, ElementName=listBox}" Margin="310,94,76,186" d:DataContext="{Binding All[0]}" Background="White">

            <TextBlock HorizontalAlignment="Left" VerticalAlignment="Top" Width="100" Height="16" Text="Adi"/>

            <TextBlock Text="{Binding Adi}" HorizontalAlignment="Left" VerticalAlignment="Top" Width="150" Height="16" Margin="104,0,0,0"/>

            <TextBlock HorizontalAlignment="Left" VerticalAlignment="Top" Width="100" Height="16" Margin="0,20,0,0" Text="Soyadi"/>

            <TextBlock Text="{Binding Soyadi}" HorizontalAlignment="Left" VerticalAlignment="Top" Width="150" Height="16" Margin="104,20,0,0"/>

        </Grid>

    </Grid>

</UserControl>

Yukarıda tüm uygulamanın XAML kodunu inceleyebilirsiniz. Özellikle renkli kısımlara bakacak olursak Blend'in neler yapmaya çalıştığını net bir şekilde görebiliriz. İlk olarak en üstte bizim arkadaki uygulamamızın bir XML namespace olarak Import edilmiş. Sonra söz konusu yerden AllData sınıfından bir instance alınıp Resrouce'lar arasına AllDataDataSource adı ile koyulmuş. Bu DataSource uygulama genelinde Grid'e bağlanmış. Grid içerisinde ListBox ise All metoduna bağlanmış. ListBox aldığı her Insan nesnesini yine Resource'lar içerisinde tanımlı InstanTemplate ile gösterebiliyor. InsanTemplate içerisinde ise her Insan'ın Property'leri uygun kontrollere bağlanmış durumda. Son olarak detayları gösterecek olan nesnelerin bulunduğu Grid'in DataContext'i bizim ListBox'ın SelectedItem'ına element binding ile bağlanmış. SelectedItem zaten bir Insan nesnesi olarak geleceğine göre Grid içerisindeki kontrolleri de doğrudan bu Insan nesnesinin Property'lerine bağlayabiliriz. Son ufak bir detay ise Grid'in DataContext'inin yanı sıra bir de d:DataContext diye bir Property'sinin All listesindeki Index numarası sıfır olan kayda bind edilmiş olması. d: XML namespace'i tamamen design view için gerekli özellikleri tanımlar. Yani tasarımcı Blend içerisinde uygulamayı görürken ve uygulama çalışmazken bu Grid'in doğrudan All listesindeki ilk nesneyi göstermesini sağlamak için Blend böyle bir kod eklemiş durumda.

Sonuç olarak gördüğünüz gibi aslında her iki tarafın da kurallara uyması halinde Expression Blend ve Visual Studio ile beraber tasarımcılar ve yazılımcıların beraber rahatça çalışması olması.

Hepinize kolay gelsin.

Thursday, September 03, 2009 5:43:47 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Expression Blend | Silverlight 3.0  | 
 Wednesday, September 02, 2009

DVD veya CD arayüzleri hala bilgisayar dünyasında ciddi bir yere sahip. İçeriğin paylaşımı adında internet ciddi bir yol almış olsa da hala CD ve DVD medyalarını kullanarak içeriğin dağıtımı bazı senaryolarda çok daha işlevsel olabiliyor. Bu gibi durumlarda doğal olarak bir arayüz ile söz konusu içeriği bir CD veya DVD içerisinde sunmak gerekiyor. Peki bu arayüzü geliştirmek için hangi teknolojileri kullanabiliriz?

Gelin beraberce sıralayalım;

HTML - Pufff!
Flash - ActionScript? Pufff!
WPF - .NET Framework yükletmek? Puff!
Silverlight - Oley! İşte bu!

Şaka bir yana :) aslında bir CD veya DVD arayüzü geliştirirken ihtiyacımız olan şey hem arayüz içerisinde her tür data işlemini yapabilimek hem de güzel bir arayüz tasarlayabilmektir. Varsayalım bir ürün kataloğu yapıyorsunuz, büyük ihtimal ile söz konusu ürünler arasında arayüzde arama da yapılabilmelidir. İşte ufak da olsa bir programlama noktası karşımızda. Olabildiğince rahat programlamak ve bildiğimiz bir dili kullanmak önemli. HTML ile JavaScript veya Flash ile ActionScript bir .NET developer için çok da ferah bir gelecek vaadetmeyecektir. Ayrıca HTML ile zaten güzel bir arayüz, zengin bir kullanıcı deneyimi sağlamak da pek mümkün değil.

Alternatiflerden bir diğeri WPF olabilir ve aslında çok da güzel olur. Fakat bu sefer de .NET Framework gereksinimi ile karşı karşıya kalıyoruz. Eh hadi DVD'nin içine koyalım ama insanların bir DVD arayüzünü görebilmek için .NET Framework kurup (en az 5dk) makinelerine restart atacaklarından emin misiniz?

Tüm bu senaryonda en güzeli Silverlight! Hem bildiğimiz VB, C# hem Expression Blend ile süper kullanıcı deneyimi hem de 4MB Runtime ve 5 saniye yükleme süresi. Hem MAC desteği de var! Eh gönül daha ne ister?

Tamam da şimdi tarayıcıda açtırmak da pek hoş olmuyor!

Kesinlikle! Bir CD veya DVD arayüzünün tarayıcı içerisinde açılması benim en uyuz olduğum noktalardan birisidir özünde. Neden mi? Çünkü ister istemez bir web sitesi hissiyatı yaratıyorsunuz ve en azından bende "çok uğraşılmamış" efekti oluşturuyor. Yani "adamlar web sitesi yapıp koymuş DVD'ye!" gibi abuk bir yorumun gelmesi olasılığı ciddi yüksek. O nedenle tarayıcıdan kurtulmamız gerek.

HTML Application = HTA!

HTA'larla ilk tanışdığımda "İşte bu" demiştim :) Sanırım SQL 2000'in yükleme CD'sinin arayüzünde görmüştüm. Arayüz ilgimi çekmişti çünkü bir tarayıcı gibi içinde HTML olduğunu hissetmiş fakat ortada bir tarayıcı görmemenin de şaşkınlığını yaşamıştım. Sonra biraz CD'yi karıştırınca HTA'larla tanıştım. HTA aslında HTML vs bir scripting dili ile yazılabilir uygulamalar şeklinde tanımlanabilir. Bu uygulamalar "Microsoft HTML Applicatio Host" adında özel bir uygulama tarafından Windows içerisinde host edilir. Arka planda Internet Explorer kullanılır fakat kullanıcılar bunu görmez. HTA uzantılı bir dosya rahatlıkla Notepad ile açılabilen birer HTML dosyasıdır aslında.

[HTA]

<HTML>

  <head>

  <link href="img/styles.css" type="text/css" rel="stylesheet" />

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

    <TITLE>

      Internet Explorer 8 -DVD

    </TITLE>

 

    <HTA:APPLICATION

     APPLICATIONNAME="IE8DVD" 

     MAXIMIZEBUTTON="no"

     MINIMIZEBUTTON="no"

     SINGLEINSTANCE="yes"

     ICON="setup.ico"

     SCROLL="no" />

  </HEAD>

 

<body style="margin:0px;">

 

    <script for="window" event="onload">

   window.resizeTo(820,640);

   window.moveTo((screen.width - 820) / 2, (screen.height - 640) / 2);

</script>

</body>

</html>

Yukarıda gördüğünüz kod basit bir HTA dosyasının içeriği. Aslında içerisinde pek birşey yok. İlk anlamamız gereken şey aslında bir HTML ile çalışıyor olduğumuz. Tabi biz arayüzü oluşturmak için HTML değil Silverlight kullanacağız. Önemli olan ilk şey tabi ki dokümanın TITLE kısmını ayarlamak çünkü bu uygulamanızın açılan penceresinin başlığı olacak. Aşağıda gördükleriniz ise uygulama parametreleri.

[HTA]

    <HTA:APPLICATION

     APPLICATIONNAME="IE8DVD" 

     MAXIMIZEBUTTON="no"

     MINIMIZEBUTTON="no"

     SINGLEINSTANCE="yes"

     ICON="setup.ico"

     SCROLL="no" />

Uygulamanın açılacağı pencerede pencereyi büyütme, küçültme vs gibi düğmeleri isteyip istemediğinizi belirtebilir hatta uygulamanın tek oturumunun olmasını da yukarıdaki parametreler ile sağlayabilirsiniz. Uygulama ikonunu da ayarladıktan sonra önemli noktalardan biri Scroll özelliği NO şeklinde set etmek. Malum biz bu HTML içerisinde arayüz olarak Silverlight koyacağımız için zaten gerekli scroll işlemlerini de SL içerisinde halledebiliriz.

[HTA]

    <script for="window" event="onload">

   window.resizeTo(820,640);

   window.moveTo((screen.width - 820) / 2, (screen.height - 640) / 2);

</script>

Yukarda basit bir JavaScript kodu görüyorsunuz. Bu kod uygulamanızın penceresinin boyutunu ayarlayacağı gibi pencerenin de ekranın tam ortasında gözükmesini sağlayacaktır. HTA'lar içerisinde hem JavaScript hem VBScript kullanabilirsiniz. Her script ait olduğu nesneye for özelliği ve ait olduğu nesnenin hangi event'ını dinleyeceğine de event parametresi ile bağlanır.

Bu noktadan sonra tabi ki sizin ilk olarak Silverlight uygulamanızı hazırlamanız gerek. Arayüz olarak kullanılacak Silverlight uygulaması hazırlandıktan sonra bu HTA içerisine uygun şekilde yerleştirilebilmesi çok önemli.

[HTA]

<HTML>

  <head>

  <link href="img/styles.css" type="text/css" rel="stylesheet" />

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

    <TITLE>

      Internet Explorer 8 -DVD

    </TITLE>

 

    <HTA:APPLICATION

     APPLICATIONNAME="IE8DVD" 

     MAXIMIZEBUTTON="no"

     MINIMIZEBUTTON="no"

     SINGLEINSTANCE="yes"

     ICON="setup.ico"

     SCROLL="no" />

  </HEAD>

 

<body style="margin:0px;">

    <div id="silverlightControlHost">

        <object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">

            <param name="source" value="ClientBin/Interface.xap"/>

            <param name="background" value="white" />

            <param name="minRuntimeVersion" value="2.0.31005.0" />

            <param name="autoUpgrade" value="true" />

            <img style="cursor:pointer;" ID="SLInstall" border="0" src="images/install.jpg" />

        </object>

    </div>

 

    <script for="window" event="onload">

   window.resizeTo(820,640);

   window.moveTo((screen.width - 820) / 2, (screen.height - 640) / 2);

</script>

     <SCRIPT FOR="SLInstall" EVENT="onclick" LANGUAGE="VBScript">

      Dim objShell

      Dim lngReturn

 

      on error resume next

 

      set objShell = CreateObject( "WScript.Shell" )

 

        lngReturn = objShell.Run ("Silverlight.2.0.exe", 4, 1)

 

      set objShell = Nothing

      document.execCommand("Refresh")

 

    </SCRIPT>

</body>

</html>

İşte esas noktaya geldik. Yukarı gördüğünüz OBJET tagları klasik bir SL uygulamasının bir HTML sayfaya yerleştirildiği taglardan farklı değil. Bir tek minik fark var o da OBJECT tagları içindeki HTML kodu! Hatırlarsanız tarayıcılar eğer OBJECT taglarını render edemezse içlerindeki HTML'i gösteriyorlardı. Bu gibi bir durumda Silverlight hedef makinede yüklü değilse bu demektir de tarayıcı OBJECT taglarını render edemeyecek bir OBJECT içerisindeki HTML'i kullanıcıya gösterecek.

[HTML]

      <img style="cursor:pointer;" ID="SLInstall" border="0" src="images/install.jpg" />

Yukarıdaki şekilde güzel bir resim Silverlight yüklü değilken gösterilecek resim olarak seçilebilir. Tabi bu resmin tasarımında kullanıcıya uygun mesajı bir metin olarak göstermeniz şart. Diğer yandan bu resim nesnesinin fare ile üzerine geldindiğinde el işareti gösterilmesi için uygun CSS'in kullanılması da gerekiyor. Son olarak bu IMG'ye bir de ID veriyoruz çünkü bu resme tıklandığında Silverlight Runtime yüklemesini başlatmalıyız!

[VBScript]

     <SCRIPT FOR="SLInstall" EVENT="onclick" LANGUAGE="VBScript">

      Dim objShell

      Dim lngReturn

 

      on error resume next

 

      set objShell = CreateObject( "WScript.Shell" )

 

        lngReturn = objShell.Run ("Silverlight.2.0.exe", 4, 1)

 

      set objShell = Nothing

      document.execCommand("Refresh")

 

    </SCRIPT>

Yukarıda bir VBScript kodu görüyorsunuz. Silverlight Runtime yüklemesine ait EXE dosyasının HTA ile aynı klasöre koyduktan sonra uygun zamanda yüklemeyi başlatmak şart. Bunun için bir Shell nesnesi yaratarak yüklemeyi başlatabiliyoruz. Yükleme bitince de sayfaya Refresh atıyoruz böylece kullanıcı yüklemeyi yaptıktan sonra ekranı kapatıp açmadan hemen arayüzü görebiliyor. Bu scriptin FOR ve EVENT özelliklerine bakarsanız bizim bir önceki adımda yarattığımız IMG nesnesinin onclick durumunda çalışacağını görebilirsiniz.

Diskten birşey çalıştırmak istersek?

Biliyorsunuz Silverlight'ın çalıştığı makinedeki diske erişimi yok. Bunun nedeni Silverlight'ın çalıştığı yer olan tarayıcı içerisindeki alandan yani bir web sitesinden de hedef makinedeki diske ulaşamıyor olmamız. HTA'lar bu konuda biraz farklılar. HTA'larda rahatlıkla diske erişebilirsiniz. Aslında bir önceki örnekte biz bu işi zaten yapmadık mı? HTA içerisinden gidip Silverlight Runtime'ı yüklemek demek harici bir EXE'yi çalıştırmak demek değil mi? Aynen öyle. Peki biz bunu Silverlight içerisinden nasıl yapabiliriz?

Silverlight içerisinde çıkıp VBScript'e ulaşıp, parametre göndererek VBScript ile de diske erişebiliriz.

[VBScript]

     <SCRIPT LANGUAGE="VBScript">

             sub Getir(byval address)

      Dim objShell

      Dim lngReturn

 

      on error resume next

 

      set objShell = CreateObject( "WScript.Shell" )

 

        lngReturn = objShell.Run (address, 4, 1)

 

      set objShell = Nothing

            end sub

    </SCRIPT>

Yukarıda gördüğünüz VBScript metodu tek bir parametre alarak Shell nesnesi yaratıp parametreden gelen adresi diskte çalıştırmaya çalışıyor. Bu durumda biz bu metodu Silverlight tarafından çağırıp uygun parametreyi verebilirsek işlem tamamlanmış demektir.

[VB]

    Private Sub btnSource_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnSource.Click

        System.Windows.Browser.HtmlPage.Window.Invoke("Getir", New String("samples/Hedehodo.exe"))

    End Sub

İşte bu kadar basit. Silverlight'tan DOM'a çıkarak Getir adındaki metodumuzu çalıştırıp bir de String parametre gönderiyoruz. Söz konusu String parametre aslında çalıştırmak istediğimiz uygulamanın HTA dosyasının bulunduğu yere göre relative Uri'sini içeriyor. Böylece bu parametre VBScript'e aktarılacak, oradan da Shell üzerinden çalıştırılabilecek.

Süper değil mi?

Şimdi yapabileceklerinizi bir düşünün :) Silverlight uygulaması içerisinde yapabildiğiniz herşey bir anda bir DVD arayüzüne dönüştürülebiliyor. SaveFileDialog, OpenFileDialog, Socket bağlantılar!, Desktop moduna geçip makineye anında yükleme yapabilmek! ve daha bir çok Silverlight özelliğini bir anda kullanabilir hale geliyorsunuz! Bu gerçekten muhteşem!

Wednesday, September 02, 2009 5:39:03 PM (GTB Standard Time, UTC+02:00)  #    Comments [1]   Silverlight | Silverlight 2.0 | Silverlight 3.0  | 
 Tuesday, September 01, 2009

Son beş gündür KKTC'deydim. "Silverlight Code Camp" adında EESTEC Famagusta'nın organizasyonundaki etkinlikte gönüllü eğitmen olarak yer aldım. Bu süreçte tabi ki çok da eğleceli bir beş gün geçirme şansım oldu.

Silverlight Code Camp, KKTC
Silverlight Code Camp, KKTC

Farklı ülkelerden katılımcıların bulunduğu bu uluslararası eğitimde özellikle katkısı olan sevgili Kubilay Özışık'a çok teşekkür ediyorum. Ayrıca tüm organizasyon ekibi bence süperdiniz! Bir başka etkinliklte ;) görüşmek üzere.

Tuesday, September 01, 2009 12:01:22 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Silverlight 3.0  | 
 Sunday, August 23, 2009

Blend içerisinde kullanılabilecek Behavior çeşitlerinden biri de Trigger'lı Behavior'lar. Bu tip Behavior'lara genelde Action da denebiliyor. Normal Behavior'lardan farklı olarak Trigger'lar da kendi içlerinde yaratılırken Inherit ettikleri sınıfa göre değişebilirler. Bu yazımızda TriggeredAction Behavior'larına göz atacağız.

Ne işe yarar?

TriggeredAction behaviorları sonuç itibari ile normal Behavior'lar gibi bir aksyon almayı hedefler fakat bu aksyon yine kullanıcı tarafından belirlenen bir durumu takiben işleme alınır. Normal şartlarda bir TriggeredAction kendi Parent kontrolünü hedef alarak onun herhangi bir event'ı çalıştığında çalışacak şekilde kenarda durur.

Örneğimizde basit bir MessageBox gösteren TriggerAction tanımlayacağız. Diyelim ki herhangi bir kontrole tıklandığında veya farklı durumlarda MessageBox'lar göstermek istiyoruz. Normalde yapmamız gereken kesinlikle kod tarafına geçip uygun kodu yazmak olurdu. Oysa hazırlayacağımız TriggerAction herhangi bir kod yazmadan tasarımcının istediği event ve zaman için istediği mesajı MessageBox ile gösterebilmesini sağlayacak.

Her zamanki gibi yeni bir Silverlight projesi yarattıkta sonra System.Windows.Interactivity.dll'i referans olarak alıyoruz. Projemize ekleyeceğimiz yeni bir VB/CS dosyasının içerisinde Behavior'ımızı tanımlayacak sınıfı yazarken TriggerAction sınıfnı da inherit ediyoruz.

[VB]

Public Class MsgBoxTrigger

    Inherits Interactivity.TriggerAction(Of UIElement)

 

    Protected Overrides Sub Invoke(ByVal parameter As Object)

 

    End Sub

End Class

[C#]

    public class MsgBoxTrigger : System.Windows.Interactivity.TriggerAction<UIElement>

    {

        protected override void Invoke(object parameter)

        {

        }

    }

Gördüğünüz üzere hemen anında Invoke adında bir de metoda sahip olduk. TriggerAction sınıfında Invoke "MustInherit" olarak tanımlandığı için söz konusu metodu bizim yazmamış şart. Zaten bu metod da bizim en çok işimize yarayacak metod olacak. Herhangi bir şekilde bizim Trigger'ımızda kullanıcı tarafından ayarlanmış event çalıştığında buradaki Invoke metodu çalışacak. Anlayacağınız TriggerAction'ımızın tüm işlevselliğini buraya yazacağız.

[VB]

    Private PText As String

    Public Property MessageText() As String

        Get

            Return PText

        End Get

        Set(ByVal value As String)

            PText = value

        End Set

    End Property

[C#]

        public string MessageText { get; set; }

Tasarımcımızdan aynı anda MessageBox ile gösterilecek metni de almamız gerek. O nedenle hemen bir de Property tanımlayarak sınıfımıza ekliyoruz. Bir sonraki adımda basit bir şekilde Invoke metodunda bu mesajı göstermemiz yeterli olacaktır.

[VB]

    Protected Overrides Sub Invoke(ByVal parameter As Object)

        MessageBox.Show(MessageText)

    End Sub

[C#]

        protected override void Invoke(object parameter)

        {

            MessageBox.Show(MessageText);

        }

Gördüğünüz gibi sistem epey basit. Her zamanki gibi isterseniz AssociatedObject'e de ulaşarak TriggerAction'ın bağlı olduğu kontrolü de bulabilirsiniz. Artık elimizdeki Behavior'ı rahatlıkla tasarımcılar da Blend içerisinde kullanarak istedikleri durumlarda MessageBox gösterebilirler.

Özel TriggerAction Behavior'ımız karşınızda.
Özel TriggerAction Behavior'ımız karşınızda.

XAML tarafına baktığımızda her zaman olduğu gibi tüm Trigger yapısı XML ile tanımlanmış durumda. Gerekli XML NameSpace'ler de eklendikten sonra hazırladığımız TriggerAction rahatlıkla kullanılabiliyor.

[XAML]

<UserControl

   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" xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:local="clr-namespace:Triggers_VB" x:Class="Triggers_VB.MainPage"

   d:DesignWidth="640" d:DesignHeight="480">

  <Grid x:Name="LayoutRoot">

 

      <Rectangle Fill="Red" Stroke="Black" HorizontalAlignment="Left" Margin="157,184,0,215" Width="159">

          <i:Interaction.Triggers>

              <i:EventTrigger EventName="MouseLeftButtonDown">

                  <local:MsgBoxTrigger MessageText="Selamlar!"/>

              </i:EventTrigger>

          </i:Interaction.Triggers>

      </Rectangle>

 

  </Grid>

</UserControl>

Hepinize kolay gelsin.

Örneğe ait kaynak kodlar - 23082009_2.rar (49,3 KB)

Sunday, August 23, 2009 1:44:20 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Expression Blend | Silverlight 3.0  | 
 Saturday, August 22, 2009

Silverlight ilk çıktığı günden itibaren video konusunda ayrı bir iddaya sahip oldu. 1.0 sürümünden başlayarak Silverlight'ın CPU ve RAM kullanımındaki başarısı ister HD ister FullHD videoların oynatılmasında olsun süper bir performans ile bizleri şaşırtmayı başardı. Bazen keşke bu başarıyı Media Player'da da görsek diyerek kendi kendime serzenişlerde bulunmuyor değilim.

Konumuza dönersek, Silverlight ile beraber gelen bir diğer ilginç video çözümlerinden biri de Smooth Streaming adını taşıyor.  Peki nedir bu Smooth Streaming? Elinizde var sayalım ki normal bir video dosyası var ve bunu internetten paylaşmak istiyorsunuz. Fakat videonuzu paylaştığınız kullanıcıların bir kısmı belki de 1Mbit bağlantıya sahipken diğerleri çok daha yüksek hızlı bağlantılara sahipler. Videomuzun kalitesini ayarlarken hangi kitleyi hedefleyeceğiz? Acaba bağlantısı sağlam arkadaşlara göre mi encode etsek? Bu durumda 1Mbit ile internete bağlananlar videoları neredeyse izleyemeyecektir. Peki gibi de 1Mbit'e göre ayarlasak? Bu sefer de bağlantısı sağlam olan kişilere gerekli kalitede görüntüyü aktaramamış olacağız.

İşte bu iki ucu kokulu değnekten sağ salim kurtulmanın yolu Smooth Streaming. Gelin bir hayal kuralım ve diyelim ki kullanıcıların standart bağlantı hızlarını da es geçip o anki bağlantı hızlarına göre video kalitesini ayarlayabiliyoruz. Hatta ilk başka kullanıcılar videoyu ilk izlemeye başlarken kalitesiz video ile buffer'ı tamamlayıp hemen videoyu oynatsak sonrasında kullanıcının hattı sağlam ise kaliteliye doğru geçip yapsak. En önemlisi tüm bunlar gerçekleşirken kullanıcı hiçbirşeyin farkında olmasa ve videosunu izlemeye rahat bir şekilde devam etse? Güzel bir hayal değil mi? Ama hayal değil, bunların hepsi Smooth Streaming ile bir gerçek.

Nasıl çalışıyor?

Normalde video dosyalarını bir web sunucusu veya Windows Media Services gibi özel altyapılar ile yayınlarız ve bu altyapılar tek bir video dosyasını alarak yayınlamaya uygun şekilde düzenlenmiştir. Oysa elimizde aynı videonun farklı bitrate'ler ile encode edilmiş hali olsa kullanıcılara seçme şansı tanıyabiliriz. Fakat iki soru var; birincisi bant genişliği konusunda kullanıcı seçim yapmamalı. Söz konusu bant genişliği seçimi otomatik olmalı. Bunun için Silverlight tarafında biraz kod yazmak yeterli olacaktır. İkinci sorun ise bant genişliği ile ilgili seçimi sadece video başlarken değil video oynatılırken de kullanıcı farkında olmadan yapabilmek isteriz. Bant genişliği değiştikçe oynatıcı anında daha kaliteli veya kalitesize hızlı bir şekilde geçmeli. İşte bu noktada farklı bir taktik uygulama zorunda olacağımız kesin.

Smooth Streaming ile beraber video dosyaları birden çok bitrate'lerde encode edilerek paketlenip sunucuya yerleştiriliyor. Bu süreçte ayrıca bahsedeceğiz. Sonrasında bu paket IIS üzerine yüklenen IIS Media Services içerisinde Smooth Streaming sistemi tarafından tanınıyor ve kullanıcıdan gelen isteği göre uygun video gönderiliyor. IIS Media Services sadece bu kadarı ile kalmıyor ve video verilerimiz artık 2 ile 4 saniye arasındaki HTTP paketleri ile gönderiliyor. Böylece duruma göre gönderilecek olan 2. veya 4. paket farklı bitrate ile encode edilmiş farklı dosyalardan olabiliyor.

Smooth Streaming mantığı.
Smooth Streaming mantığı.

Yukarıdaki görselde de inceleyebileceğiniz üzere videonun sunucudan istemciye yolculuğunda eski taktik alt kısımda gösterilmiş durumda. Üst kısımdaki ise video 2sn'lik HTTP paketleri şeklinde gönderiliyor. Bu paketli sistemin ilk avantajı arada kalitenin değiştirilebilmesi iken ikinci bir avantaj ise bu paketlerin http paketlerini önbellekleyen bir başka sunucu tarafından önbelleğe alınabilmesi. Böylece son kullanıcıya aynı paketlerin aktarılması için bir noktaya kadar hız avantajı sağlanabiliyor.

Peki Nasıl Yaparız?

İlk olarak tabi ki sunucunuzda IIS Media Services kurulu olmalı. Doğrudan IIS7 üzerine yüklenen bu eklenti ile beraber artık sunucunuza Smooth Streaming desteği gelecektir. Apache tarafında da buradaki downloadları deneyebilirsiniz. Bir sonraki adımda gerekli olan şey elimizde Smooth Streaming'e uygun şekilde paketlenmiş birden çok bitrate'te kaydedilmiş video dosyaları. Bu şekilde bir paket hazırlamanın en kolay yolu Expression Encoder 3'ten geçiyor. Expression Endoer 3 varsayılan ayarları ile Smooth Streaming tanıyor.

Expression Encoder 3 içerisinde Smooth Streaming ayarları.
Expression Encoder 3 içerisinde Smooth Streaming ayarları.

Expression Encoder içerisine bir video ekledikten sonra video profillerinden Smooth Streaming altındaki profillerden birini seçebilirsiniz. Söz konusu seçimi yaptıktan sonra encode etmek istediğiniz ve kullanıcılarınıza ayrı ayrı sunmak istediğiniz her bandwidth'i listeye eklemeniz gerekiyor. Ayarları tamamladıktan sonra video dosyasını encode edebilirsiniz. Sonuçta elinize ism uzantısı ile başlayan dosyalar gelecektir. Yapmış olduğunuz ayarlara göre her encode edilen video dosyası ayrı bir ISMV dosyasında tutulabileceği gibi hepsi bir dosyada da tutulabilir. Bunu Expression Encoder içerisinde "Create separate file per stream" seçeneği ile ayarlayabilirsiniz.

[ISM Dosyası]

<?xml version="1.0" encoding="utf-16"?>

<!--Created with Expression Encoder version 3.0.1332.0-->

<smil xmlns="http://www.w3.org/2001/SMIL20/Language">

  <head>

    <meta

      name="clientManifestRelativePath"

      content="Live stream archive 17.08.2009 15.07.ismc" />

  </head>

  <body>

    <switch>

      <video

        src="Live stream archive 17.08.2009 15.07_305.ismv"

        systemBitrate="305000">

        <param

          name="trackID"

          value="2"

          valuetype="data" />

      </video>

      <video

        src="Live stream archive 17.08.2009 15.07_230.ismv"

        systemBitrate="230000">

        <param

          name="trackID"

          value="2"

          valuetype="data" />

      </video>

      <audio

        src="Live stream archive 17.08.2009 15.07_1644.ismv"

        systemBitrate="64000">

        <param

          name="trackID"

          value="1"

          valuetype="data" />

      </audio>

    </switch>

  </body>

</smil>

Yukarıdaki XML yapısı ISM dosyasının içinde saklı. Yapı ile beraber bu pakette bulunan videoların bitrate'leri ve dosyaların adları bulunuyor. Böylece IIS Media Services rahatlıkla bu dosya üzerinden bilgileri alabilecek. Ayrıca istemci tarafında kullanılacak bilgileri içeren ayrı bir manifest dosyası da ISMC uzantısı ile kayıtlıdır.

[ISMC Dosyası]

<?xml version="1.0" encoding="utf-16"?>

<!--Created with Expression Encoder version 3.0.1332.0-->

<SmoothStreamingMedia

  MajorVersion="2"

  MinorVersion="0"

  Duration="59730000">

  <StreamIndex

    Type="video"

    Chunks="1"

    QualityLevels="8"

    MaxWidth="640"

    MaxHeight="480"

    DisplayWidth="640"

    DisplayHeight="480"

    Url="QualityLevels({bitrate})/Fragments(video={start time})">

    <QualityLevel

      Index="0"

      Bitrate="1644000"

      FourCC="WVC1"

      MaxWidth="640"

      MaxHeight="480"

      CodecPrivateData="250000010FE3F213F0EF8A13F83BF1C0EFC80000010E5A0040" />

    <QualityLevel

      Index="1"

      Bitrate="1241000"

      FourCC="WVC1"

      MaxWidth="640"

      MaxHeight="480"

      CodecPrivateData="250000010FE3E613F0EF8A13F83BF1C0EFC80000010E5A0040" />

    <QualityLevel

      Index="2"

      Bitrate="937000"

      FourCC="WVC1"

      MaxWidth="640"

      MaxHeight="480"

      CodecPrivateData="250000010FE3DC13F0EF8A13F83BF1C0EFC80000010E5A0040" />

    <QualityLevel

      Index="3"

      Bitrate="708000"

      FourCC="WVC1"

      MaxWidth="428"

      MaxHeight="320"

      CodecPrivateData="250000010FE3D613F0EF8A13F83BF1C0EFC80000010E5A0040" />

    <QualityLevel

      Index="4"

      Bitrate="534000"

      FourCC="WVC1"

      MaxWidth="428"

      MaxHeight="320"

      CodecPrivateData="250000010FE3D013F0EF8A13F83BF1C0EFC80000010E5A0040" />

    <QualityLevel

      Index="5"

      Bitrate="403000"

      FourCC="WVC1"

      MaxWidth="428"

      MaxHeight="320"

      CodecPrivateData="250000010FE3CC13F0EF8A13F83BF1C0EFC80000010E5A0040" />

    <QualityLevel

      Index="6"

      Bitrate="305000"

      FourCC="WVC1"

      MaxWidth="364"

      MaxHeight="272"

      CodecPrivateData="250000010FE3C813F0EF8A13F83BF1C0EFC80000010E5A0040" />

    <QualityLevel

      Index="7"

      Bitrate="230000"

      FourCC="WVC1"

      MaxWidth="364"

      MaxHeight="272"

      CodecPrivateData="250000010FE3C613F0EF8A13F83BF1C0EFC80000010E5A0040" />

    <c

      n="0"

      d="58320001">

      <f

        i="0"

        s="438"

        q="7405" />

      <f

        i="1"

        s="327"

        q="5536" />

      <f

        i="2"

        s="237"

        q="4016" />

      <f

        i="3"

        s="195"

        q="3299" />

      <f

        i="4"

        s="137"

        q="2321" />

      <f

        i="5"

        s="101"

        q="1706" />

      <f

        i="6"

        s="75"

        q="1267" />

      <f

        i="7"

        s="63"

        q="1063" />

    </c>

  </StreamIndex>

  <StreamIndex

    Type="audio"

    Index="0"

    FourCC="WMAP"

    Chunks="3"

    QualityLevels="1"

    Url="QualityLevels({bitrate})/Fragments(audio={start time})">

    <QualityLevel

      Bitrate="64000"

      SamplingRate="44100"

      Channels="2"

      BitsPerSample="16"

      PacketSize="2973"

      AudioTag="354"

      CodecPrivateData="1000030000000000000000000000E00042C0" />

    <c

      n="0"

      d="39009523" />

    <c

      n="1"

      d="19504762" />

    <c

      n="2"

      d="1680045" />

  </StreamIndex>

</SmoothStreamingMedia>

Yukarıda gördüğünüz dosyaların yanında sadece farklı bitrate'ler ile encode edilmiş videolar bulunuyor. Olay aslında bu kadar basit. İstemci tarafına baktığımızda ise esas mesele bu yapıyı Silverlight nasıl oynatacak veya kendi bant genişliğini algılayarak nasıl seçim yapacak sorusu geliyor. İşte bu sorularla iglili cevapları içeren kodu sıfırdan yazmamız gerekmiyor. Expression Encoder ile beraber gelen tüm Silverlight şablonları hali hazırda Smooth Streaming desteğine sahip. Unutmayın ki bu şablonların hepsinin kaynak kodları da yine Expression Encoder'ın yüklü olduğu klasörde mevcut. Böylece oluşturulan yapıyı kopyalayarak kendi projelerinizde de rahatlıkla ilerleyebilirsiniz.

Sonuç olarak...

Hemen Smooth Streaming'i denemek isterseniz aşağıdaki adresten örnek bir videoyu izleyebilirsiniz. Hatta söz konusu sitede göreceğiniz Silverlight uygulamalasında kendi bant genişliğinizi üst kısımdan limitleyebilir ve alt solda da hangi kalitede videonun istemciye indirildiğini test edebilirsiniz.

Hepiniz kolay gelsin...

Saturday, August 22, 2009 6:50:42 PM (GTB Standard Time, UTC+02:00)  #    Comments [7]   Silverlight 3.0  | 
 Friday, August 21, 2009

Dün Expression Blend 3 içerisinde Behaviorlara göz attık. Bugün de gelin bir yazılımcı gözü ile nasıl Behavior yaratabiliriz inceleyelim.

Behavior'lar kendi içlerinde ayrı gruplarda incelenebilirler. Bazıları bir event üzerinden tetiklenmesi gerekirken bazıları ise doğal yoldan bağlandıkları nesnelere bir özellik ekleyebiliyorlar. Örneğin GoToStateAction aslında TriggerAction tipinde bir Behavior olarak karşımıza çıkarken FluidMoveBehavior ise tek başına Trigger içermeyen bir Behavior olarak tasarlanmıştır. Bu gibi farklı Behavior tiplerinin programlanması da kısmen tabi ki birbirinden farklı oluyor.

Bu yazımızda inceleyeceğimiz Behavior'lar Trigger içermeyen ve hedef kontrollerine özellik katma amacı güden Behavior'lar olacak.

Nasıl Behavior yazarız?

Behavior'lar System.Windows.Interactivity.dll içerisinde saklı Behavior<T> / Behavior(Of T) sınıfından türeyerek oluşturulular. O nedenle yarattığımız yeni bir Silverlight projesi içerisinde ilk olarak söz konusu DLL'i referans olarak eklemeliyiz. Projenize "solution explorer" içerisinde sağ tuş tıklayarak "add reference" komutunu verdiğiniz System.Windows.Interactivity karşınıza gelecektir. Hemen referans olarak ekleyerek ilerleyebilirsiniz.

Yeni Silverlight projemizde Behavior'ımızı yazmak için yeni bir VB/CS dosyası ekleyerek Behavior sınıfından türeyen yeni bir sınıf yaratıyoruz.

[VB]

Public Class OpacityBehavior

    Inherits Interactivity.Behavior(Of UIElement)

 

 

End Class

[C#]

    public class OpacityBehavior : System.Windows.Interactivity.Behavior<UIElement>

    {

    }

Behavior sınıfından türetirken generics yapısını kullanarak UIElement'i parametre olarak verdiğimizi görebilirsiniz. Bunun nedeni geliştirdiğimiz Behavior'ın tüm UIElement'lere uygulanabilecek olması. Behaviorımız şimdilik örnek olması amacıyla basit bir işlem yapacak ve kendisine verilen herhangi bir kontrolün fare ile üzerine gelinmesi halinde şeffaflığını değiştirecek. Böylece üzerine gelince şeffaf, çıkınca normal gözüken kontroller tasarlamak istersek doğrudan bu geliştirdiğimiz Behavior'ı kullanabileceğiz.

Şeffaflık değişimi için erişmemiz gereken property olan Opacity aslında UIElement'ten türetilmiş tüm kontrollerde bulunduğu için bizim de UIElement'leri hedeflememiz doğru olacaktır.

[VB]

    Private POpacity As Double

    <ComponentModel.Category("Behavior Özellikleri")> _

    Public Property Opacity() As Double

        Get

            Return POpacity

        End Get

        Set(ByVal value As Double)

            POpacity = value

        End Set

    End Property

[C#]

        [System.ComponentModel.Category("Behavior Özellikleri")]

        public double Opacity { get; set; }

Yukarıdaki basit bir şekilde Opacity adında bir Property tanımlıyoruz. Bu property bizim Behavior'ımızın bir propertysi olacak. Böylece kullanıcıların Behavior'ı kullanırken kendisine parametre olarak hedef kontrolün fare ile üzerine gelindiğinde ne kadar şeffaf olmasını istediklerini yazabilecekler. Bu Property Expression Blend içerisinde sağ kolonda gözükecek. Blend içerisinde Properties panelinde bu şekilde Property'lerinizi gruplayabilirsiniz. Property'mize verdiğimiz meta data bunu sağlıyor. Şimdi sıra geldi hedef kontrolü ve onun MouseEnter, MouseLeave eventlarını yakalamaya.

[VB]

    Protected Overrides Sub OnAttached()

        AddHandler AssociatedObject.MouseEnter, AddressOf MouseEnter

        AddHandler AssociatedObject.MouseLeave, AddressOf MouseLeave

        MyBase.OnAttached()

    End Sub

 

    Protected Overrides Sub OnDetaching()

        RemoveHandler AssociatedObject.MouseEnter, AddressOf MouseEnter

        RemoveHandler AssociatedObject.MouseLeave, AddressOf MouseLeave

        MyBase.OnDetaching()

    End Sub

[C#]

       protected override void OnAttached()

        {

            AssociatedObject.MouseEnter += new MouseEventHandler(AssociatedObject_MouseEnter);

            AssociatedObject.MouseLeave += new MouseEventHandler(AssociatedObject_MouseLeave);

            base.OnAttached();

        }

 

        protected override void OnDetaching()

        {

            AssociatedObject.MouseEnter -= new MouseEventHandler(AssociatedObject_MouseEnter);

            AssociatedObject.MouseLeave -= new MouseEventHandler(AssociatedObject_MouseLeave);

            base.OnDetaching();

        }

Yukarıdaki kod grubunda da gördüğünüz üzere BaseClass'tan gelen OnAttached ve OnDetaching metodlarını override ediyoruz. Bu metodlar aslında Behavior nesnemizin yaratıldığı anda çalışan OnAttached ve sahneden kaldırıldığı anda çalışan OnDetaching eventları şeklinde düşünülebilir. Biz de bu durumda OnAttached içerisinde hemen AssociatedObject'e ulaşarak istediğimiz eventları bağlıyoruz. AssociatedObject otomatik olarak bu Behavior'ın içerisine konduğu yani hedeflediği kontrolün bir referansını taşıyor. Aynı şekilde herhangi bir karışıklık olmasın diye OnDetaching'de de event listenerlarımızı kaldırıyoruz.

[VB]

    Public Sub MouseEnter(ByVal sender As Object, ByVal e As System.Windows.Input.MouseEventArgs)

        AssociatedObject.Opacity = POpacity

    End Sub

 

    Public Sub MouseLeave(ByVal sender As Object, ByVal e As System.Windows.Input.MouseEventArgs)

        AssociatedObject.Opacity = 1

    End Sub

[C#]

        void AssociatedObject_MouseLeave(object sender, MouseEventArgs e)

        {

            AssociatedObject.Opacity = 1;

        }

 

        void AssociatedObject_MouseEnter(object sender, MouseEventArgs e)

        {

            AssociatedObject.Opacity = Opacity;

        }

Eh artık sıra geldi MouseEnter ve MouseLeave durumlarında yapılacakları yazmaya. Gördüğünüz gibi daha önce yarattığımız parametremizden gelen değeri hemen duruma göre AssociatedObject'in Opacity özelliğine aktarıyoruz. Burada tabi ki hoş bir animasyon da kullanabilirdik.

Bu behavior'ı nasıl kullanacağız?

Herşeyi kaydettikten sonra projenizi Blend içerisinde açarsanız Asset Library içerisinde Behaviors tabında kendi Behavior'ınızı da görebilirsiniz. Hemen herhangi bir kontrole ataçlayarak sağ tarafta da ayarladığımız özellikleri inceleyebilirsiniz.

Blend içerisinde kendi Behavior'ımız.
Blend içerisinde kendi Behavior'ımız.

Aşağıdaki XAML koduna baktığımızda ise iki farklı XML NameSpace'i görebiliyoruz. Bunlardan biri doğrudan System.Windows.Interactivity'i hedeflerken local adındaki diğeri ise bizim kendi projemizin Assembly'sini hedefliyor. Bu NameSpace'ler üzerinden behavior'ımızı rahatlıkla kullanabiliyoruz.

[XAML]

<UserControl

   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"

   xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

   xmlns:local="clr-namespace:SilverlightApplication1"

   x:Class="SilverlightApplication1.MainPage"

   d:DesignWidth="640" d:DesignHeight="480">

  <Grid x:Name="LayoutRoot">

      <Rectangle Fill="Red" Stroke="Black" Margin="154,177,273,163">

          <i:Interaction.Behaviors>

              <local:OpacityBehavior Opacity="0.2"/>

          </i:Interaction.Behaviors>

      </Rectangle>

  </Grid>

</UserControl>

Gördüğünüz gibi Behavior yazmak hiç de zor değil. Yazdığınız her Behavior tasarımcıların işini kolaylaştırmakla kalmıyor aynı anda yazılımcılar olarak sizin iş yükünüzü de azaltıyor.

Hepinize kolay gelsin ;)

Örneğe ait kaynak kodlar - 21082009_2.rar (47,29 KB)

Friday, August 21, 2009 1:41:36 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Silverlight 3.0  | 
 Thursday, August 20, 2009

Silverlight 3 ile beraber gelen en güzel özelliklerden biri de Behavior yapısı. Behavior'lar bize normalde programatik olarak yaptığımız işlevsellikleri XAML içerisinde tanımlama şansı tanıyan kontroller olarak öngörülebilirler. Daha fazla detaylarına inmeden SL 3.0 SDK ve Silverlight Toolkit ile beraber gelen Behavior'lara isterseniz tek tek göz atalım ve bakalım eskisine kıyasla işimizi ne kadar kolaylaştırıyorlar.

Behaviorlar nerede?

Behaviorlar'ı kullanmanın en kolay yolu Expression Blend'in içi. Sonuç olarak bu kontroller birer XAML kodu yaratacağına göre ve biz de bunları ezberleyip yazma eğiliminde en azından başlangıç aşamasında değilsek Blend doğru seçim olacaktır. Blend 3 içerisinde yeni bir SL projesi yarattıktan sonra hemen Assets paneline göz atıp Behaviors tabını görebilirsiniz.

Expression Blend içerisinde Behavior'lar.
Expression Blend içerisinde Behavior'lar.

Gördüğünüz üzere ilk bakışta toplam sekiz Behavior bizi bekliyor. Hemen en kolayı ile başlayarak bakalım Behavior'ları nasıl kullanıyoruz.

HyperlinkAction

Varsayalım ki elinizde bir kontrol var ve tıklandığında internette bir adrese yönlendirme yapmak istiyorsunuz. Bu durumda HyperlinkButton belki durumu kurtarabilir fakat elinizdeki her kontrolü tasarımı ile beraber birer HyperlinkButton'a çevirmek de ciddi işkence olabilir. Keşke herhangi bir kontrole tıklandığında bir URL'e yönlendirebilsek? Aslında bunu kod tarafından yapabiliriz. Örneğin elimizde bir Rectangle varsa onun MouseLeftButtonDown durumunu yakalayıp orada da kod ile kullanıcıyı başka bir web sayfasına yönlendirebiliriz.

Tüm bunları yapmak yerine basit bir şekilde HyperlinkAction Behavior'ını da kullanabiliriz. Hemen sahneye örnek bir Rectanlge aldıktan sonra Assets içerisinden de HyperlinkAction'ı alarak "Objects and Timeline" içerisinde Rectangle'ın üzerine bırakabilirsiniz.

Behavior'ımız sahnede.
Behavior'ımız sahnede.

Behavior'ı eklediğiniz anda yukarıdaki manzara ile karşılaşacaksınız. Artık Rectangle'ın için bir başka kontrol var yani bir Behavior var. Hemen bu Behavior nesnesini seçerseniz bu sefer de Properties panelinde Behavior'a ait özellikleri bulabilirsiniz.

Behavior'ımıza ait ayarlar.
Behavior'ımıza ait ayarlar.

Behavior'ımızın özelliklerini incelediğimizde aslında karşımızda bir EventTrigger olduğunu görüyoruz. Bu yapı daha önce WPF ile çalışanlara tanıdık gelecektir. SourceName özelliğinde Parent yazması Behavior'ımızın otomatik olarak içerisinde olduğu Rectangle'ı kaynak aldığını anlamına geliyor. EventName kısmında Rectangle'ın hangi durumunda linkin çalışmasını istiyorsak onu seçiyoruz. Varsayılan ayarlarda MouseLeftButtonDown geliyor. Son olarak tabi linkimizin adresini ve hedefini de girersek artık herşey tamamdır. Bir satır bile kod yazmadan sadece birkaç ayar ile işimizi bitirdik. Bu sistem özellikle tasarımcılar açısından çok değerli olsa da hızlı uygulama geliştirme noktasında herkesin işine yarayacaktır. Diğer yandan bu şekilde farklı Behavior'ları yazılımcıların da hazırlayabileceğini düşünürsek işlevselliklerin merkezi hale getirilmesinde de Behavior'lar ciddi rol oynayabilir.

MouseDragElementBehavior

Drag&Drop işlemleri alışkın olduğumuz işlevsellikler arasında. Bu işlevselliği Silverlight içerisinde sağlamak için de hemen kod tarafından ilerleyebiliriz. Oysa basit durumlarda kullanabileceğimiz bir Behavior olarak MouseDragElementBehavior da rahatlıkla kullanılabilir. Söz konusu behavior'ı herhangi bir kontrole bağlamanız onun sürüklenip bırakılabilir hale gelmesi için yeterli. MouseDragElementBehavior'ın ConstrainToParentBounds adında tek bir özelliği var. Eğer bu özelliğe True değerini verirseniz sürükelyip bırakmayı hedeflediğiniz nesne sadece kendi Parent nesnesi içerisinde sürüklenebilecektir. Aksi halde nesne tamamen global olarak sürüklenebilir.

PlaySoundAction

Silverlight içerisinde ses veya video dosyalarını oynatmak için MediaElement kullanıyoruz. Oysa bazı durumlarda basit bir uyarı sesi veya farklı bir efekt yaratmak isteyebiliriz. Bu gibi durumlarda özellikle sahnede bir MediaElement tutmak, duruma göre Source'unu değiştirmek ve Play metodu ile sesi oynatmak ciddi miktarda kod yazmanızı gerektirebilir. Oysa PlaySoundAction Behavior'ı ile doğrudan herhangi bir elementin bir durumunda ses oynatabilirsiniz.

PlaySoundAction ayarları.
PlaySoundAction ayarları.

PlaySoundAction Behavior'ı SourceName ile aldığı kendi Parent kontrolünün herhangi bir durumunda yine Behavior'a Source olarak verilmiş bir WMA veya MP3 dosyasını oynatabiliyor. Görüldüğü üzere işimizi ciddi şekilde kolaylaştırabilir.

RemoveElementAction

Adından da anlaşılacağı üzere RemoveElementAction sahnedeki herhangi bir kontrolü sahneden kaldırıp yok eden bir işlevselliğe sahip. Örneğin bir Rectangle'a tıklandığında başka bir kontrolün sahneden kalkmasını istiyorsanız RemoveElementAction doğru seçim olacaktır.

RemoveElementAction'ın ayarları.
RemoveElementAction'ın ayarları.

Klasik Behavior'larda olduğu gibi RemoveElementAction'ın da SourceName ve EventName adında iki property'si ile Behavior'ın hangi kontrolün hangi eventı çalıştığında çalıştırılacağına karar verebiliyoruz. Sonrasında da TargetName ile sahneden kaldırılacak kontrolü behavior'a teslim etmemiz gerekiyor. Bu noktada ekrandaki herhangi bir elementi seçebilirsiniz, Behavior'ınız rahatlıkla çalışacaktır.

ControlStoryboardAction

Hayatınızı ciddi anlamda kolaylaştırabilecek Behavior'lardan biri de ControlStoryBoardAction olacaktır. Tahmin edebileceğiniz üzere bu Behavior herhangi bir kontrolün bir durumunda gidip bir animasyonu çalıştırabilir, durdurabilir veya bu iki durum arasında toggle işlemi de sağlayabilir. Tüm bunları bir satır bile kod yazmadan artık doğrudan Blend arayüzünde ayarlayabilirsiniz.

ControlStoryboardAction iş başında.
ControlStoryboardAction iş başında.

Yukarıdaki ekran görüntüsünde de görebileceğiniz üzere rahatlıkla bir kontrolün MouseLeftButtonDown durumunda başka bir animasyonu çalıştırabiliyoruz.

ChangePropertyAction

Varsayalım ki herhangi bir kontrolün bir property'sine ait değeri değiştirmek istiyorsunuz. Bu çok basit bir şekilde nesnenin rengi olabileceği gibi belki çok daha farklı bir özelliği de olabilir. Bunun için arka tarafa geçip C# veya VB kodu yazabilirsiniz fakat tasarımcı gözü ile baktığımızda bu pek de sıcak bir seçenek değil. İşte bu gibi durumlarda lowlevel bir Behavior olarak ChangePropertyAction karşımıza çıkıyor ve herhangi bir kontrolün herhangi bir özelliğini istediğimiz zaman değiştirebiliyor.

ChangePropertyAction ile animasyon dahi oluşturabilirsiniz.
ChangePropertyAction ile animasyon dahi oluşturabilirsiniz.

ChangePropertyAction'ın TargetName özelliğine herhangi bir özelliğini değiştirmek istediğiniz hedef kontrolünü aktarıyorsunuz. Bizim örneğimizde Behavior'ın verildiği nesnenin ta kendisini değiştireceğimiz için TargetName değerini Parent olarak kaldı. Sonrasında hedef elementin hangi özelliğini değiştireceğimizi seçip yeni değeri veriyoruz. Eğer isterseniz ek olarak bir de Animation properties tabından animasyon ayarı yapabilirsiniz. Eğer seçtiğiniz hedef Property anime edilebilir bir Property ise bu kısımda yazdığınız Duration (Süre) ve Ease (İvme) ayarına göre animasyon oynatılacaktır. Bizim örneğimizde hedef nesnenin Fill özelliğini değiştirdiğimiz animasyon çalışmayacaktır çünkü Fill değil ancak Fill'in için SolidColorBrush'ının Color özelliği anime edilebilir. Oysa Opacity Property'sini seçseydik doğrudan hedef kontol üzerinden anima edebilirdik.

FluidMoveBehavior

Behavior'lar arasında en havalı efekte sahip Behavior ile karşı karşıyayız :) FliudMoveBehavior'ın amacı herhangi bir Panel kontrolü içerisindeki nesnelerin Panel kontrolü nedeniyle oluşan görsel değişikliklerinin birer animasyon ile gerçekleşmesini sağlamak. Güzel ve bir o kadar da anlaşılması zor bir cümle olduğunun farkındayım :) Şimdi diyelim ki bir WrapPanel'iniz var ve içerisinde birçok kontrolünüz bulunuyor. Bu kontroller WrapPanel'in durumuna göre yukarı aşağı toparlanarak farklı satırlara veya aynı satıra toplanırlar. İşte bu toplanma işleminde kontrollerin pozisyonları değişir fakat bu değişim bir animasyon ile gerçekleşmez. FliudMoveBehavior bu değişimin bir animasyon ile yapılmasını sağlar.

FliudMoveBehavior ve WrapPanel beraber.
FliudMoveBehavior ve WrapPanel beraber.

Yukarıdaki ekran görüntüsünde inceleyebileceğiniz örneğimizde bir Wrappanel içerisinde birçok düğme bulunuyor. Bu düğmeler duruma göre WrapPanel içerisinde değişik yerlerde bulunacaklar. İşte bu konum değişikleri de birer animasyonla yapılsın diye WrapPanel içine FluidMoveBehavior'ı atıp efektin WrapPanel'e değil de WrapPanel içindeki tüm kontrollere uygulanması gerektiğini de Applies To seçeneğine Children değerini vererek belirtiyoruz. İsterseniz oluşturulacak animasyonlar için süre ve easing de ayrıca Behavior üzerinden ayarlayabilirsiniz.

İtiraf etmek gerekirse tüm Behavior'lar arasında hızlı bir şekilde etkileyici görsel şölen yaratan en başarılı Behavior'lardan biri FluidMoveBehavior. O nedenle kesinlikle denemenizi tavsiye ederim.

GoToStateAction

Son Behavior'ımızın adı GoToStateAction. Hatırlarsanız VisualStateManager ile beraber neredeyse tüm kontrollerin farklı State'leri vardı. Bu State'ler kontrollerin farklı durumlarında tasarımcıların farklı tasarımlar ve hatta geçiş animasyonları yapmasını sağlıyor. Yazılımcılarla tasarımcıların beraber çalıştığı projelerde ayrıca özel UserControl'ler için de custom state tanımlamaları yapılabiliyor. İşte tüm bu işlemlerde bir control'ün state'ini değiştirmek için normalde yine kod tarafına geçerdir ama artık GoToStateAction ile beraber buna gerek kalmıyor ve Blend içerisinde de hangi kontrolün hangi durumunda herhangi bir kontrolün  farklı bir State'ine geçiş yapılabileceğine karar verebiliyoruz.

GoToStateAction'a ait ayarlar.
GoToStateAction'a ait ayarlar.

Behavior'ımızı herhangi bir kontrole ekleyip söz konusu kontrolün hangi event'ında çalışacağına da karar verdikten sonra TargetName hedef kontrolümüzü seçerek sonrasında da StateName kısmında da geçiş yapılmasını istediğimiz hedef kontrolün State'ini seçiyoruz. Ayrıca geçiş esnasında animasyon yapılmasını istiyorsanız UserTransitions özelliğini de seçili tutmayı unutmayın.

Sonuç olarak...

Behavior'lar ciddi anlamda Blend kullanımını arttırarak olabildiğince kod yazımını azaltabiliyor. Böylece hedef projeler çok daha hızlı bir şekilde sonlandırılabilir. Yazılımcılar olarak isterseniz kendi Behaviorlarınızı da yazabilirsiniz. O konuda da çok yakında ayrı bir makale ile karşınıza olacağım.

Hepinize kolay gelsin!

Thursday, August 20, 2009 12:37:47 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Silverlight 3.0  | 
 Sunday, August 16, 2009

DeepZoom uygulamalar Silverlight 2.0 yayınlandığından bu yana en ilgi çeken popülar uygulamalardan biri. DeepZoom ile beraber çok büyük fotoğraf arşivlerini rahatlıkla tarayıcı içerisinde gösterebilmekle kalmıyor aynı anda kullanıcılara çok farklı bir deneyim de yaşatabiliyoruz. Bugüne kadar DeepZoom uygulamaları geliştirirken kullandığımız DeepZoom Composer bundan bir süre önce beta olmaktan kurtulup ilk sürümü ile yayınlandı.

DeepZoom Composer download link:
http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=457b17b7-52bf-4bda-87a3-fa8a4673f8bf

DeepZoom Composer'ın son sürümünde standart DeepZoom uygulamaları ile ilgili çok güzel yenilikler geliyor ve bir satır bile kod yazmadan günü birlik kullanıma uygun güzel sonuçlar alınabiliyor. Bu yazımızda DeepZoom Composer'ın son sürümünü inceleyerek gelen yeniliklerden bahsedeceğiz.

Başlangıç için...

DeepZoom Composer'a açtığımızda hemen projemizi yaratıp ekrandaki "Add image" menüsünden yüksek çözünürlüklü resimlerimizi ekleyebiliyoruz. Import modundayken tüm resimlerimizi ekledikten sonra uygulamanın arayüzünü tasarlamak için Compose moduna geçebiliriz. Resimlerimizi farklı şekillerde ister iç içe, ister yan yana yerleştirebiliriz. Hayal gücü size kalmış. Şimdi gelin elimizdeki yeni proje ile en son yeniliklere göz atalım.

DeepZoom içinde linkler!

DeepZoom uygulamalarında binlerce resim olabilir. Bu gibi durumlarda bazen bu resimlerden birine tıklandığında veya ekranda belirli bir alana tıklandığında başka bir resme kullanıcıyı yönlendirmek isteyebilirsiniz. Belki de tamamen DeepZoom uygulaması dışında bir adrese yönlendireceksiniz kullanıcıları. Tüm bunları artık doğrudan DeepZoom Composer içerisinde ayarlayabiliyorsunuz.

Link olacak alanı belirlerken...
Link olacak alanı belirlerken...

Uygulamanız içerisinde link olarak tıklanacak bir alanı belirlemek için yukarıdaki resimde de görebileceğiniz ufak "Add area" düğmesini kullanacağız. Düğmeye tıkladıktan sonra DeepZoom uygulamanızın compose ekranında istediğiniz alanı bir kare içerisine alabilirsiniz.

Link olacak bölgeyi yarattık.
Link olacak bölgeyi yarattık.

Link olarak kullanılacak bölgeyi tanımladıktan sonra sıra geliyor linkin ne işe yarayacağını ayarlamaya. Daha önce de bahsettiğimiz gibi aslında iki farklı seçenek var. Bunlardan ilki linkin kullanıcıyı DeepZoom uygulaması içerisinde başka bir resme yönlendiriyor olması. Diğer seçenek ise doğrudan kullanıcıyı harici bir adrese yönlendirmek. İlk olarak gelin dahili linkleme işlemini nasıl yapabiliyoruz görelim.

Dahili linkleme işlemini yapıyoruz...
Dahili linkleme işlemini yapıyoruz...

Programı sol kısmında yukarıdaki aşağıya dikey bir menü göreceksiniz. Bu menünün üzerinde "Internatl Links" tabına bastıktan sonra "Layers" tabında yer alan ve daha önce yarattığımız alanlardan (Area) herhangi birini alıp "Internatl Links" tabına sürükleyerek atabiliyoruz. Bu işlemi yaptığımız gibi hemen karşımıza Direct Link penceresi geliyor ve bu alanın bir dahili link (Internal link) olarak kullanılırken DeepZoom uygulaması içerisinden hangi resmi hedef alacağını belirtmemizi istiyor. Bu ekrandan da bir resim seçmeniz ile artık linkleme işlemini bitirdiniz demektir. Bu şekilde rahatlıkla istediğiniz kadar link ayarlayabilir ve DeepZoom uygulamalarınız içerisinde ilginç navigasyon sistemleri oluşturabilirsiniz.

Harici link eklemenin yolu.
Harici link eklemenin yolu.

DeepZoom uygulamalarınız içerisinde dahili linkler gibi isterseniz harici sitelere linkler de yerleştirebilirsiniz. Bunun için ilk olarak "External Link" tabını açmış olmanız gerekiyor. Sonrasında ekranın sağ tarafındaki "Layers" tabından herhangi bir "Area"yı alarak "External Links" tabına sürükleyip bırakabilirsiniz. İşlemi tamamlamanızla beraber karşınıza "External Links Management" ekranı gelecektir. Hemen araç çubuğundan bir link ekleyebilir ve linkin adını, adresini girebilirsiniz. İşte bu kadar, artık DeepZoom uygulamasından dışarıya çıkacak bir link eklemiş oldunuz bile.

DeepZoom ile Slideshow!

DeepZoom uygulamaları resim göstermek ne kadar hoş olsa da bazı durumlarda resimlerin sırası ile ekrandan geçmesini isteriz. Bu gibi durumlarda ayrı Slideshowlar kullanmak zorunda kalsak da aslında DeepZoom uygulamaları kendi iç özelllikleri ile beraber SlideShow altyapıları içinde muhteşem bir sistem olarak öngörülebilir. Bu gibi bir çalışmayı yapmak için daha önceleri kod yazmak zorunda kalsak da artık DeepZoom Composer bize bu özelliği hazır olarak getiriyor.

Slideshow hazırlıyoruz.
Slideshow hazırlıyoruz.

DeepZoom uygulamanız içerisinde resimleri istediğiniz gibi yerleştirebilir, sıralayabilirsiniz. Bazı resimler ufak bazıları ise büyük hatta iç içe resimler bile olabilir. Tüm bunları algılayacak olan SlideShow sistemi otomatik olarak resimlerinizi tek tek gezerek ekrana sığdıracak şekilde resimlerinize gerektiğinde zoom da yapacaktır, aynı şekilde zoom out da yapabilir.

Uygulamanızın ana ekran tasarımını bitirdikten sonra "SlideShow" tabını açarak sağ taraftaki "Layers" bölümünden istediğiniz resimleri sırası ile "SlideShow" tabına sürükleyerek bırakabilirsiniz. Tab içerisindeki resimler yine tab içerisindeki sıraları ile DeepZoom uygulamasında gezilecektir.

Örnek DeepZoom uygulamasından bir kare.
Örnek DeepZoom uygulamasından bir kare.

DeepZoom uygulamalarında dahili menüler!

Menü sistemleri neredeyse her web sitesinde vardır. SlideShow ve harici linklerin dışında bazen bir DeepZoom uygulamasında çok fazla fotoğraf olduğunda ayrı bir menü ile fotoğrafların listelenebilmesi hatta belki de kategorilerine göre ayrılabilmelerini sağlamak hoş olabilir. İşte bu gibi bir işlevselliği rahatlıkla DeepZoom Composer içerisindeki menü sistemi ile geliştirebilirsiniz.

DeepZoom'umuz için menü hazırlıyoruz.
DeepZoom'umuz için menü hazırlıyoruz.

DeepZoom uygulamanıza bir menü eklemek için hemen "Menu" tabını açarak sağ taraftaki "Layers" tabından istediğiniz fotoğrafları "Menu" tabına sürükleyerek atabilirsiniz. Tüm fotoğraflarınızı taba aldıktan sonra istediğiniz bir fotoğrafı seçip dikey veya yatay olarak menü içerisindeki pozisyonunu değiştirebilirsiniz. Böylece rahatlıkla gruplamalar da yapabilirsiniz.

DeepZoom uygulamamızdaki menü!
DeepZoom uygulamamızdaki menü!

Gördüğünüz gibi artık DeepZoom uygulamamızın sol üstündeki oktan ayrı bir menüye ulaşabiliyoruz. Bu menüde daha önce DeepZoom Composer içerisinde hazırladığımız resimlerin bir listesi uygun şekilde listeleniyor. Menüden herhangi bir resme tıkladığınızda DeepZoom uygulaması otomatik olarak o resme zoom yapıyor.

DeepZoom Composer gün geçtikçe tek başına ayakta durabilen ayrı bir program halini alıyor. Özellikle bu son eklenen işlevsellikler ile beraber internetten indirilebilen ücretsiz bir program olarak web siteleri hazırlarken hızlı çözümler yaratmak adına işimizi kolaylaştıracağı kesin.

Hepinize kolay gelsin!

Sunday, August 16, 2009 6:25:44 PM (GTB Standard Time, UTC+02:00)  #    Comments [2]   Silverlight 3.0  | 
 Saturday, August 15, 2009

Silverlight uygulamalarınızda harici kontroller kullanmamak elde değil. Bugün ister Silverlight SDK ile beraber gelen kontroller olsun ister Silverlight Toolkit ile beraber gelenler hepsi de neredeyse hayati öneme sahip. Tüm bu senaryoda XAP dosyamızın büyüklüğünü olabildiğince ufak tutmak da tabi bir diğer hedefimiz. Bu çerçevede zaten hali hazırdaki çoğu kontrol de ayrı kütüphanalere saklanmış durumda. Örneğin Silverlight SDK ile beraber gelen ve Visual Studio içerisinde varsayılan yüklemelerle Toolbox'a yerleşen DataGrid aslında harici bir DLL içerisinde bulunuyor. Siz herhangi bir şekilde uygulamanıza DataGrid eklediğinizde akraplanda söz konusu DLL de projenize eklenmiş oluyor. Tabi bu durum gün geçtikçe XAP dosyanızın şişmesine neden olabiliyor çünkü malum, kullandığınız tüm DLL'lerin XAP'ın içerisinde yerleştirilmek durumunda.

Yazıma böyle uzun ve konumuzla alakası pek de belli olmayan bir yazıyla başlamamın emin olun bir nedeni var :) Konumuz olan "Assembly Caching" aslında da tam bir önceki parafragta bahsettiğimiz sorunları kısmen de olsa çözmeyi planlıyor. Sonuçta eğer DataGrid kullanacaksanız DLL'ini istemciye göndermek zorundayız, bunu engelleyemeyiz fakat nasıl gönderileceği ile ilgili belki bazı değişiklikler yapabiliriz...

Yeni  bir proje yeni bir hayat....

Şimdi gelin Visual Studio'da yeni bir Silverlight projesi yaratalım ve araç çubuğundan bir DataGrid tutup hemen sahneye atın. Bu işlemi yapmanızla beraber XAML tarafında DataGrid için gerekli namespace'lerin tanımlandığını ve bazı DLL'lerin otomatik olarak referans alındığını göreceksiniz. Hemen başka kod yazmadan projenizi Build ederek XAP dosyasının da uzantısını değiştirip ZIP yapıp içerisine bir göz atalım.

DataGrid ile normal bir Silverlight projesi.
DataGrid ile normal bir Silverlight projesi.

Yukarıdaki gördüğünüz üzere bir DataGrid için toplam 4 ayrı DLL XAP dosyasına eklenmiş durumda. Özellikle DataGrid'in kendisinin bulunduğu DLL 430KB. Tabi tüm bunlar XAP içerisinde ZIP'lendiği için daha ufalıyor fakat şu anda bile XAP dosyamızın büyüklüğü 206KB. Bu arada assembly_caching_VB.dll dosyası kafanızı karıştırmasın, benim yaratmış olduğum örnek projenin adı assembly_Caching_VB olduğu için aslında o dosya projemizin kendi kodlarını içeriyor.

Peki ne yapabiliriz?

Daha önce de dediğim gibi sonuçta bu DLL'lerin istemciye gitmesi şart. Yapabileceğimiz tek şey uygulamadan bağımsız olarak bu DLL'lerin istemci tarafında önbelleklenebilmesini sağlamak. Genelde bu şekilde uygulama geliştirirken uygulamanız sürekli değişebilir fakat kullandığınız hazır kütüphanaler genelde hep aynıdır. Yani bizim DataGrid olan uygulamamız değişse ve cachlenmese de aslında DataGrid'in kendisinin bulunduğu DLL rahatlıkla daha uzun vadeli olarak cachelenebilir.

Tabi bu gibi bir Cachelemenin tarayıcı tarafından yapılabilmesi için bu DLL'lerin ayrı paketler olarak istemciye gönderilmesi gerekiyor. Bu gibi bir senaryoyu kendi kodlarımızı yazarak oluşturabiliriz fakat Silverlight 3.0 ile beraber gelen Assembly Caching mekanizması zaten bunu yapıyor. O nedenle pek de kod yazmadan hemen işimizi halledebiliriz.

Silverlight projenize sağ tuş ile tıklayarak gelen menüden "Properties" komutunu verip karşınıza çıkan ekranda "Reduce XAP Size..." seçeneğini işaretleyebilirsiniz.

Assembly Caching'i aktif hale getiriyoruz.
Assembly Caching'i aktif hale getiriyoruz.

Hemen projemizi yeni build edip ASP.NET projemizdeki ClientBin klasörü içerisine bir göz atalım. Artık ClientBin içerisinde XAP dosyamızın yanı sıra ayrı ZIP dosyaları da bulunuyor. Bu dosyalar bir önceki adımda XAP dosyası içerisindeki DLL'lerin ZIP'lenmiş halleri. Anlayacağınız söz konusu DLL'ler artık XAP içerisinde değiller ve XAP dosyamız 5KB'a düşmüş durumda. DLL'ler harici ZIP'ler şeklinde paketlenerek XAP ile aynı konuma yerleştirilmiş.

Assembly'ler ayrı ZIP'lerde...
Assembly'ler ayrı ZIP'lerde...

Ne fark etti derseniz aslında konu çok basit. Yukarıdaki şekli ile DLL'ler ayrıldığında yazılımcıların bu DLL'leri ayrı ayrı update etme şansı oluyor. Sonuç olarak sunucu tarafındaki HTTP Header'a LastModifiedDate'e bakacak olan tarayıcı taraflı cachleme mekanizmaları uygulama değişmiş olsa da DLL'leri içerisinde ZIP'ler değişmemişse DLL'lere ait ZIP'leri indirmeyecek ve önbellektekini kullanacaktır. Bu durum da tabi ki performansta ciddi bir artış yaratır.

[AppManifest.xaml / Assembly Caching Öncesi]

<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" EntryPointAssembly="assembly_caching_VB" EntryPointType="assembly_caching_VB.App" RuntimeVersion="3.0.40624.0">

  <Deployment.Parts>

    <AssemblyPart x:Name="assembly_caching_VB" Source="assembly_caching_VB.dll" />

    <AssemblyPart x:Name="System.ComponentModel.DataAnnotations" Source="System.ComponentModel.DataAnnotations.dll" />

    <AssemblyPart x:Name="System.Windows.Controls.Data" Source="System.Windows.Controls.Data.dll" />

    <AssemblyPart x:Name="System.Windows.Controls.Data.Input" Source="System.Windows.Controls.Data.Input.dll" />

    <AssemblyPart x:Name="System.Windows.Data" Source="System.Windows.Data.dll" />

  </Deployment.Parts>

</Deployment>

[AppManifest.xaml / Assembly Caching Sonrası]

<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" EntryPointAssembly="assembly_caching_VB" EntryPointType="assembly_caching_VB.App" RuntimeVersion="3.0.40624.0">

  <Deployment.Parts>

    <AssemblyPart x:Name="assembly_caching_VB" Source="assembly_caching_VB.dll" />

  </Deployment.Parts>

  <Deployment.ExternalParts>

    <ExtensionPart Source="System.ComponentModel.DataAnnotations.zip" />

    <ExtensionPart Source="System.Windows.Controls.Data.zip" />

    <ExtensionPart Source="System.Windows.Controls.Data.Input.zip" />

    <ExtensionPart Source="System.Windows.Data.zip" />

  </Deployment.ExternalParts>

</Deployment>

Bildiğiniz üzere her XAP dosyası içerisinde Assembly'lerin adreslerini tutan birer de Manifest dosyası bulunuyor. Bu manifest dosyalarına dair birer örneği yukarıda inceleyebilirisniz. Assembly Caching aktif hale getirildikten sonra artık Assembly'lerin birer ExtensionPart olarak geçtiğini ve uygun ZIP dosyalarının adreslerinin de Source olarak verilmiş olduğunu görebiliyoruz.

Ya Kendi Kütüphanalerimiz?

Dikkat ettiyseniz yukarıdaki kütüphanalerin hepsi de Microsoft kütüphanaleriydi. Aynı sistemi kullanarak Silverlight projenize kendi yarattığınız bir kütüphaneyi referans alırsanız Assembly Caching aktif olsa da DLL'inizin yine XAP dosyası içerisine yerleştirildiğini göreceksiniz. Gelin bu durumu nasıl değiştirebileceğimize göz atalım.

İlk olarak ileriki aşamalarda Assembly'miz ile ilgili bir Public Key Token'a ihtiyacıımz olacağı için Assembly'mizin Strong Name'e ihtiyacı var. Bunun için Silverlight ile aynı Solution içerisine eklediğimizi varsaydığım örnek "Silverlight Class Library" projesine sağ tuş ile tıklayarak gelen menüden "Properties" komutunu verip "Signing" tabına geçmemiz gerekiyor.

Assembly'miz Strong Name ekliyoruz.
Assembly'miz Strong Name ekliyoruz.

Uygun bir "Key File Name" verip yaratacağınız key dosyasını seçerek bu işlemi tamamlayabilirsiniz. Daha önce de bahsettiğim üzere bize esas lazım olan Public Key Token olacak. Söz konusu key'i alabilmek için Class Library'i build ettikten sonra .NET Framework SDK ile beraber gelen Strong Name Utility'yi kullanmamız şart. Visual Studio ile beraber yüklenen "Visual Studio 2008 Commant Prompt"u başlat menüsünden bulup çalıştırdıktan sonra sn -Tp Assembly Adı şeklinde gerekli komutu çalıştırabilirsiniz.

Bize "Public Key Token" lazım!
Bize "Public Key Token" lazım!

Şimdi aldığımız bu Token ile aşağıdaki formatta bir XML dosyası yaratıp DLL'imiz ile aynı yere yani bin klasöründe Release altına koymalıyız.

[OrnekLibrary_VB.extmap.xml]

<?xml version="1.0"?>

<manifest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

          xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <assembly>

    <name>OrnekLibrary_VB</name>

    <version>1.0.0.0</version>

    <publickeytoken>266fe0f58a4900bb</publickeytoken>

    <relpath>OrnekLibrary_VB.dll</relpath>

    <extension downloadUri="OrnekLibrary_VB.zip" />

  </assembly>

</manifest>

Gördüğünüz üzere dosya içerisinde DLL'in adı, sürüm numarası, Assembly adı ve Token bulunuyor. Ayrıca extension tagına ait downloadUri özelliğinde de dosyanın adresini veriyoruz. Adres kısmında eğer yukarıdaki şekilde bir dosya adı verirseniz Visual Studio sizin adınızda DLL'i söz konusu dosya adı ile ZIP'ler ve ClientBin içerisinde yerleştirir. İsterseniz downloadUri'ye harici bir adres de verebilirsiniz. Hatta bu adres XAP'ın bulunduğu alan adı haricinde de olabilir. Böyle bir durumda Visual Studio ZIP üretmeyecektir. Unutmayın harici bir alan adında caching yapacaksanız kesinlikle o alan adında ZIP dosyaları için uygun ClientAccessPolicy.xml dosyalarının da oluşturulmuş olması gerekir, aksi halde cross-domain-request yapılamaz.

Dosyamız hazır olduğuna göre onu DLL ile aynı yere kaydedebilirsiniz. Kaydederken Assembly Adı ve sonrasında da extmap.xml yazmanız şart.

Artık herşeyimiz hazır olduğuna göre Silverlight projemizi build edebiliriz. ClientBin içerisinde DLL'inizi ayrıca ZIPlenmiş olarak göreceksiniz!

Neden kullanayım?

Assembly Caching kullanmak uygulamanızın açılışını hızlandırmaz. Eğer hedef makinede daha önce uygulama açılmamış ise yine tüm kütüphanaler indirileceği için birşey değişmeyecektir. Fakat ikinci ve sonraki açılışlarda tarayıcı harici kütüphanaleri cachlediği için uygulamanız değişse de çok daha hızlı açılacaktır. Siz reference DLL'leri değiştirmedikçe ve harici kütüphanalere dair dosyalar yenilenmedikçe cacheleme tarayıcı tarafından yapılacaktır.

Özellikle harici bir alan adında kütüphaneleri önbelleklemek belki de aynı kütüphanaleri farklı uygulamalarda kullananlar için uygun olabilir. Böylece merkezi bir yönetim de kısmen sağlanmış olacaktır. Tüm bunları yaparken de ek kod yazmak zorunda kalmamak güzel bir avantaj olsa gerek.

Hepinize kolay gelsin!

Dikkat: Assembly Caching Silverlight 3 Beta iken çok daha farklı çalışıyordu. Bu konuda internette bir araştırma yaptığınızda karşılaşacağınız kaynakların çoğu size Assembly Caching'in sadece MS kütüphanaleri için yapılabileceğini ve kütüphanalerin MS tarafından host edildiğini söylemeyebilir. Silverlight 3 Beta'yken durum böyleydi  fakat artık değil. MS kütüphanaleri dahil olmak üzere hostingi sizin yapmanız gerekiyor ayrıca yukarıda da gördüğünüz üzere kendi kütüphanalerinizde de bu özelliği kullanabiliyorsunuz.

Örneğe ait kaynak kodlar - 15082009_6.rar (986,18 KB)

Saturday, August 15, 2009 3:26:52 PM (GTB Standard Time, UTC+02:00)  #    Comments [1]   Silverlight 3.0  | 
 Friday, August 14, 2009

Silverlight 3.0 ile beraber tarayıcı içerisinde Multitouch desteği de geldi. Şu an için sadece Windows 7 ve Internet Explorer üzerinde sunulabilen bu deneyimi yaratmak için WPF tarafından biraz daha farklı tekniklerle ilerlemek gerekiyor. Bu yazımıda Silverlight 3.0 tarafında Multitouch API'larına göz attıktan sonra bir Manipulation örneği yapacağız.

Silverlight ve Multitouch

Silverlight içerisinde herhangi bir şekilde gerçekleşen Touch durumunu algılamak için kullanabileceğimiz tek bir event bulunuyor. Söz konusu event'ın adı Touch.FrameReported şeklinde. Bu eventa bağlanan bir event listener'ın argümanı ile beraber gelen bilgiler bizim için fazlası ile yeterli olacaktır.

[VB]

    Public Sub New()

        InitializeComponent()

        AddHandler Touch.FrameReported, AddressOf Touched

    End Sub

 

    Sub Touched(ByVal sender As Object, ByVal e As System.Windows.Input.TouchFrameEventArgs)

      

    End Sub

[C#]

        public MainPage()

        {

            InitializeComponent();

            Touch.FrameReported += new TouchFrameEventHandler(Touch_FrameReported);

        }

 

        void Touch_FrameReported(object sender, TouchFrameEventArgs e)

        {

 

        }

Event'ımızın TouchFrameEventArgs'ında birçok değerli veri bulunuyor. Örneğin her bir touch için birer Timestamp alabiliyoruz. Timestamp'i özünde eski OLEAutomationDate'lere benzetebilirsiniz. Size farklı touch işlemleri arasında süreyi rahatlıkla hesaplayabilmeniz için integer Timestamp'ler döndürüyor. Bunun haricince argüman tarafında önemli üç adet metod bulunuyor. Bunlardan ilki GetPrimaryTouchPoint metodu. Touch işlemi esnasından bir yada daha çok noktadan ekrana dokunulabileceği için ilk dokunma noktası Primary denerek GetPrimaryTouchPoint aracılığı ile bize iletiliyor. Ayrıca GetTouchPoints metodu da tüm dokunulan noktaların bir listesini getirir. TouchPoint tipinde gelen bu noktalara ait ek bilgileri de TouchPoint sınıfı üzerinden alabilirsiniz. Örneğin dokunulan noktanın pozisyonu, alanı, hatta TouchDevice üzerinden de unique ID'sini elde etmek mümkün. Son olarak argüman üzerindeki SuspendMousePromotionUntilTouchUp metodu de Touch işlemleri bitene yani kullanıcı tüm parmaklarını ekrandan çekene kadar fare kullanımını engelleyecektir.

Tüm bu hikaye içerisinde en önemli şeylerden biri Touch işleminin hangi aşamada olduğu. Toplam üç farklı aşama mevcut. Bunlardan ilgi kullanıcının ekrana değdiği ilk an (Down), bir sonraki kullanıcının parmağını ekranda gezdirdiği süre (Move), son olarak (Up) kullanıcının parmağını ekranda çektiği an şeklinde üç farklı aksyon bulunuyor. Bu her aksyon TouchPoint nesnelerinin Action değişkeninde bir enumaration olarak bizi bekliyor. Herhangi bir şekilde bize raporlanan TouchPoint'in pozisyonunu alabildiğimiz gibi o anda ilk dokunma mı, bırakma mı yoksa sürekli dokunma mı oluştuğunu takip edebiliyoruz. Unutmadan hatırlatmak fayda var, kullanıcı parmağını ekranda oynatmadan tutarsa da bu bir Move aksyonu olarak algılanıyor.

Şimdi gelin manipülasyon örneğimize geçelim ve tüm bunların birlikte nasıl kullanıldığına göz atalım. Manipülasyon örneğinde bildiğiniz üzere amacımız ekrana basit bir resim koyarak onun kullanıcı tarafından iki parmak kullanılarak boyutlandırılabilmesini, taşınabilmesini ve çevrilebilmesini sağlamaktır. WPF tarafından farklı olarak daha Silverlight için etrafta hazırlanmış bir ManipulationProcessor bulunmadığı için tüm işlemleri bizim yapmamız gerekecek. İlk aşamada gelin uygulamamızın ekranını hazırlayarak konuya girelim.

[XAML]

  <Grid x:Name="LayoutRoot">

        <Image Source="Koala.jpg" RenderTransformOrigin="0.5,0.5">

            <Image.RenderTransform>

                <TransformGroup>

                    <ScaleTransform x:Name="ImageScale" />

                    <TranslateTransform x:Name="ImageTranslate" />

                    <RotateTransform x:Name="ImageRotate" />

                </TransformGroup>

            </Image.RenderTransform>

        </Image>

    </Grid>

Uygulama ekranımızda basit bir Image nesnesi bulunuyor. Bu Image nesnesinin durumda göre pozisyonunu, dönüş açısını ve boyutunu değiştireceğimiz için uygun Transform nesnelerini de içerisinde yerleştirerek gerekli isimlendirmeleri de yaptık. Böylece rahatlıkla kod tarafından bu işlemleri halledebiliriz. Şimdi sıra geldi Touch ile ilgili gerekli işlemleri arka tarafta yapmaya.

Hemen makalemizin başında da gördüğümüz üzere FrameReported eventına bir event-listener bağlıyoruz.

[VB]

    Public Sub New()

        InitializeComponent()

        AddHandler Touch.FrameReported, AddressOf Touched

    End Sub

[C#]

        public MainPage()

        {

            InitializeComponent();

            Touch.FrameReported += new TouchFrameEventHandler(Touch_FrameReported);

        }

Amacımız FrameReported içerisinde ilk olarak en az iki tane TouchPoint olurkenki durumları yakalamak. Kullanıcının resmi tutup manipulasyon yapabilmesi için en az iki parmağının ekrana dokunuyor olması gerek. Ayrıca Action olarak da parmaklarını ilk dokundurduğu anda değil parmaklarını sürüklerken işlem yapmamız şart. Tüm bu süreçleri kontrol ederken sürekli olarak parmakların bir önceki durumu ile şu anki durumu arasındaki farklara göre gerekli hesaplmaları yaparak resmimize yansıtacağız. Aslında tüm bu sürece gelişmiş bir drag&drop gözü ile bakabilirsiniz.

[VB]

    Dim _FirstTouch As Point = New Point(0, 0)

    Dim _SecondTouch As Point = New Point(0, 0)

Uygulamamızda ilk olarak yukarıdaki şekli ile iki tane Point değişkenini global olarak tanımlıyoruz. Bu değişkenler sürekli olarak kullanıcının parmaklarının bir önceki koordinatlarını saklayacak. Böylece biz de o anki koordinatlar ile bir önceki arasında farkları yakalayabileceğiz.

[VB]

    Sub Touched(ByVal sender As Object, ByVal e As System.Windows.Input.TouchFrameEventArgs)

        If Not e.GetPrimaryTouchPoint(Me) Is Nothing Then

            Dim IlkDokunus As TouchPoint

            Dim IkinciDokunus As TouchPoint

 

            IlkDokunus = e.GetPrimaryTouchPoint(Me)

            If IlkDokunus.Action = TouchAction.Down Then

                _FirstTouch = New Point(0, 0)

                _SecondTouch = New Point(0, 0)

            ElseIf IlkDokunus.Action = TouchAction.Move Then

                If e.GetTouchPoints(Me).Count > 1 Then

                    IkinciDokunus = e.GetTouchPoints(Me)(1)

                    ''BURADA MANIPULATION YAPILACAK

                End If

            ElseIf IlkDokunus.Action = TouchAction.Up Then

               _FirstTouch = New Point(0, 0)

               _SecondTouch = New Point(0, 0)

            End If

 

        End If

    End Sub

[C#]

void Touch_FrameReported(object sender, TouchFrameEventArgs e)

        {

            if ((e.GetPrimaryTouchPoint(this) != null))

            {

                TouchPoint IlkDokunus = default(TouchPoint);

                TouchPoint IkinciDokunus = default(TouchPoint);

 

                IlkDokunus = e.GetPrimaryTouchPoint(this);

                if (IlkDokunus.Action == TouchAction.Down)

                {

                    _FirstTouch = new Point(0, 0);

                    _SecondTouch = new Point(0, 0);

                }

                else if (IlkDokunus.Action == TouchAction.Move)

                {

                    if (e.GetTouchPoints(this).Count > 1)

                    {

                        IkinciDokunus = e.GetTouchPoints(this)[1];

                        //BURADA MANIPULATION YAPILACAK

                    }

                }

                else if (IlkDokunus.Action == TouchAction.Up)

                {

                    _FirstTouch = new Point(0, 0);

                    _SecondTouch = new Point(0, 0);

                }

            }

        }

Yukarıdaki ilk kodumuz karışık gibi gözükse de aslında basit birkaç koşul kontrolünden farklı değil. İlk olarak GetPrimaryTouchPoint ile hali hazırda ilk TouchPoint var mı yok mu kontrolünü gerçekleştiriyoruz. Eğer varsa ikinci amacımız söz konusu Touch işleminin Action'una göre işlem yapmak. Eğer ilk TouchPoint'imize ait Action Up veya Down ise yani kullanıcı parmağını ilk defa dokunduruyor veya çekiyorsa hemen global değişkenlerimizi sıfırlıyoruz. Böylece bir sonraki Touch ile sürükleme işleminde gerekli kontrolleri yaparak işlemleri sıfırdan başlatabiliriz. Fakat unutmayın ki daha bu sadece ilk TouchPoint yani kullanıcının ilk parmağı! Belki de hiç ikinci bir parmak değimedi ekrana. Böyle bir durumda manipülasyon yapamayacağımız için ikinci parmak var mı diye kontrol etmemiz şart.

Eğer ilk TouchPoint'in aksyonu Move ise bu sefer hemen GetTouchPoints ile toplam parmak sayısını alıyoruz. Eğer bu sayı birden yüksekse belli ki ekranda iki parmak var. Her iki parmağa ait TouchPoint'lerini ayrı birer değişkene aldıktan sonra sıra gelecek bu parmakların koordinatlarına göre hesaplamaları yapmaya.

[VB]

                    Dim FirstTouch As Point = IlkDokunus.Position

                    Dim SecondTouch As Point = IkinciDokunus.Position

 

                    If _FirstTouch.X <> 0 Then

                        ''ESAS OLAY BURADA :)

                    End If

 

                    _FirstTouch = FirstTouch

                    _SecondTouch = SecondTouch

[C#]

                        Point FirstTouch = IlkDokunus.Position;

                        Point SecondTouch = IkinciDokunus.Position;

 

                        if (_FirstTouch.X != 0)

                        {

                           //ESAS OLAY BURADA :)

                        }

 

                        _FirstTouch = FirstTouch;

                        _SecondTouch = SecondTouch;

Ele aldığımız TuchPoint'lerden Position alarak birer değişkene aktarıyoruz. Sonrasında tabi ki bu pozisyonları bir önceki pozisyonlar ile karşılaştıracağımız için aslında "bir önceki pozisyon" diye birşey var mı onu kontrol etmemiz gerekiyor. Eğer varsa gerekli işlemleri yapacağız yoksa eldeki pozisyonu kenara atacağız ki bir sonraki işlemde bu pozisyona göre değişiklikleri hesaplayabilelim.

[VB]

Dim ScaleDelta = (((((FirstTouch.X - SecondTouch.X) ^ 2) + ((FirstTouch.Y - SecondTouch.Y) ^ 2)) ^ (1 / 2)) / _

                             ((((_FirstTouch.X - _SecondTouch.X) ^ 2) + ((_FirstTouch.Y - _SecondTouch.Y) ^ 2)) ^ (1 / 2))) - 1

[C#]

 double ScaleDelta = (Math.Sqrt(Math.Pow(FirstTouch.X - SecondTouch.X, 2) + Math.Pow(FirstTouch.Y - SecondTouch.Y, 2)) /

                                Math.Sqrt(Math.Pow(_FirstTouch.X - _SecondTouch.X, 2) + Math.Pow(_FirstTouch.Y - _SecondTouch.Y, 2))) - 1;

İlk olarak boyutlandırma işlemi ile başlayalım. Resmimizin boyutunun ne kadar değişeceğini 1 üzerinden orantılayarak vermemiz gerekiyor ki ScaleTransform'un ScaleX ve ScaleY'sine aktarabilelim. Gelen ScaleDelta'yı sonrasında bu ScaleX ve ScaleY'ye ekleyeceğiz o nedenle bulduğumuz sonucu 1'den çıkartıyoruz ki normal boyuta göre farkı bulalım.

Resmin boyut değişikliği ile ilgili hesaplamayı yaparken izlediğimiz yol her iki parmağın bir önceki pozisyonlarına göre aralarındaki mesafeyi bulup sonra da şu an pozisyonlara göre mesafeleri arasında oranı bir üzerinden hesaplamak. Basit bir hipotenüs hesaplaması gözü ile bakarsak elimizdeki iki noktadan bir üçgen oluşturup hipotenüsü bulmamız mesafe için yeterli olacaktır. Üçgenin yatay kenarı ve dikey kenarının uzunlukları için noktaların X ve Y koordinatları arasındaki farkları kullanabiliyoruz.

[VB]

Dim PositionPoint = New Point(((FirstTouch.X + SecondTouch.X) / 2) - ((_FirstTouch.X + _SecondTouch.X) / 2), _

                                                 ((FirstTouch.Y + SecondTouch.Y) / 2) - ((_FirstTouch.Y + _SecondTouch.Y) / 2))

[C#]

 var PositionPoint = new Point(((FirstTouch.X + SecondTouch.X) / 2) - ((_FirstTouch.X + _SecondTouch.X) / 2),

                                ((FirstTouch.Y + SecondTouch.Y) / 2) - ((_FirstTouch.Y + _SecondTouch.Y) / 2));

Resmin pozisyonu ile ilgili değişikliği hesaplamak biraz daha kolay. İki parmak arasındaki doğrunun orta noktasını bularak bir önceki orta nokta ile şimdiki orta nokta arasındaki farkı almak pozisyon değişikliğini yakalamak için yeterli olacaktır.

[VB]

Dim AngleDelta = (Math.Atan2(FirstTouch.Y - SecondTouch.Y, FirstTouch.X - SecondTouch.X) * 180 / Math.PI) - _

                             (Math.Atan2(_FirstTouch.Y - _SecondTouch.Y, _FirstTouch.X - _SecondTouch.X) * 180 / Math.PI)

[C#]

 var AngleDelta = (Math.Atan2(FirstTouch.Y - SecondTouch.Y, FirstTouch.X - SecondTouch.X) * 180 / Math.PI) -

                                (Math.Atan2(_FirstTouch.Y - _SecondTouch.Y, _FirstTouch.X - _SecondTouch.X) * 180 / Math.PI);

İki keranın bildiğiniz bir üçgenin iç açılarından birini nasıl bulursunuz? :) Bazılarınızı yıllar önceki lise yıllarına döndürdüğümün farkındayım. Dikey mesafe (Y) ve yatay mesafeyi (X) verip bir noktanın x eksenine göre (0,0)'dan açısını radyan olarak veren Math.Atan2 metodunu kullanarak parmaklarımızla oluşturduğumuz çizginin orta noktasının (0,0)'a göre x ekseninden açısını alabiliyoruz. Tabi radyanı da bildiğimiz açıya çevirmek için 180'le çarpıp PI'ye bölüyoruz. Eski pozsiyonlara göre hesapladığımız açı ile şimdiki açı arasındaki fark da tam olarak bulmak istediğimiz şeydi.

[VB]

                        ImageScale.ScaleX += ScaleDelta

                        ImageScale.ScaleY += ScaleDelta

                        If ImageScale.ScaleX < 0 Then ImageScale.ScaleX = 0

                        If ImageScale.ScaleY < 0 Then ImageScale.ScaleY = 0

 

                        ImageTranslate.X += PositionPoint.X

                        ImageTranslate.Y += PositionPoint.Y

 

                        ImageRotate.Angle += AngleDelta

[C#]

                            ImageScale.ScaleX += ScaleDelta;

                            ImageScale.ScaleY += ScaleDelta;

                            if (ImageScale.ScaleX < 0) ImageScale.ScaleX = 0;

                            if (ImageScale.ScaleY < 0) ImageScale.ScaleY = 0;

 

                            ImageTranslate.X += PositionPoint.X;

                            ImageTranslate.Y += PositionPoint.Y;

 

                            ImageRotate.Angle += AngleDelta;

Sıra geldi tüm bu hesaplamalarla bulduğumuz değerleri Image nesnesminde Transform'lara aktarmaya. Sadece Scale için dikkat etmemiz gereken şey eksi değer vermemek. Aksi halde resim ters dönecektir.

Kodumuz bu kadar. Manipülasyon işlemimizi de tamamladık ve artık projemiz çalışmaya hazır.

Hepinize kolay gelsin.

Örneklere ait kaynak kodlar. - 14082009_1.rar (73,14 KB)

Friday, August 14, 2009 1:29:53 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   MultiTouch | Silverlight 3.0  | 
 Tuesday, August 11, 2009

Son iki gündir Microsoft Yaz Okulu'nun İstanbul ayağında Silverlight anlatıyordum. İstanbul yaz okulu öğrencileri doğrudan Microsoft ofisinden olmanın tadını çıkartmıyorlar dersem yalan olur :) Ama itiraf ediyorum en azından aralarda Counter vs oynamıyorlardı. İki gün gibi kısa bir sürede Silverlight'tan girdik WPF'ten çıktık. Önümüzdeki hafta yaz okulunun proje haftasında tekrar onlarla beraber olacağım, bakalım ne fikirler çıkacak.

Microsoft Yaz Okulu, İstanbul
Microsoft Yaz Okulu, İstanbul

Yaz okulundaki öğrencilerime tavsiyem hafta sonunda benim SL seminerlerine bir göz atmaları. Unutmayın SeminerTV'de oyun programlama konusunda da bir seminer kaydı var, eğer projelerinizde SL ile oyun programlama gibi şeyler düşünüyorsanız bence kaçırmayın ;) Haftaya görüşmek üzere...

Tuesday, August 11, 2009 7:18:43 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Silverlight 3.0  | 
 Wednesday, July 15, 2009

Uzun süre oldu :) Bu sürede onlarca mail aldım. İlk olarak hepinize ilginiz için çok teşekkür ediyorum. Biliyorum geciktim, fakat eksikler vardı ve toparlamam gerekti. Neden mi bahsediyorum? Tabi ki "Silverlight 3.0 Öğrenme Ayı" webinerlerinin kayıtlarından bahsediyorum. Biliyorsunuz kayıtların üçü başarısız olmuştu, sonrasında aynı webinerleri tekrar offline'da kaydetmem gerekti. Buna bir de encoding süreçleri eklenenince herşeyip derlenip toparlanması epey uzun sürdü. Fakat sonunda herşey hazır!

15 Saatlik Silverlight 3.0 Eğitimi

Bir de baktım ki toplam 15 saati bulmuş. Sanırım güzel bir eğitim seti oldu. Özellikle baştan sonra doğru izlerseniz daha da faydalı olacağını düşünüyorum çünkü konuların her biri birini takip ediyor. Tabi unutmayın ki tüm webinerler Silverlight 3.0 Beta ile yapıldı o nedenle buradaki makaleyi incelemeyi de unutmayın.

Hepsi sizi bekliyor! Download linkleri için hemen SeminerTV'yi ziyaret edin!

Not: Eğitim setini her yerde çekinmeden duyurabilirsiniz hatta doğrudan download linklerini de gönül rahatlığı ile paylaşabilirsiniz! Bilgi paylaşımı için... İleri! :)

Tuesday, July 14, 2009 11:28:35 PM (GTB Standard Time, UTC+02:00)  #    Comments [23]   Silverlight 3.0  | 
 Saturday, July 11, 2009

Heyecanle beklenen an geldi ve Silverlight 3 yayınlandı. Beta sürümünden Release'e kadar olan değişikliklere göz atacağımız bu yazıda önce gelin Silverlight 3 dünyasına hızlıca nasıl gireriz bir göz atalım.

http://www.silverlight.net/getstarted

Silverlight dünyasına girmenin en kolay yolu hemen yukarıdaki adresi ziyaret etmek. Bu adres içerisinde Silverlight 3 tarafında bilgisayarınıza yüklemeniz gereken herşeyi bulabilirsiniz. Unutmadan, eğer bilgisayarınızda Silverlight 3 Beta yüklemeleri varsa herşeyin öncesinde bu yüklemeleri bilgisayarınızdan kaldırmayı unutmayın.

Silverlight 3.0 Yüklemeleri
Silverlight 3.0 Yüklemeleri

Yukarıdaki ekran görüntüsünde silverlight.net sitesinin bir parçasını görüyorsunuz. Burada ilk aşamada yer alan yüklemelere özellikle dikkat etmek gerek. "Web Platform Installer" olarak geçen yükleme paketi bilgisayarınız için gerekli tüm yüklemeleri yapabilir. Bu yüklemeler içerisinde Visual Studio'nun Express sürümü ve SQL Express de dahil. WPI ile ilgili ayrı bir yazı yazılabilir fakat şimdilik kabaca genel anlamına değinmek yeterli olacaktır diye tahmin ediyorum. WPI'ın amacı bilgisayarınıza tüm yazılım geliştirme platformunu yüklemektir. Yükleme esnasından ASP.NET MVC'den PHP modüllerine kadar istediklerinizi seçebilirsiniz.

Eğer bilgisayarınızda hali hazırda Visual Studio yüklü ise be zaten "benim platformum hazır" diyorsanız bu sefer hızlıca "download the tools directly" linkine tıklayıp Visual Studio Tools for Silverlight paketini indirebilirsiniz. Böylece yazılım geliştirme ortamınız Silverlight 3.0 ile tanışmış olacaktır.

Expression Blend 3 RC

Silverlight 3.0 ile beraber tabi ki yeni bir de Blend sürümüne ihtiyacımız var. Expression Studio 3 daha yayınlanmadığı için Silverlight 3.0'a özel olarak Expression Blend 3'ün RC sürümü Silverlight'cılarla paylaşıldı. Çok yakın zamanda Expression Studio 3 ile beraber Blend 3 de release olacak. Şimdilik RC sürümü ile dahatlıkla idare edebilirsiniz. Sketchflow kısmı eminim ki dikkatinizi çekecektir. Bu konuda uzun uzun makale ve seminer planlarım var :)

Silverlight Toolkit ve DeepZoom tarafında da yeni gelişmeler var. İndirip yeni sürümlerini ücretsiz olarak bilgisayarınıza kurabilirsiniz. Özellikle DeepZoom Composer tarafında yeni menü navigasyon sistemlerini incelemenizi tavsiye ederim. Ayrı makaleler bu konuda da çok yakında karşınızda olacak.

Silverlight 3.0 RTW ile değişenler?

En büyük değişikliklerden biri Silverlight 3.0 SDK içerisinde yer alan bazı kontrollerin doğrudan Toolkit içine taşınmış olması. Bu duruma çok farklı yorumlar getirmek mümkün fakat esas yapılmak istenen şey bu kontrolleri SDK içerisine eklemeden önce biraz daha feedback almak ve bu süreçte de kontrolleri açık kaynak kodları ile beraber yazılım geliştiriciler ile paylaşabilmek. O nedenle aşağıdaki kontrollerin hepsi Beta SDK içerisinden Toolkit tarafına kaydırıldı.

DockPanel, WrapPanel, Expander, HeaderedContentControl, Viewbox,
DataForm, ExpandDirection, ExpanderAutomationPeer, LengthConverter, StretchDirection

DataForm ve DataPager

DataForm ve DataPager kontrollerinde büyük değişiklikler oldu. Artık DataForm kontrolünün Fields adında bir collection'ı yok. Onun yerine eskiden de olan Edit ve Normal modlar için özeleştirme sağlayan Template yapıları kullanılacak. Eski Template yapılarında tasarımı sıfırdan yaratırken bu sefer Template'ler içerisinde IPagedCollectionView adında kontroller de kullanabileceğiz. Söz konusu DataField'ler içerisine kendi kontrollerini koyarak özelleştirmeler yapabilirsiniz. Açıkçası bu değişiklik benim çok hoşuma gitmedi. Eskisi özellikle DataGrid ile çok daha uyumlu bir yazılımcı deneyimi sağlıyordu. Fakat özünde bu değişikliklerin tekrar değişmesi de olası, çünkü ne de olsa bu kontroller Toolkit içerisinde.

DataPager kontrolü tarafında ise IPagedCollectionView sınırlaması kaldırılarak artık herhangi bir IEnumerable kullanılabilmesi sağlanmış.

MetaAttributes

Özellikle DataForm içerisinde Fields Collection'ın kaldırılması sonrasında canım biraz sıkılmıştı ki MetaAttributes tarafındaki değişiklikler keyfimi yine getirdi. Genelde ister DataGrid olsun ister DataForm, bizim Fields veya Columns collectionları editlememizin esas nedeni bir kolonu göstermek istemememizden veya editlenmesini istememizden kaynaklanır. AutoGenereateColumns/Fields özelliklerini sevsek de kolon başına ayar yapabilmek için Field'leri elle yaratmamız gerekiyordu.

[VB]

    <Bindable(True, BindingDirection.TwoWay)> _

    Private _Ornek As String

    Public Property Ornek() As String

        Get

            Return _Ornek

        End Get

        Set(ByVal value As String)

            _Ornek = value

        End Set

    End Property

Eskiden uygulayabildiğimiz yukarıdaki teknik ile bir Property'nin Bindable olup olmadığını hatta ne şekilde Bind edilebileceğini de ayarlayabiliyorduk. Yeni SL 3.0 RTW ile bu MetaAttribute'ler biraz değişti.

[VB]

    <Display(AutoGenerateField:=True)> _

    <Editable(False)> _

    Private _Ornek As String

    Public Property Ornek() As String

        Get

            Return _Ornek

        End Get

        Set(ByVal value As String)

            _Ornek = value

        End Set

    End Property

Artık çok daha kolay bir şekilde doğrudan bir Property'nin AutoGenerateField aşamasında yaratılıp yaratılmayacağını belirleyebiliyor veya Editable ile bu özelliğini editlenebilir olup olmadığı bilgisini de aktarabiliyoruz. Bunun gibi yaratılacak olan kolonun kaçıncı sırada olacağını veya kolon adını da aşağıdaki şekilde belirtebilirsiniz.

[VB]

    <Display(AutoGenerateField:=True, Name:="Kolon Adı", Order:=1)> _

    <Editable(False)> _

    Private _Ornek As String ....

Navigation Framework

Silverlight 3.0 Beta ile karşılaştığımız Navigation Framework içerisinde de ciddi değişiklikler var. Eski halinde UriMapper'ımızı App.XAML içerisinde yaratmak zorunda olup bir de bunun üzerine yarattığımız Mapper sınıfının instance'ına ait ismin de uriMapper olmak zorunda olması ciddi anlamda saçmalıktı :) Tabi ki bu durum düzeltilmiş ve artık uriMapper'larınızı UserControl içerisinde de yaratabiliyor ve istediğiniz ismi verebiliyorsunuz. Sonrasında da uriMapper'ı elle istediğiniz bir frame'e bağlamanız gerekiyor.

[XAML]

....

    <UserControl.Resources>

        <navigationCore:UriMapper x:Key="HerhangiBirMapper">

            <navigationCore:UriMapping Uri="Siparis" MappedUri="/Sayfalar/Siparis.xaml" />

        </navigationCore:UriMapper>

    </UserControl.Resources>

    <Grid x:Name="LayoutRoot">

        <navigation:Frame x:Name="frame" UriMapper="{StaticResource HerhangiBirMapper}" />

    </Grid>
.....

Son olarak eskiden herhangi bir UserControl'ü de içine alabilen frame'ler artık sadece Page'den Inherit edilmiş sayfaları alabilecek.

Diğer mini değişiklikler...

PixelShader Efektleri : Eskiden Content veya Resource olarak projelere eklenebilen PS dosyaları rahatlıkla kullanılabiliyordu artık sadece Resource olarak ayarlanmış PS dosyaları PixelShader olarak kullanılabilecek.

OutOfBrowser Desktop Uygulamaları: Eskiden Detach komutu yeni sürümde Install olarak değiştirildi. Yeni komut : App.Current.Install() Aynı şekilde eski App.Current.RunningOffline da App.Current.IsRunningOutOfBrowser oldu. ExecutionStateChanged, InstallStateChanged şeklinde değiştirildi. Ayrıca OutOfBrowser uygulamaları ile ilgili yaratılan XAML kodu da tamamen değiştirilecek harici bir OutOfBrowserSettings.xml dosyasında tutulması gerekiyor.

[OutOfBrowserSettings.xml]

<OutOfBrowserSettings ShortName="Kısayol Adı" EnableGPUAcceleration="False" ShowInstallMenuItem="True">

  <OutOfBrowserSettings.Blurb>Açıklama tooltip</OutOfBrowserSettings.Blurb>

  <OutOfBrowserSettings.WindowSettings>

    <WindowSettings Title="Pencere adı" />

  </OutOfBrowserSettings.WindowSettings>

  <OutOfBrowserSettings.Icons>

    <Icon Size="16,16">1.png</Icon>

    <Icon Size="32,32">2.png</Icon>

    <Icon Size="48,48">3.png</Icon>

    <Icon Size="128,128">4.png</Icon>

  </OutOfBrowserSettings.Icons>

</OutOfBrowserSettings>

Visual Studio 2008'de Design arayüzü? Artık yok! Yanlış duymadınız :) artık Visual Studio 2008 içerisinde Silverlight XAML dosyalarını açtığınızda otomatik olarak XAML kod kısmı açılacak ve Preview kısmı olmayacak. Aslında bu kadar çok doğru bir karar çünkü design modunda "design" yapılamadığı için :) zaten pek de anlamlı olmuyordu ve gereksiz yere işleyişi yavaşlatıyordu. Visual Studio 2010'a kadar Silverlight projelerinde Visual Studio içerisinde design penceresi olmayacak.

Silverlight.js dosyasında ufak değişiklikler var. İsteyenler hemen buradan indirebilirler: http://code.msdn.microsoft.com/silverlightjs

.NET Ria Services artık Go-Live lisansına sahip. Yani sorumluluk size ait olsa da kullandığınız projeleri yayınlayabilirsiniz. Fakat bu tabi ki RIA Services'in release olduğu anlamına gelmiyor.

Eminim ki unuttuğum bir çok değişiklik veya yenilik vardır. Onları da en kısa zamanda ayrı ayrı makaleler olarak sizlerle paylaşmak üzere...

Saturday, July 11, 2009 7:03:06 PM (GTB Standard Time, UTC+02:00)  #    Comments [2]   Silverlight 3.0  | 
 Friday, July 03, 2009

Silverlight içerisinde veri validasyonu konusunda validasyon kontrollerimiz yok gibi gözükse de aslında arka planda işimize yarayabilecek ilginç bir sistem var. Bu sistemi hem tasarımcı hem de yazılımcı beraber kullanırlarsa ortaya çok güzel sonuçlar çıkabilir. Bu yazımızda nesne bazlı veri validasyonunu nasıl yapabiliriz konusuna göz atacağız.

Hemen örneğimize başlama yoluna uygulamamızın ekranına bir DataGrid koyalım. Bir sonraki adımda uygun nesneleri yaratarak bu Grid'e bağlayacağız sonrasında da Grid içerisinde hücre başına validasyon yapmaya çalışacağız.

[XAML]

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

       x:Class="SilverlightApplication81.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="400" Height="300">

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

        <data:DataGrid x:Name="myGrid"></data:DataGrid>

    </Grid>

</UserControl>

Görsel tarafı en azından şimdilik hallettiğimize göre hemen veri tarafına geçip işimizi görecek kadar örnek veri yaratalım. İlk olarak verimiz içerisinde her nesneyi tanımlayacak olan Urun sınıfını hazırlıyoruz.

[VB]

    Public Class Urun

 

        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 PSatis As Integer

        Public Property Satis() As Integer

            Get

                Return PSatis

            End Get

            Set(ByVal value As Integer)

                PSatis = value

            End Set

        End Property

 

    End Class

Göreceğiniz üzere Urun sınıfının sadece iki tane Property'si var. Bunlardan biri ürünün adını taşırken diğeri ise toplam satış rakamını taşıyacak. Bu sınıfımızdan bir liste yaratarak rastgele urunler ekleyip Grid'imize basit bir şekilde bağlayacağız.

[VB]

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

        Dim Liste As New List(Of Urun)

        For index As Integer = 1 To 10

            Liste.Add(New Urun() With {.Adi = "Ürün Adi" & index, .Satis = Rnd() * 50})

        Next

 

        myGrid.ItemsSource = Liste

    End Sub

Bütün bu manzara içerisinde bir anda aklınızda "ürün isimleri boş bırakılmasın" şeklinde bir validasyon geldiğini varsayalım. Peki ne yapacağız? Aslında konu tahmin edeceğinizden çok daha basit. İlk olarak validasyon işlemini yapabileceğimiz basit bir yer var, orası da Urun sınıfımızın Property'lerinin Set durumu.

[VB]

       Private PAdi As String

        Public Property Adi() As String

            Get

                Return PAdi

            End Get

            Set(ByVal value As String)

                If String.IsNullOrEmpty(value) Then

                    Throw New ValidationException("isim boş geçilemez!")

                Else

                    PAdi = value

                End If

 

            End Set

        End Property

Yukarıdaki kod içerisinde sadece tek bir Property'nin set ve get durumunu görüyorsunuz. İlk baştaki örneğimizden farklı olarak Set işleminde gelen value'yu kontrol ediyoruz. Eğer Grid içerisinde bu Property'e denk gelen TextBox boş bırakılırsa doğan olarak arka tarafa grid boş değer döndürerek söz konusu nesnenin property'sini değiştirmeye çalışacak. İşte tam da o notkada devreye girip kontrolleri yapıp gerekiyorsa bir ValidationException fırlatıyoruz. Exception fırlatırken kullanıcıya vermek istediğimiz mesajı da parametre olarak constructor'a geçebiliriz.

Eğer projeyi bu hali ile Debug modunda açarsanız hatayı Debug tarafında görürsünüz. Oysa projeyi Debug mode yerine normal şekilde çalıştırırsanız aşağıdaki manzara ile karşılaşacaksınız.

Basit bir validasyon sonucu...
Basit bir validasyon sonucu...

Gördüğünüz gibi manzara gerçekten çok hoş. Görsel olarak da güzel bir animasyon ile sahneye gelen bu tooltip çoğu validasyon ihtiyacınızı giderecektir. Grid içerisinde herhangi bir ek işleme gerek kalmadan validasyonu çalışır hale getirmiş olsak da farklı kontrollerde Binding ayarlarken vermeniz gereken ek parametreler de mevcut.

[XAML]

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

       x:Class="SilverlightApplication81.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="400" Height="300">

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

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

            <data:DataGrid.Columns>

                <data:DataGridTemplateColumn>

                    <data:DataGridTemplateColumn.CellTemplate>

                        <DataTemplate>

                            <TextBox Text="{Binding Adi}" />

                        </DataTemplate>

                    </data:DataGridTemplateColumn.CellTemplate>

                </data:DataGridTemplateColumn>

            </data:DataGrid.Columns>

        </data:DataGrid>

    </Grid>

</UserControl>

Gridimizi yukarıdaki şekli ile biraz değiştirdik. Artık DataGrid ile beraber gelen hazır kolonları değil de özel kolon tasarımlarını kullandığımızı düşünelim. Bizim örneğimizde sadece Adi kolonunu ekledik ve bu kolonun tasarımına da bir TextBox koyduk. Böylece bu kolon sürekli TextBox olarak gözükecek. Peki acaba bu TextBox'ı da boş bıraktığımızda validasyon hatasını görebilecek miyiz? Maalesef hayır.

[XAML]

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

       x:Class="SilverlightApplication81.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="400" Height="300">

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

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

            <data:DataGrid.Columns>

                <data:DataGridTemplateColumn>

                    <data:DataGridTemplateColumn.CellTemplate>

                        <DataTemplate>

                            <TextBox Text="{Binding Mode=TwoWay, NotifyOnValidationError=true, Path=Adi, ValidatesOnExceptions=true}" />

                        </DataTemplate>

                    </data:DataGridTemplateColumn.CellTemplate>

                </data:DataGridTemplateColumn>

            </data:DataGrid.Columns>

        </data:DataGrid>

    </Grid>

</UserControl>

Bu noktada değiştirmemiz gereken şey Binding mekanizmamızla alakalı ayarlar. İlk olarak Mode'u TwoWay yaparak buradaki değişikliklerin de nesne tarafına yansımasını sağlıyoruz ki nesne tarafındaki validasyon gerçekleşsin. Ayrıca NotifyOnValidationError ve ValidatesOnExceptions özelliklerini de True yaparak gerekli hataların gösterilmesini sağlıyoruz.

Özel binding ile validasyon.
Özel binding ile validasyon.

Görselliği değiştirelim...

Herhangi bir kontrolün validation işlemi sonrasında InValid olması haline gösterilmek üzere InValid State'lerinin bulunması gerekir. Hali hazırda Silverlight içerisindeki çoğu kontrolde bu state'ler zaten hazır ve tasarımları yapılmış durumda. Eğer yukarıdaki örneğimizi Expression Blend ile açar ve TextBox'ın Template'ini editlemek isterseniz söz konusu menüde "Edit a copy" demeyi unutmayın.

TextBox kontolümüzü Template'ini kopyalayarak editleyelim.
TextBox kontolümüzü Template'ini kopyalayarak editleyelim.

Böylece Blend sizin yerinize standart şablonu kopyalayacaktır. Bunu yaptıktan sonra söz konusu şablonun için VisualStateManager'da InValid statelerini bulabilir ve bu stateler için hazırlanmış tasarımları değiştirebilirsiniz.

Blend içerisinde TextBox'a ait ayrı stateler...
Blend içerisinde TextBox'a ait ayrı stateler...

State'ler arası tasarımları değiştirdikten sonra büyük ihtimal ile InValid durumunde gösterilen Tooltip'in de tasarımını değiştirebilmek isteyeceksiniz. Blend içerisinden şu an için (Blend 3 MIX Preview sürümü) bir çözüm bulunmasa da kod tarafından gerekli özelleştirmeleri yapabiliriz. Textbox'ımızın Template'inden bir kopya alıp editlerken aslında beraberindeki Tooltip'in tasarımının da bir kopyası uygulamamızın sayfasında Resource olarak saklanıyor.

[XAML]

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

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

       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows" xmlns:System="clr-namespace:System;assembly=mscorlib" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

       x:Class="SilverlightApplication81.MainPage" Width="400" Height="300" mc:Ignorable="d">

    <UserControl.Resources>

        <ControlTemplate x:Key="ValidationToolTipTemplate">

            <Grid x:Name="Root" Margin="5,0" Opacity="0" RenderTransformOrigin="0,0">

                <vsm:VisualStateManager.VisualStateGroups>

                    <vsm:VisualStateGroup x:Name="OpenStates">

                        <vsm:VisualStateGroup.Transitions>

                            <vsm:VisualTransition GeneratedDuration="0"/>

                            <vsm:VisualTransition GeneratedDuration="0:0:0.2" To="Open">

                                <Storyboard>

                                    <DoubleAnimationUsingKeyFrames Storyboard.TargetName="xform" Storyboard.TargetProperty="X">

                                        <SplineDoubleKeyFrame KeyTime="0:0:0.2" Value="0"/>

                                    </DoubleAnimationUsingKeyFrames>

                                    <DoubleAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity">

                                        <SplineDoubleKeyFrame KeyTime="0:0:0.2" Value="1"/>

                                    </DoubleAnimationUsingKeyFrames>

                                </Storyboard>

                            </vsm:VisualTransition>

                        </vsm:VisualStateGroup.Transitions>

                        <vsm:VisualState x:Name="Closed">

                            <Storyboard>

                                <DoubleAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity">

                                    <SplineDoubleKeyFrame KeyTime="0" Value="0"/>

                                </DoubleAnimationUsingKeyFrames>

                            </Storyboard>

                        </vsm:VisualState>

                        <vsm:VisualState x:Name="Open">

                            <Storyboard>

                                <DoubleAnimationUsingKeyFrames Storyboard.TargetName="xform" Storyboard.TargetProperty="X">

                                    <SplineDoubleKeyFrame KeyTime="0" Value="0"/>

                                </DoubleAnimationUsingKeyFrames>

                                <DoubleAnimationUsingKeyFrames Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity">

                                    <SplineDoubleKeyFrame KeyTime="0" Value="1"/>

                                </DoubleAnimationUsingKeyFrames>

                            </Storyboard>

                        </vsm:VisualState>

                    </vsm:VisualStateGroup>

                </vsm:VisualStateManager.VisualStateGroups>

                <Grid.RenderTransform>

                    <TranslateTransform x:Name="xform" X="-25"/>

                </Grid.RenderTransform>

                <Border Margin="4,4,-4,-4" Background="#152A2E31" CornerRadius="4"/>

                <Border Margin="3,3,-3,-3" Background="#252A2E31" CornerRadius="4"/>

                <Border Margin="2,2,-2,-2" Background="#352A2E31" CornerRadius="4"/>

                <Border Margin="1,1,-1,-1" Background="#452A2E31" CornerRadius="4"/>

                <Border Background="#FFDC000C" CornerRadius="4"/>

                <Border CornerRadius="4" Background="Green">

                    <TextBlock Margin="8,3,8,4" MaxWidth="250" UseLayoutRounding="false" Foreground="White" Text="{Binding Path=(Validation.Errors)[0].Exception.Message}" TextWrapping="Wrap"/>

                </Border>

            </Grid>

        </ControlTemplate>

        <Style x:Key="TextBoxStyle1" TargetType="TextBox">

            <Setter Property="BorderThickness" Value="1"/>

            <Setter Property="Background" Value="#FFFFFFFF"/>

            <Setter Property="Foreground" Value="#FF000000"/>

            <Setter Property="Padding" Value="2"/>

Yukarıdaki örnek uygulamaızın XAML kodunun ilk altmış satırını görebilirsiniz. Bu altmış satır içerisinde önemli olan şey ValidationToolTipTemplate adındaki ControlTemplate nesnesi. Söz konusu nesne TextBox'ın Template'i içerisinde de kullanılıyor. Aşağıdaki TextBox'ımızın Template'ine ait XAML kodu içerisinde yukarıdaki ControlTemplate'in nasıl kullanıldığını görebilirsiniz.

[XAML]

<Border x:Name="ValidationErrorElement" Visibility="Collapsed" BorderBrush="#FFDB000C" BorderThickness="1" CornerRadius="1">

                                <ToolTipService.ToolTip>

                                    <ToolTip x:Name="validationTooltip" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" Template="{StaticResource ValidationToolTipTemplate}" Placement="Right" PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}">

                                        <ToolTip.Triggers>

                                            <EventTrigger RoutedEvent="Canvas.Loaded">

                                                <BeginStoryboard>

                                                    <Storyboard>

                                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="validationTooltip" Storyboard.TargetProperty="IsHitTestVisible">

                                                            <DiscreteObjectKeyFrame KeyTime="0">

                                                                <DiscreteObjectKeyFrame.Value>

                                                                    <System:Boolean>true</System:Boolean>

                                                                </DiscreteObjectKeyFrame.Value>

                                                            </DiscreteObjectKeyFrame>

                                                        </ObjectAnimationUsingKeyFrames>

                                                    </Storyboard>

                                                </BeginStoryboard>

                                            </EventTrigger>

                                        </ToolTip.Triggers>

                                    </ToolTip>

                                </ToolTipService.ToolTip>

                                <Grid Height="12" HorizontalAlignment="Right" Margin="1,-4,-4,0" VerticalAlignment="Top" Width="12" Background="Transparent">

                                    <Path Fill="#FFDC000C" Margin="1,3,0,0" Data="M 1,0 L6,0 A 2,2 90 0 1 8,2 L8,7 z"/>

                                    <Path Fill="#ffffff" Margin="1,3,0,0" Data="M 0,0 L2,0 L 8,6 L8,8"/>

                                </Grid>

                            </Border>

TextBox'ın Template'i içerisinde bulunan bir Border nesnesine Tooltip kontrolü atanmış. Söz konusu Tooltip kontrolünün şablonu ise doğrudan ValidationToolTipTemplate adındaki şablonla eşleştirilmiş durumda. Böylece biz doğrudan ValidationToolTipTemplate nesnesini değiştirerek validasyon esnasında görülen Tooltip'i değiştirebiliriz.

[XAML]

                <Border CornerRadius="4" Background="Green">

                    <TextBlock Margin="8,3,8,4" MaxWidth="250" UseLayoutRounding="false" Foreground="White" Text="{Binding Path=(Validation.Errors)[0].Exception.Message}" TextWrapping="Wrap"/>

                </Border>

Yukarıdaki kesiti ValidationToolTipTemplate içerisinden aldım. Tooltip şablonu içerisinde buradaki Textblock'un Text'i görüldüğü üzere bir Binding ile beraber geliyor. Arkaplanda yolladığımız Exception'ların mesajlarının Tooltip içerisinde gözükmesini sağlayan sistem bu bindingden ibaret. Eğer burayı silersiniz mesajlarınız gözükmeyecektir o nedenle tasarımınızı değiştirirken bu noktaya dikkat etmekte fayda var.

Hepinize kolay gelsin.

Friday, July 03, 2009 5:16:38 PM (GTB Standard Time, UTC+02:00)  #    Comments [2]   Silverlight 3.0  | 
 Thursday, July 02, 2009

Silverlight içerisinde herhangi bir nesneye şeffaf fon vermek veya bir renk seçerken Alpha kanalını da ayarlamak mümkün fakat elinizde hali hazırda bir görsel varsa ve bu görsel içerisindeki belirli bir rengin şeffafa çevrilmesini istiyorsanız ne yapabilirsiniz?

Ne zaman lazım olacak ki?

"Chroma Key" denilen bu efektin en çok kullanıldığın yer fotoğraf veya videoların birleştirilmesi noktasında karşımıza çıkıyor. Örneğin koşan bir adam videosu var elinizde fakat bu adamı kendi fon görselinizde koşturmak istiyorsunuz. Bu durumda videonun fonunu silip yerine başka bir şeyler koyabiliyor olmanız gerekir. Aynı şey fotoğraflar için de geçerli. Tabi her konuda olduğu gibi bu konuda da belirli kurallar var. Bir şekilde bir resmin ve videonun fonunu silebilmek istiyorsanız aslında o videonun da uygun şekilde hazırlanmış olması gerekiyor.

Chroma Key'e uygun bir video.
Chroma Key'e uygun bir video.

Yukarıda görmüş olduğunuz kare bir videonun alınma. Fonun yeşil olması tabi ki şart değil. Bu notkada önemli olan şey video içerisinden almak istediğiniz obje ile fonun birbirine tezat renkler taşıması. Böylece bu videoyu gösterirken yeşil pikselleri bularak şeffaf hale çevirebileceğiz. Durum böyle olunca da rahatlıkla bu videonun arkasına başka bir video veya herhangi bir kontrol yerleştirebiliriz.

Peki nasıl yapacağız?

Bir video veya resim dosyasından bir rengi şeffaf hale getirmek için o dosyanın görselinde her pikseli tek tek gezmemiz gerekecek fakat özellikle video dosyalarından bahsedersek video içerisinde her kareyi de tek tek incelemek epey zahmetli olabilir. Oysa Silverlight 3.0 ile beraber gelen yeni özelliklerden biri olan PixelShader efektleri tam da bu iş için biçilmiş kaftan.

PixelShader efektleri zaten uygulandıkları görselin her pikseli gösterilmeden önce o pikseldeki renk değerlerine müdahale edebilen efektler şeklinde tasarlanıyor. Silverlight 3.0 ile Piksel Shader kullanımı makalemizde daha detaylı bilgi bulabilirsiniz.

Biz örneğimizde kullanmak üzere yeni bir PixelShader efekti yaratacağız. Bu efekt kendisine gelen her pikseli inceleyerek Chrome Key rengine yakın bir renk var ise o rengi şeffaf hale çevirecek. Tüm bu kontrol yapılırken tabi ki eldeki renk ile Chrome key renginin yakınlığı konusunda da bir parametre kullanmak durumundayız. Özetlemek gerekirse, eğer videomuzun fonu yeşil ile Chrome Key rengimiz de yeşil olacak ve PixelShader efektine yeşil rengini vereceğiz fakat duruma göre tam olarak RGB değerleri belli olan bir yeşil rengini değil de o yeşil rengine çok yakın diğer yeşilleri de şeffaflaştırmak isteyebiliriz. İşte bu yakınlık değerini Threshold denilen ayrı bir parametrede tutacağız.

[HLSL]

sampler2D input : register(S0);

 

float red : register(C0);

float green : register(C1);

float blue : register(C2);

float threshold : register(C3);

 

float4 main(float2 pos : TEXCOORD) : COLOR

{

    float4    color;

    float    rkey, gkey, bkey, thresh;

 

    thresh = threshold;               

    rkey = red/255.0;

    gkey = green/255.0;

    bkey = blue/255.0;

 

    color = tex2D(input, pos);

 

    if (abs(color.r - rkey) <= thresh)

    {

        if (abs(color.g - gkey) <= thresh)

        {

            if (abs(color.b - bkey) <= thresh)

            {

                // Şeffaflaştır

                color.a = 0.0;

            }

        }

    }

 

    return color;

}

Yukarıdaki kod efektimizim HLSL ile yazılmış hali. Konumuz HLSL olmadığı için detaylarına girmeyeceğim. Önemli olan bu şekilde HLSL efektini yazdıktan sonra efekti compile edip ortaya çıkan PS dosyasını Silverlight ile kullanmak. PixelShader efektlerinin Silverlight ile kullanımı konusunda Silverlight 3.0 ile Piksel Shader makalesini incelemeyi unutmayın.

[C#]

    public class AlphaShader : ShaderEffect

    {

        public AlphaShader()

        {

            this.PixelShader = new PixelShader();

            this.PixelShader.UriSource = new Uri("/Alpha.ps", UriKind.Relative );

 

            this.UpdateShaderValue(InputProperty);

            this.UpdateShaderValue(RedProperty);

            this.UpdateShaderValue(GreenProperty);

            this.UpdateShaderValue(BlueProperty);

            this.UpdateShaderValue(ThreshProperty);

        }

 

        public Brush Input

        {

            get { return (Brush)GetValue(InputProperty); }

            set { SetValue(InputProperty, value); }

        }

 

        public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(AlphaShader), 0);

        public static readonly DependencyProperty RedProperty = DependencyProperty.Register("Threshold", typeof(double), typeof(AlphaShader), new PropertyMetadata(0.0, PixelShaderConstantCallback(0)));

        public static readonly DependencyProperty GreenProperty = DependencyProperty.Register("Red", typeof(double), typeof(AlphaShader), new PropertyMetadata(0.0, PixelShaderConstantCallback(1)));

        public static readonly DependencyProperty BlueProperty = DependencyProperty.Register("Green", typeof(double), typeof(AlphaShader), new PropertyMetadata(0.0, PixelShaderConstantCallback(2)));

        public static readonly DependencyProperty ThreshProperty = DependencyProperty.Register("Blue", typeof(double), typeof(AlphaShader), new PropertyMetadata(0.25, PixelShaderConstantCallback(3)));

 

        public double Threshold

        {

            get { return (double)GetValue(ThreshProperty); }

            set { SetValue(ThreshProperty, value); }

        }

 

        public double Red

        {

            get { return (double)GetValue(RedProperty); }

            set { SetValue(RedProperty, value); }

        }

 

        public double Green

        {

            get { return (double)GetValue(GreenProperty); }

            set { SetValue(GreenProperty, value); }

        }

 

        public double Blue

        {

            get { return (double)GetValue(BlueProperty); }

            set { SetValue(BlueProperty, value); }

        }

    }

Yukarıdaki sınıf bizim AlphaShader adını verdiğimiz kendi PixelShader efektimizin Silverlight içerisindeki tanımı. Söz konusu PixelShader efekti uygulama içerisindeki compile edilmiş Alpha.ps efektini kullanıyor. Gördüğünüz tüm Dependency Propoerty'ler de aslında HLSL'e gönderdiğimiz parametreler. Chroma Key rengimizin RGB değerlerini ayrı ayrı gönderirken Threshold değerini de 1 ile 0 arasında gönderiyoruz.

Artık efektimiz hazır olduğuna göre uygulamamızın arayüzünü de hazırlayabiliriz.

[XAML]

<UserControl x:Class="SilverlightApplication30.MainPage"

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

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

   Width="800" Height="600"

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

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

        <Image Margin="8,18,86,69" Source="Desert.jpg"/>

        <MediaElement

           x:Name="myVideo"

           Margin="30,32,109,89"

           Source="ornek.wmv"/>

    </Grid>

</UserControl>

Gördüğünüz gibi örneğimizde basit bir şekilde bir resim ve üzerinde de bir video bulunuyor. Amacımız video içerisindeki yeşil kısmı kaldırıp sanki video içerisinde arkadaş resmin üzerinde hareket ediyormuş izlenimi yaratmak. Şimdi tekrar kod tarafına geçerek efektimizi MediaElement'imize uygulayalım.

[C#]

        void MainPage_Loaded(object sender, RoutedEventArgs e)

        {

            var x = new AlphaShader();

            x.Threshold = 0.37;

            x.Red = 91;

            x.Green =254;

x.Blue = 125;

            myVideo.Effect = x;

        }

Gördüğünüz gibi daha önceki adımlarda yarattığımız AlphaShader sınıfımızdan bir kopya alıp tüm Property'lerine uygun değerleri veriyoruz. Sonrasında da artık efektimizi MediaElement'in Effect'ine eşitliyoruz. Peki nasıl bir sonuç mu alıyoruz? Sonuç aşağıda.

PixelShader ile Chrome Key efekti.
PixelShader ile Chrome Key efekti.

Gördüğünüz gibi videomuz içerisindeki yeşil kısımlar tamamen yok oldu ve onun yerine çok daha farklı bir görsel yerleştirebildik. Videomuz oynarken de herhangi bir sorun olmayacak ve PixelShader efektimiz sürekli çalışarak bulduğu yeşil pikselleri şeffaf hale getirecek.

Hepinize kolay gelsin.

Thursday, July 02, 2009 11:03:09 AM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Silverlight 3.0  | 
 Wednesday, July 01, 2009

Sayfalama senaryoları her zaman ihtiyacımız olan işlevselliklerden olmuştur. En basit kullanımı ile bir DataGrid içerisindeki verinin sayfalanabilir olarak gösterilebilmesi gerekir. Bu sayfalama işlemi bazen sunucu taraflı bazen de istemci taraflı yapılabilir. Bu makalede Silverlight 3.0 ile beraber gelen DataGrid kontrolünü kullanarak sayfalama işlemlerine göz atacağız.

Sayfalama için görsel kontrol?

Sayfalama işlemi için doğrudan Silverlight 3.0 ile beraber gelen DataPager kontrolünü kullanacağız. Bu kontrol kendisine verilen PagedCollectionView nesnesine göre sayfalama yapabiliyor. Tabi söz konusu veriyi doğrudan DataPager'a vermek pek anlamlı değil o nedenle verimizi yaratıp ilk olarak bir gride bağlayalım. Sonrasında Grid içerisindeki veriyi sayfalaması üzerine DataPager'a gerekli komutu vereceğiz.

İstemci taraflı sayfalama...

İlk örneğimizde tüm veriyi istemci tarafında sayfalayacağız. Bu gibi bir sayfalamayı ancak tüm veriyi istemci tarafına uygun bir sürede alabiliyorsanız yapabilirsiniz. Eğer elinizdeki veri bir defada istemciye yüklenemeyecek durumda ise sayfalamayı da tabi ki sunucu tarafında yapmak gerekecektir. İstemci tarafındaki sayfalama aslında sadece görsel amaçlarla ve ileriki adımlarda filtreme için kullanılabilir.

[XAML]

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

       xmlns:layoutToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Layout.Toolkit"

       xmlns:dataControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm"

       x:Class="SilverlightApplication77.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="400" Height="300">

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

        <StackPanel>

            <data:DataGrid x:Name="Grid"></data:DataGrid>

            <dataControls:DataPager x:Name="Pager"></dataControls:DataPager>

        </StackPanel>

    </Grid>

</UserControl>

Yukarıdaki örneğimizde bir DataGrid ve bir de DataPager kontrolü bulunuyor. Söz konusu kontrollere gerekli isimleri de vererek kod tarafından ulaşılabilir hale getirip uygulamamıza devam edebiliriz. Elimizde basit bir isim listesi olduğunu düşünürsek ilk olarak bu listeyi bir PagedCollectionView'a çevirmemiz gerekecek.

[VB]

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

        Dim AnaListe As New List(Of String)

        AnaListe.Add("Daron")

        AnaListe.Add("Burak")

        AnaListe.Add("Muammer")

        AnaListe.Add("Uğur")

        Dim Liste As New PagedCollectionView(AnaListe)

 

        Grid.ItemsSource = Liste

    End Sub

Yukarıdaki kod içerisinde basit bir String List'in PagedcollectionView'a çevrilmesini görebilirsiniz. Aslında işlem epey basit çünkü PagedCollectionView zaten constructor'larından birinde IEnumarable interface'ini implemente eden obje istiyor. Elimizdeki liste de buna uyduğunu göre doğrudan çeviri işlemini hızlıca halledebiliyoruz. Son olarak eldeki PagedCollectionView'u da Grid adındaki DataGrid'imize aktarmamız ilk aşamanın tamamlanması için yeterli olacaktır.

[XAML]

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

       xmlns:layoutToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Layout.Toolkit"

       xmlns:dataControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm"

       x:Class="SilverlightApplication77.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="400" Height="300">

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

        <StackPanel>

            <data:DataGrid x:Name="Grid"></data:DataGrid>

            <dataControls:DataPager x:Name="Pager" PageSize="2" DisplayMode="PreviousNextNumeric"

                   Source="{Binding ItemsSource, ElementName=Grid}"></dataControls:DataPager>

        </StackPanel>

    </Grid>

</UserControl>

Şu ana kadar DataPager kontrolümüz ile DataGrid'in birbirinden haberi yoktu. İşte yukarıdaki kod içerisinde bu iki kontrolü birbirinden haberdar ediyoruz ve DataGrid'in ItemsSource'un kodumuz ile vermiş olduğumuz PagedViewCollection'ın DataPager tarafından kontrol edilmesini sağlıyoruz.

Tüm bu işlemleri yapabilmek için basit bir Element Binding kullanarak DataPager'ın Source Property'sini Grid'in ItemsSource'una bind etmemiz yeterli oluyor. Artık elimizde istemci tarafında sayfalama ile çalışabilen bir DataGrid var. DataGrid'in her sayfasında kaç kayıt gösterileceğini PageSize özelliğinden ayarlayabilirsiniz.

Sayfalama desteği ile bir DataGrid kontrolü.
Sayfalama desteği ile bir DataGrid kontrolü.

DataPager ile ilgili farklı görsel özellikler için DisplayMode Enumaration'ını incelemenizi tavsiye ederim.

Sunucu taraflı sayfalama...

Eğer veri miktarınız çok yüksek ise maalesef tüm veriyi istemci tarafına almak gibi bir şansınız olmayabilir. Bu gibi durumlarda Silverlight uygulamasının sürekli olarak istediği sayfanın sayısını sunucuya göndererek sadece o sayfadaki veriyi sunucudan alması gerekecektir. Tahmin edebileceğiniz üzere bu işin iki farklı ayağı var; birincisi sunucu tarafında uygun sayfaya ait veriyi sayfa numarasına göre döndürebilen bir web servisinin hazırlanması, ikincisi ise bu servisin Silverlight tarafından doğru olarak kullanılabilmesi.

[Service / VB]

    Dim DB As New DataClasses1DataContext

 

    <OperationContract()> _

    Public Function VeriGetir(ByVal Atla As Integer, ByVal Al As Integer) As List(Of Urunler)

        Return (From inc In DB.Urunlers Skip (Atla) Take (Al) Select inc).ToList()

    End Function

 

    <OperationContract()> _

    Public Function KayitSayisi() As Integer

        Return (From inc In DB.Urunlers Select inc).Count

    End Function

Yukarıda sunucu tarafındaki servisimizin kodunu bulabilirsiniz. Toplamda iki servise ihtiyacımız var; bu servislerinden biri veritabanında bulunan toplam kayıt sayısını alarak istemciye göndermek zorunda. Böylece DataPager kontrolü toplam kaç sayfalık bir veri ile uğraştığını bilebilecek. Diğer yandan sayfa sayfa duruma göre veri döndürebilecek bir servis de kesinlikle şart. Tüm bunları LINQ2SQL ile beraber yaparsanız sayfalamanız otomatik olarak SQL tarafına kadar taşınmış oluyor. Aksi halde sizin SQL tarafındaki sayfalama mekanizmasını da kurmanız gerekecektir.

Özellikle VeriGetir adındaki metodumuzu incelemek gerekirse toplamda iki parametre aldığını görebiliriz. Bunlardan biri veritabanından kayıt çekilirken kaç kayıt atlanmasını gerektiğini diğeri ise kaç kayıt alınması gerektiğini belirtiyor. Örneğin her defasında 10'ar kayıt gösteriyorsak ve üçüncü sayfadaysak toplam 20 kayıt atlamamız gerektiği ve 10 kayıt almamız gerektiğini hesaplamak pek zor değil. Bu hesaplamaları Silverlight tarafında yaparak servise parametre olarak vereceğiz. Servis de bize uygun veriyi döndürecek.

[XAML]

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

       xmlns:layoutToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Layout.Toolkit"

       xmlns:dataControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm"

       x:Class="SilverlightApplication77.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="400" Height="300">

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

        <StackPanel>

            <data:DataGrid x:Name="Grid"></data:DataGrid>

            <dataControls:DataPager x:Name="Pager" PageSize="1" DisplayMode="PreviousNextNumeric"></dataControls:DataPager>

        </StackPanel>

    </Grid>

</UserControl>

Yukarıdaki XAML kodu uygulamamızın ana ekranını temsil ediyor. Bir önceki örneğimizden farklı olarak burada artık bir ElementBinding kullanmadığımı görebilirsiniz. Nedeni çok basit; artık tüm veri DataGrid üzerinde değil o nedenle DataPager gidip de Grid'in ItemsSource'u üzerinden sayfalama yapamayacak.

Not: ElementBinding mekanizmasının çalışmasını ve ilk örneğimizdeki sistemi daha profesyonel bir şekilde veriyi sunucu tarafında sayfalar hale getirmek isterseniz yapmanız gereken IPagedCollectionView interface'ini implemente eden kendi PagedCollectionView nesnenizi programlamanız. Nesne içerisinde sayfa değişim eventlarından bir çok farklı duruma erişip verinin uygun kaynaklardan alınmasını sağlayabilirsiniz. Ben kişisel olarak ne kadar bu yolun daha profesyonel olduğunu düşünsem de hem yazılım geliştirme süreci olacak hem de kalifikasyon açısından hedeflediğimiz işlevsellik için biraz yüksek seviyede kaldığını düşünüyorum. O nedenle makalede çok daha basit bir teknik kullanacağım. Eğer siz daha karışık işlevselliklere ihtiyaç duyarsanız IPagedCollectionView interface'i üzerinden ilerleyebilirsiniz.

DataPager'ı kullanmaktaki tek amacımız kullanıcıya uygun arayüzü göstermesi. Yani "sağ" "sol" düğmeleri ve "sayılar" bizim için yeterli. Bundan sonrasında ne de olsa sunucu tarafında sayfalama yapacağımıza göre DataPager'ın bize kullanıcı tarafından seçili sayfanın sayısını vermesi yeterli olacaktır. Fakat DataPager'ın çalışması için uygun bir PagedViewCollection'ın Source olarak atanmış olması şart.

[VB]

    WithEvents Servis As New ServiceReference1.Service1Client

 

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

        Servis.KayitSayisiAsync()

    End Sub

 

    Private Sub Servis_KayitSayisiCompleted(ByVal sender As Object, ByVal e As ServiceReference1.KayitSayisiCompletedEventArgs) Handles Servis.KayitSayisiCompleted

        Dim Liste As New List(Of Nullable(Of Integer))

        For index As Integer = 1 To e.Result

            Liste.Add(Nothing)

        Next

        Dim DummyList As New PagedCollectionView(Liste)

        Pager.Source = DummyList

 

        Servis.VeriGetirAsync(Pager.PageIndex * Pager.PageSize, Pager.PageSize)

    End Sub

Sayfa ilk yüklendiğinde hemen kayıt sayısını getirecek olan servisimizi çağırıyoruz. Söz konusu servisten toplam kayıt sayısı geldiği gibi sadece DataPager'a verilmek üzere boş bir List yaratıyoruz. Bu List içerisinde Item sayısı gelen kayıt sayısı ile aynı fakat List'in içindeki her Item aslında boş! DataPager'ın çalışması için aslında boş bir liste yaratıp kendisini kandırıyoruz.

[VB]

    Private Sub Pager_PageIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Pager.PageIndexChanged

        Servis.VeriGetirAsync(Pager.PageIndex * Pager.PageSize, Pager.PageSize)

    End Sub

 

    Private Sub Servis_VeriGetirCompleted(ByVal sender As Object, ByVal e As ServiceReference1.VeriGetirCompletedEventArgs) Handles Servis.VeriGetirCompleted

        Grid.ItemsSource = e.Result

    End Sub

Artık DataPager kontrolümüzün PageIndexChanged durumunu yakalayabiliriz. Her defasında sayfa değiştiğinde sayfa sayısı ile sayfa boyutunu çarpıp atlanacak olan kayıt sayısını buluyoruz. Zaten sayfa sayısı Index olarak geldiği için ilk sayfada 0 gelecek ve atlanacak kayıt sayısı da 0 olacak. Servise göndermemiz gereken ikinci parametre de zaten DataPager'ın PageSize özelliği, yani sayfa başına gösterilecek kayıt sayısı.

Son olarak bu servisimizden gelen sonucu da Grid'imize bağlarsak işlem tamamlanmış olacaktır. Artık elimizde sunucu tarafında sayfalama yapabilen bir arayüzümüz var.

Wednesday, July 01, 2009 6:17:59 PM (GTB Standard Time, UTC+02:00)  #    Comments [1]   Silverlight 3.0  | 
 Monday, June 22, 2009

Bu yazımızda Silverlight Toolkit ile beraber gelen AutoCompleteBox kontrolünü inceleyeceğiz. Bilgisayarınızda Silverlight Toolkit yükledikten sonra Toolkit içerisindeki tüm kontrolleri Visual Studio içerisinde Toolbox'ta görebilirsiniz. Ayrıca Toolkit DLL'lerini referans alarak kontrolleri elle XAML sayfalarınıza ekleme şansınız da var. Örneğin AutoCompleteBox kontrolünü inceleyecek olursak; bir sayfaya söz konusu kontrolü eklemek için ilk olarak Toolkit Assembly'lerinden System.Windows.Controls.Input Assembly'sini projenize referans almanız sonrasında da System.Windows.Controls NameSpace'i altından kontrolü bulmanız gerekecektir.

[XAML]

<UserControl xmlns:input="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input" x:Class="SilverlightApplication54.MainPage"

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

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

   Width="400" Height="300">

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

        <input:AutoCompleteBox></input:AutoCompleteBox>

    </Grid>

</UserControl>

Yukarıda gördüğünüz basit örnek içerisinde XAML root elementimizde gerekli input XML namespace'i tanımlanmış durumda. Böylece artık input namespace'i üzerinden AutoCompleteBox kontrolümüzü kullanabiliriz. Daha önce de bahsettiğim üzere eğer Visual Studio'nun Toolbox'ından söz konusu kontrolü kod tarafına sürükle bırak tekniği ile yerleştirirseniz zaten tüm bu işlemleri otomatik olarak gerçekleştirilebiliyor.

Haydi veriye bağlayalım!

Kontrolümüzü artık sahneye yerleştirdiğimize göre hemen çalıştırmak ve sonucu görmek isteyeceğiz fakat onun öncesinde tabi ki bir veri bağlantısı yapmamız şart. Kullanıcılar kutuya birşey yazarken AutoComplete kısmında neler gösterilecek?

[XAML]

<UserControl xmlns:input="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input" x:Class="SilverlightApplication54.MainPage"

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

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

   Width="400" Height="300">

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

        <input:AutoCompleteBox x:Name="myAutoCompleteBox" VerticalAlignment="Top"></input:AutoCompleteBox>

    </Grid>

</UserControl>

[VB]

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

        Dim Secenekler() As String = {"Silverlight", "Silvernight", "Silverfight", "SilverMonth"}

        myAutoCompleteBox.ItemsSource = Secenekler

    End Sub

[C#]

        void MainPage_Loaded(object sender, RoutedEventArgs e)

        {

            string[] Secenekler = { "Silverlight", "Silvernight", "Silverfight", "SilverMonth" };

            myAutoCompleteBox.ItemsSource = Secenekler;

        }

Yukarıdaki kod içerisinde aslında basit bir şekilde tüm seçenekleri doğrudan AutoCompletebox'ın ItemsSource'una atamış oluyoruz. Sonrasında AutoCompleteBox kendi içerisinde gerekli filtrelemeleri kullanıcılar metin giriş yaptıkça gerçekleştirerek sonucu gösteriyor.

Silverlight içerisinde basit bir AutoCompleteBox
Silverlight içerisinde basit bir AutoCompleteBox

Peki ya web servisinden alacak olsak sonuçları?

Bir önceki örneği incelerken büyük ihtimal ile kendi içinizden "Eee peki kocaman bir listemiz varsa hepsini istemciye mi alacağız?" diye sormuşsunuzdur. Tabi ki hayır. Aslında normal şartlarda yapmamız gereken şey AutoCompleteBox içerisine yazılan kelimeyi sunucuya göndermek ve geri gelen sonucu da AutoCompleteBox'ın AutoComplete bölümünde göstermek. İşte bu işlemi yapabilmek için AutoCompletebox'ın Populating eventını kullanıyoruz.

Örneğimizde Tavsiye adında bir webmethod kullanacağız. Web servisinin yazımı kısmına girmeyeceğim fakat kabaca bahsetmek gerekirse istemciden aranacak kelimeyi alan ve veritabanına gidip uygun AutoComplete seçeneklerini bir list olarak döndüren bir webmethod yeterli olacaktır.

[VB]

    Private Sub myAutoCompleteBox_Populating(ByVal sender As Object, ByVal e As System.Windows.Controls.PopulatingEventArgs) Handles myAutoCompleteBox.Populating

        e.Cancel = True

        Dim servis As New ServiceReference1.Service1Client

        AddHandler servis.TavsiyelerCompleted, AddressOf Servis_TavsiyelerCompleted

        servis.TavsiyelerAsync(myAutoCompleteBox.Text)

    End Sub

[C#]

        public MainPage()

        {

            InitializeComponent();

            this.Loaded += new RoutedEventHandler(MainPage_Loaded);

            myAutoCompleteBox.Populating += new PopulatingEventHandler(myAutoCompleteBox_Populating);

        }

 

        void myAutoCompleteBox_Populating(object sender, PopulatingEventArgs e)

        {

            e.Cancel = true;

            ServiceReference1.Service1Client servis = new ServiceReference1.Service1Client();

            servis.TavsiyelerCompleted += Servis_TavsiyelerCompleted;

            servis.TavsiyelerAsync(myAutoCompleteBox.Text);

        }

Yukarıdaki kodumuzda AutoCompleteBox'ın Populating durumunu yakalayarak hemen e.Cancel değerini True yaparak Populate işlemini iptal ediyoruz. Neden mi? Çünkü Populate işlemi, yani AutoComplete listesinin gösterilmesi işlemi şu anda yapılamaz. İşlemi yapabilmek için bizim Webmethod'umuza parametreyi vermemiz, söz konusu bilginin sunucuya gitmesi ve en önemli cevabın gelmesi gerekiyor! Cevap gelmeden Populate edemeyiz! O nedenle burada hemen Populate işlemini iptal ediyoruz. Sonrasında WebServis'imizden bir kopya alıp Completed durumu da ayrı bir handler'a bağlayıp elimizdeki AutoCompleteBox içerisinde metni Tavsiyeler adındaki Webmethod'umuza gönderiyoruz.

[VB]

    Private Sub Servis_TavsiyelerCompleted(ByVal sender As Object, ByVal e As ServiceReference1.TavsiyelerCompletedEventArgs)

        myAutoCompleteBox.ItemsSource = e.Result

        myAutoCompleteBox.PopulateComplete()

    End Sub

[C#]

        private void Servis_TavsiyelerCompleted(object sender, ServiceReference1.TavsiyelerCompletedEventArgs e)

        {

            myAutoCompleteBox.ItemsSource = e.Result;

            myAutoCompleteBox.PopulateComplete();

        }

Populating durumunda çağırdığımız web servisimizin Completed durumunda artık veri elimizde olduğunda göre doğrudan AutoCompleteBox'ımızın ItemsSource'una verebiliriz. Son olarak tabi ki Populate işlemini bitirdiğimizi de AutoCompleteBox'a belirtmemiz gerek ki hemen sonucu kullanıcıya göstersin.

Arama şeklini nasıl değiştiririz?

Varsayılan ayarları ile AutoCompleteBox'lar kendilerine verilen verinin içinde arama yaparken kullanıcının yazdığı metinile başlayan sonuçları gösterirler. Bunun değiştirmenin yolu AutoCompletebox'ların SearchMode özelliğinde yatıyor.

Veri kaynağında nasıl arama yapılacağını belirleyin.
Veri kaynağında nasıl arama yapılacağını belirleyin.

Peki bu arama sistemleri size yetmedi ve daha da özelleştirmek isterseniz ne yapabilirsiniz? Filtreleme işlemini tamamen ele alma şansınız da var. Bu durum özellikle AutoCompleteBox'a kendi nesne tiplerinizi bağladığınızda çok anlamlı olabilir. Filtreleme esnasından belki de sadece ItemsSource'a verdiğiniz nesnelerin belirli Property'lerine göre ayrı ayrı mantıklarıda aramalar yapılsın isteyebilirsiniz.

[VB]

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

        myAutoCompleteBox.ItemFilter = AddressOf Arama

    End Sub

 

    Function Arama(ByVal metin As String, ByVal nesne As ServiceReference1.Service1Urun) As Boolean

        If nesne.Adi.Contains(metin) Then

            Return True

        Else

            Return False

        End If

    End Function

[C#]

        public MainPage()

        {

            InitializeComponent();

            myAutoCompleteBox.ItemFilter = Arama;

        }

 

        bool Arama(string metin, ServiceReference1.Service1Urun nesne)

        {

            if (nesne.Adi.Contains(metin)) {

                return true;

            }

            else {

                return false;

            }

        }

Yukarıdaki örneğimizde AutoCompleteBox'ın ItemFilter'ına yeni bir metod atıyoruz. Bu metod parametre olarak o anda filtrelenmek istenen Item'ı, yani benim örneğimde ServiceReference ile beraber gelen tipte bir nesneyi ve filtrelemede kullanılacak metni alıyor. Sonrasında gerekli kontrolleri yaptıktan sonra geriye filtrelediğiniz her bir nesnenin gösterilip gösterilmeyeceğine dair birer Boolean değer döndürmeniz yeterli olacaktır.

Görsel özelleştirmeler...

Makalemizde incelediğimiz tüm özellikleri kullandınız, kendi nesnelerinizi web servisi ile döndürdünüz ve özel bir filtrelemede de eklediniz. Fakat AutoComplete listesinde daha çok detay göstermek istiyorsunuz. Bu durumda gelin AutoCompleteBox'ın ItemTemplate'ini bir inceleyelim.

[XAML]

        <input:AutoCompleteBox x:Name="myAutoCompleteBox" VerticalAlignment="Top">

            <input:AutoCompleteBox.ItemTemplate>

                <DataTemplate>

                    <Grid HorizontalAlignment="Stretch" Width="Auto">

                        <Grid.ColumnDefinitions>

                            <ColumnDefinition Width="250" />

                            <ColumnDefinition Width="250" />

                        </Grid.ColumnDefinitions>

                        <TextBlock Foreground="Red" Grid.Column="0" Text="{Binding Adi}" />

                        <TextBlock Foreground="Red" Grid.Column="1" Text="{Binding Soyadi}" />

                    </Grid>

                </DataTemplate>

            </input:AutoCompleteBox.ItemTemplate>

        </input:AutoCompleteBox>

Kod içerisinde de gördüğünüz üzere AutoCompleteBox'ın ItemTemplate'i aslında kendi içerisinde AutoComplete kısmında tekrar ettiği satırları tanımlıyor. Bu satırların tasarımı aslında arkaplanda bir ListBox'ın Item'larıdır. Bizim örneğimizde ulaşmaya çalıştığımız işlevsellik iki veya daha çok Property'i kullanıcıya gösterirken arka plandaki filtrelememiz ile de bir AutoComplete işlevselliği sağlamak. Zaten hali hazırda AutoCompleteBox'ımıza nesnelerimizi ItemsSource üzerinden aktardığımız için o nesnelerin tüm Property'lerine dair Binding'leri de ItemTemplate içerisinde ayarlayabiliyoruz.

AutoComplete kısmı özelleştirilmiş bir AutoCompleteBox.
AutoComplete kısmı özelleştirilmiş bir AutoCompleteBox.

Taktikler

Eğer herhangi bir AutoCompleteBox'ın ItemsSource'una kendi tanımladığınız tiplerden oluşan listeler verirseniz AutoComplete bölümünde saçma metinler görebilirsiniz. Normal şartlarda bir String Array vs verildiğinde herhangi bir sorun olmuyor fakat kendi tanımladığınız sınıflarda veya web servislerinden Proxy aracılığı ile aldığınız sınıflarda sorun yaşayabilirsiniz. Bu durumu düzeltmenin yolu kendi sınıflarınız için birer ToString extension'ı yazmak olabilir. AutoCompleteBox kendisine verilen tüm nesnelerin üzerinden ToString metodunu çağırarak gelen metni AutoComplete kısmında gösterir. O nedenle siz de kendi sınıflarınızda ToString'i override ederek kendi istediğiniz verileri geri döndürebilirsiniz.

[VB]

Namespace ServiceReference1

    Partial Public Class Service1Urun

        Public Overrides Function ToString() As String

            Return Me.Adi & Me.Soyadi

        End Function

    End Class

End Namespace

[C#]

namespace ServiceReference1

{

    public partial class Service1Urun

    {

        public override string ToString()

        {

            return this.Adi + this.Soyadi;

        }

    }

}

Yukarıdaki örnek kod içerisinde ServiceReference1 adında bir servis referansı ile beraber gelen Service1Urun adındaki nesnenin ToString metodunu override ediyoruz. Artık söz konusu nesneden sonra ToString denildiğinde buradaki Function çalışacak ve nesnenin Adi ile Soyadi Property'lerini birleştirerek geri döndürecek. Böylece AutoCompleteBox da bu ToString'den gelen veriyi gösterebilecek.

Otomatik seçilseler...

AutoComplete listesini gösteriyorsunuz fakat her seferinde kullanıcının bir kayıt seçmek zorunda kalmasını da istemiyorsunuz. Belki de en uygun seçecek hemen seçilebilir şekilde gelse? Nasıl mı?

IsTextCompletionEnabled = True olursa...
IsTextCompletionEnabled = True olursa...

AutoCompleteBox'ın IsTextCompletionEnabled özelliği True yaparsanız yukarıdaki gibi kullanıcılar metin girişi yaparken bir yandan da en uygun seçeneği seçtirebilirsiniz. Böylece anında seçimi onaylayarak kullanıcılar hızlıca işleme devam edebilirler.

Hepinize kolay gelsin.

Monday, June 22, 2009 2:32:11 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Silverlight 3.0  | 
 Saturday, June 13, 2009

Silverlight 3.0 ile beraber gelen en ilgi çekici özelliklerden biri 3D desteği. Bu makalemizde Silverlight ile beraber gelen 3D özelliklerine bakmadan önce aslında nasıl bir 3D mantığı ile karşı karşıya olduğumuzu netleştirmekte fayda var. Özellikle Silverlight öncesi WPF ile ilgilnenlerin 3D beklentisi eminim ki çok yüksek olacaktır. Tabi daha öncesinde 3D oyun programlama ile ilgilenenleri liste dışında tutuyoruz :) amacımız zaten oyun tarafı değil.

WPF içerisinde alıştığımız yapıdaki 3D ortamda istediğimiz bir 3D objeyi alarak X, Y ve Z koordinatları ile doğrudan kontrol edebiliyorduk oysa Silverlight tarafındaki 3D desteği biraz daha farklı. Aslında bakılırsa tam olarak 3D desteği olarak da adlandırılmıyor söz konusu mekanizma. Esas bahsedilen özelliğin adı "3D Plane" olarak geçiyor. Peki bunun anlamı nedir? Özetlemek gerekirse aslında Silverlight içerisindeki 3D desteği sadece 3D ortam şeklinde. Yani sadece içerisinde bulunduğunuz ortam 3D :) elinizdeki nesneler 3D değil.

Ne? Nasıl?

Elinizde iki boyutlu bir nesne olduğunu düşünün. Aslında çok basit bir şekilde bir TextBox düşünseniz de yeterli olacaktır. Bir TextBox nesnesi 3D bir nesne midir? Yani kalınlığı var mıdır? Hayır, bir TextBox'ın sadece genişlik ve yüksekliği vardır. İşte bu şekilde 2D nesneleri Silverlight içerisinde artık sanki 3D ortamdaymış gibi gösterebiliyorsunuz. Özünde bahsettiğimiz şey 3D Projection (Yansıtma). Nasıl ki elimizdeki 2D nesnelere RenderTransform'lar uygulayarak iki boyutlu ortamda istediğimiz gibi manipüle edebiliyorduysak artık Silverlight içerisinde tüm görsel nesneleri 3D ortamda da manipüle edebiliyorsunuz. Gelin hemen hızlı bir örnek ile konuya girelim.

Farklı 3D Transform'lar uygulanmış ComboBox'lar.
Farklı 3D Transform'lar uygulanmış ComboBox'lar.

Yukarıdaki ekran görüntüsünde aslında basit bir ComboBox görüyorsunuz fakat söz konusu ComboBox sanki üç boyutlu ortamdaymış gibi döndürülmüş hatta görüntülerden birinde arkasına çevrilmiş. Tüm bu mekanizma animasyonlar dahil çalışırken eldeki nesneleriniz hepsi "arkası döndürüldüğünde" bile kullanılabilir olmaya devam ediyor. Düşünün, tüm bunlar animasyonlar ile birleştirildiğide üç boyutlu ortamda gezen kontroller şeklinde :) ne kadar ilginç arayüzler yaratılabilir.

Kod tarafına geçiş yapalım...

Kod tarafında herhangi bir nesneyi 3D ortamdaymış gibi ekrana yansıtmak istiyorsanız yapmanız gereken söz konusu nesne için hemen bir Projection tanımlamak. Şu an için Silverlight içerisinde sadece PlaneProjection destekleniyor o nedenle tek seçeneğimiz var.

[XAML]

<UserControl x:Class="SilverlightApplication43.MainPage"

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

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

   Width="400" Height="300">

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

        <ComboBox Width="200" Height="20">

            <ComboBox.Projection>

                <PlaneProjection RotationX="208"

                                CenterOfRotationY="1"

                                CenterOfRotationX="1"

                                RotationY="33"

                                GlobalOffsetY="15"

                                GlobalOffsetX="35"/>

            </ComboBox.Projection>

            <ComboBox.Items>

                <ComboBoxItem Content="Seçenek 1"></ComboBoxItem>

                <ComboBoxItem Content="Seçenek 1"></ComboBoxItem>

                <ComboBoxItem Content="Seçenek 1"></ComboBoxItem>

                <ComboBoxItem Content="Seçenek 1"></ComboBoxItem>

                <ComboBoxItem Content="Seçenek 1"></ComboBoxItem>

            </ComboBox.Items>

        </ComboBox>

    </Grid>

</UserControl>

Bir önceki bölümde sizinle paylaştığım ekran görüntüsünde sağ tarafta bulunan ComboBox'ın XAML kodunu yukarıda bulabilirsiniz. Burada önemli olan şey ComboBox'ın Projection özelliğine atanmış olan PlaneProjection nesnesi. Söz konusu PlaneProjection nesnesinin birçok özelliği set edilmiş durumda. İşte biz de makalemizin geri kalanında bu özelliklerin anlamlarına göz atacağız.

RotationX, RotationY, RotationZ

İsimlerinden de anlaşılacağı üzere bu özellikler bir şekilde elimizdeki nesnenin üç boyutlu ortamda kaç derece ile döndürüleceğini belirliyor. Peki X, Y ve Z derken neyi kast ediyorlar? Hemen gözünüzde üç boyutlu bir ortam canlandırın ve söz konusu ortadam ComboBox'ımızı koyalım. ComboBox'ı etrafında döndürebileceğiniz aslında üç adet eksen vardır. Bunlardan birincisi X ekseki. Eğer X ekseki etrafında ComboBox'ı döndürmek istiyorsanız RotationX değerine gerekli açıyı verebilirsiniz. Aynı şekilde Y ve Z eksenleri etrafında da eldeki nesne döndürülecekse rahatlıkla özelliklere de açı değerleri vererek istediğiniz sonucu alabilirsiniz.

Eksenler ve Rotation özellikleri...
Eksenler ve Rotation özellikleri...

Yukarıdaki görsel içerisinde X, Y ve Z eksenlerini ve etraflarında döndürülmüş Button görsellerini inceleyebilirsiniz. Bu noktada dikkat edilmesi gereken detaylardan biri de aslında eksenlerin gerçekten de her nesnenin tam ortasından geçiyor olması. Her görsel nesne kendi X, Y ve Z eksenlerine sahip ve bunlar varsayılan ayarlarda nesnenin tam ortasından geçecek şekilde ayarlanmış. Örneğin X ekseni etrafında 45 derece döndürülen bir Button eksen tam da Button'un ortasından geçtiği için kendi merkezi etrafında dönüyormuş gibi gözüküyor.

İsterseniz eksenlerin yerlerini değiştirme şansınız da var. Tüm eksenler varsayılan ayarları ile nesnelerin tam ortasından geçersen harici Offset değerleri ayarlanabiliyor. Offset değerleri bilindiği üzere 0 ile 1 arasında nesnenin sol üst köşesinden başlayarak sağ alt köşesine gider. Eğer X eksenine 0 Offset değerini verirseniz bu X ekseninin düğmenin en üstüne denk geleceği anlamına gelir. Aynı şekilde eğer 1 değerini verirseniz ekseni düğmenin en alt noktasına itmiş olursunuz.

CenterOfRotation ayarları.
CenterOfRotation ayarları.

Eksenin yerini değiştirmiş olmak artık düğmemizin kendi merkezine göre değilde üst noktasına göre veya alt noktasına göre dönmesini sağlayacaktır. CenterOfRotation olarak geçen bu özelliğin X, Y ve Z Offset değerlerini ayrı ayrı düzenleyebiliyorsunuz. Yukarıdaki görselde de görebileceğiniz üzere aslında X ekseninin Y eksenine göre ne kadar aşağıda veya yukarıda olacağını ayarlıyoruz. O nedenle düğmenizin X eksenin Offset değeri olarak ne kadar uzak olacağına dair bilgiyi CenterOfRotationY değerine atamanız gerekiyor. Aynı şekilde diğer eksenler için de CenterOfrotationX ve CenterOfRotationZ özellikleri de mevcut.

GlobalOffset ve LocalOffset ayarları.

Şu ana kadar eksenleri etrafında nesnelerin ne açı ile döneceklerini ve eksenlerin nesnelerin neresine denk geleceğini ayarladık. Oysa başka dertlerimiz de olabilir. Bunlardan en basiti tüm işlemleri yaparken nesneyi uygulama genelinde Global olarak da posizyonlandırmak isteyebilirsiniz. İşte üç boyutlu ortamda nesnenize Global bir pozisyon vermek istiyorsanız kullanacağınız özellikleri GlobalOffsetX, GlobalOffsetY ve GlobalOffsetZ özellikleri olacaktır. Bu özellikler kesinlikle animasyonunuzu veya nesnenin nasıl döneceğini vs değiştirmez. Nesneyi kendi X, Y ve Z eksenleri ile beraber Global, uygulama bazında ayrı bir X, Y ve Z ekseni üzerinden tekrar pozusyonlandırdığınızı düşünebilirsiniz.

Gelelim animasyonlarınızı en hareketli ve janjanlı hale getirecek olan son özelliğe; LocalOffset! Adından da anlaşılacağı üzere aslınad yine nesnenizi bir şeyden uzaklaştırıyorsunuz fakat bu sefer global anlamda değil. Nesnenin kendi içinde belirlenen açılarla etrafında döndüğü eksenlerden devasa miktarlarda uzaklaştırılarak sanki uzaklardan bir yerden geliyormuş efekti ile ekrana gelmesini sağlayan mekanizma LocalOffset'tir. Düşünün ki nesneniz ekseni etrafında değil de ekseninden 50 piksel uzakta bir dairede dönüyor? Nasıl gözükürdü?

Ekseninden uzaklarda dönen bir Button.
Ekseninden uzaklarda dönen bir Button.

Yukarıdaki görüntüden bir Button'un ekseninden uzakta dönmesi halinde nasıl bir animasyon oluşabileceğini tahmin edebilirsiniz. Offset değerlerini arttırarak ve sadece X değil farklı eksenlerde aynı anda açılar vererek animasyonlar hazırladığınızda nesnelerinizi üç boyutlu ortamda istediğiniz gibi hareketlendirebiliyorsunuz. Tüm bu süreçte Button'un sürekli olarak tıklanabilir kaldığını da hatırlatmak isterim.

Sonuç

Yukarıda bahsettiğimiz tüm özelliklere programatik erişimin haricinde animasyon içerisinde de kullanabileceğiniz kesin. Özellikle unutulmaması gereken nokta Projection'ın tüm nesnelere uygulanabiliyor olması, yani elinizde içi dolu bir StackPanel veya bir DataGrid olabilir ve tüm bunları keni iç elementleri ile beraber rahatlıkla anime edebilirsiniz. Hatta belki StackPanel'inizin içinde bir nesne zaten kendi X, Y, Z koordinatlarına göre üç boyutlu bir animasyonu gerçekleştiriyordur ve siz StackPanel'i de başka bir üç boyutlu animasyona alarak daha ilginç manzaralar da yaratabilirsiniz.

Hepinize kolay gelsin.

Saturday, June 13, 2009 9:31:50 PM (GTB Standard Time, UTC+02:00)  #    Comments [3]   Silverlight 3.0  | 
 Saturday, June 06, 2009

Silverlight ve WPF ile beraber gelen Resource yapıları özellikle web tasarımcıları tarafından alışılmış CSS'in çok daha gelişmiş bir hali şeklinde tanımlanabilir. Sadece görsel anlamda değil aynı anda kontrollerin veya nesnelerin işlevsellik anlamında da birçok özelliklerinin birer stil olarak saklanmasını ve tekrar kullanılmalarının yanı sıra uygulama çerçevesinde merkezi yönetim sağlamasını da düşünürsek Silverlight projelerde Resource kullanımının büyük önemi var.

Maalesef Silverlight 2.0 ile beraber elimizdeki tek seçenek Resource'larımızı App.xaml içerisine yerleştirerek uygulama çapında kullanmaktı. Oysa çoğu zaman harici CSS dosyaları gibi Silverlight tarafında da Resource'larımızı harici dosyalara alarak farklı uygulamalarda da ortak olarak kullanmak istedik. Microsoft bizi dinlemiş olsa gerek ki Silverlight 3.0 ile beraber MergedDictionaries yapısı geliyor.

Nedir bu MergedDictionaries?

MergedDictionary'ler sayesinde harici bir dosyada yer alan ResourceDictionary'lerimizi Silverlight uygulamalarımda yer alan kontrollerinin Resources koleksyonlarına ekleyebiliyoruz. Böylece artık harici dosyalarda Resource'larımızı saklayabiliriz. Peki nasıl yapacağız? Gelin basit bir örnek ile adım adım bu işlemleri nasıl yapabileceğimiz ve ne gibi seçeneklerin bizi beklediğine bir göz atalım.

[XAML / HariciResource.xaml]

<ResourceDictionary

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

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

    <SolidColorBrush x:Key="OzelRenk" Color="#FF499422"/>

</ResourceDictionary>

Yukarıda gördüğünüz kod harici bir XAML dosyası içerisinde tek başına bulunuyor. Aslında bu dosya harici bir ResourceDictionary tanımlıyor ve içerisinde de sadece bir adet SolidColorBrush var. Bunun gibi farklı Resource tiplerini de bu dosya içerisinde saklayabiliriz. Biz şimdilik kodlarımız kısa ve anlaşılır olsun diye bu basit örnek üzerinden ilerleyelim. OzelRenk adındaki bu Resource'u uygulamanız içerisinde kullanabilmeniz için ilk olarak dosyayı Silverlight projenize eklemeniz gerekiyor. Ekleme işlemi sonrası Solution Explorer içerisinden dosyayı seçtiğinizde Properties panelinde Build Action'ın Content olmasına dikkat edin. Böylece XAML dosyası XAP içerisindeki DLL'inize eklenecektir.

Dosyamızı projemize ekledik fakat hala onu herhangi bir yerde tanımlamadık. Bu noktada iki seçenek var, bunlardan biri söz konusu tanımlamayı App.XAML içerisinde yaparak harici Resource dosyasındaki tüm Resource'ların uygulamada her yerde kullanılabilmesini sağlamak, ikincisi ise sadece UserControl başına harici dosyayı Import etmek. Her iki seçenekte de aynı kod kullanılacağı için gelin App.XAML içerisinde nasıl bir değişiklik yapmamız gerektiğine göz atalım.

[XAML / App.xaml]

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

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

            x:Class="SilverlightApplication14.App"

            >

    <Application.Resources>

 

        <ResourceDictionary>

            <ResourceDictionary.MergedDictionaries>

                <ResourceDictionary Source="HariciResource.xaml"/>

            </ResourceDictionary.MergedDictionaries>

        </ResourceDictionary>

 

    </Application.Resources>

</Application>

Gördüğünüz gibi Application.Resources içerisinde bir MergedDictionaries serisi yaratarak içerisine de harici ResourceDictionary'imizi koyuyoruz. Böylece artık harici dosyamızdaki tüm Resource'lar rahatlıkla uygulama genelinde kullanılabilecek.

[XAML / UserControl1.xaml]

<UserControl x:Class="SilverlightApplication14.MainPage"

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

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

   Width="400" Height="300">

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

        <Rectangle Fill="{StaticResource OzelRenk}" Stroke="#FF000000" Height="64" Margin="74,75,159,0" VerticalAlignment="Top"/>

 

    </Grid>

</UserControl>

Yukarıda gördüğünüz basit UserControl içerisinde bir Rectangle bulunuyor. Bu Rectangle aslında bizim biraz önce uygulamamıza eklediğimiz harici Resource dosyası içerisindeki bir kaynağı kullanıyor. Sadece buradaki koda baktığımızda aslında söz konusu Resource'ların App.xaml içerisinde olması ile harici dosyada olması arasında bir fark yok.

Ya harici projelerdeki Resource dosyaları?

Şu ana kadarki örneğimizde Resource dosyası kendi projemiz içerisindeydi. Oysa belki de Resource'larınızı harici Silverlight Class Library'ler içerisinde tutuyor olabilirsiniz. İşte böyle bir durumda da doğrudan söz konusu DLL'i referans alarak onun içerisindeki bir Resource Dictionary'yi projenizde kullanmak isteyeceğiniz kesin. Bu gibi bir durumda değişen tek şey hedeflediğiniz XAML dosyasının adresi yani Source.

[XAML / App.xaml]

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

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

            x:Class="SilverlightApplication16.App"

            >

    <Application.Resources>

        <ResourceDictionary>

            <ResourceDictionary.MergedDictionaries>

                <ResourceDictionary Source="SilverlightClassLibrary1;component/HariciResource.xaml"/>

            </ResourceDictionary.MergedDictionaries>

        </ResourceDictionary>

    </Application.Resources>

</Application>

Yukarıdaki şekli ile örneğimizi incelediğinizde unutmayın ki projemize referans aldığımız ve Assembly adı SilverlightClassLibrary1 olan bir DLL bulunuyor. Söz konusu DLL içerisinde yine bir önceki örnekte kullandığımız HariciResource.xaml dosyası bulunuyor. App.xaml dosyamızdaki tek farklılık HariciResource.xaml dosyasına ulaşırken kullandığımız yol tanımı.

Böylece Resource'larınızı harici DLL'lerde tutabilir ve istediğiniz projeye referans alarak kullanabilirsiniz. Veya projelerinizde Static Resource isimleri kullanarak yer geldiğinde sadece referansı değiştirerek tüm projenin görselliğinin değişmesini sağlayabilirsiniz.

Hepinize kolay gelsin.

Saturday, June 06, 2009 9:16:29 PM (GTB Standard Time, UTC+02:00)  #    Comments [2]   Silverlight 3.0  | 
 Friday, June 05, 2009

Bazı yazılımsal işlemleri özel donanım desteği her zaman performansı arttırır. Aslında bunun çok basit bir nedeni var özünde. Yapacağımız yazılımsal işleve özel bir donanım kullanıyor olmanın yanı sıra özellikle bugün bilgisayarlarımızda normal şartlarda söz konusu işlemi yapmakla yükümlü birimi başka işlemler için serbest bırakıyor olmamız da önemli bir nokta. Neden mi bahsediyorum? Örneğin bilgisayarlarımızdaki ekran kartları! Hatırlayanlarınız vardır :) Eskiden oyunlardaki 3D hesaplamaları vs bilgisayarlarımızın normal işlemcileri üstlenirdi. Sonrasında ekran kartlarında GPU denilen bir birim ortaya çıktı ve 3D dahil birçok görsel hesaplamalayı işlemcilerimizin üzerinden aldı. Böylece hem işlemcilerimiz rahatladı :) diğer işlemler ile çok daha rahat ilgilenir oldular hem de söz konusu 3D işlemleri yapmaya özel tasarlanmış bir donanım artık söz konusu işlemlerden sorumlu olduğu için çok daha yüksek performans alınabildi.

Silverlight ile alakası nedir?

Aslına bakarsak Silverlight içerisinde de bolca görsel işlem yapıyoruz. En basit örnek olarak bir video oynatmayı bile ele alabiliriz. Biz videomuzu bir MediaElement ile sahneye koyduğumuzda video dosyasının orijinal yükseklik ve genişliği ile hiç ilgilenmeyiz. Oysa arkaplanda oynatılacak olan video uygun şekillerde MediaElement içerisine yerleştirilir ve aslında gerçek zamanlı olarak videonun içindeki her kare resim tekrar boyutlandırılır. Özellikle Full HD gibi yoğun data içeren yüksek çözünürlüklü videolarda aslında ciddi bir işlem gücü gerekiyor ve şu ana kadar da bu işlemleri Silverlight tarafında bilgisayarımızın işlemcisi üstleniyordu.

Oysa zaten bugün hali hazırda neredeyse her bilgisayarda en kötüsünden bir GPU yok mu? Neredeyse GPU'suz bilgisayar satın alamaz hale geldik. Tabi bu güzel bir gelişme ama bu gelişmeden Silverlight'ın da faydalanması gerek! İşte Silverlight 3.0 ile beraber "GPU Acceleration" denilen GPU (donanım) destekli işlemler ile uygulamanın hızlandırılması artık mümkün.

Video uygulamalarında Hardware Acceleration...

Hemen basit bir şekilde elimizdeki High Definition bir videoyu oynatan Silverlight uygulaması hazırlayalım. Ekrana bir MediaElement koyarak Source özelliğini ayarlamamız yeterli olacaktır.

[XAML]

<UserControl x:Class="SilverlightApplication10.MainPage"

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

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

   Width="400" Height="300">

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

        <MediaElement Source="Wildlife.wmv" />

    </Grid>

</UserControl>

Yukarıdaki XAML kodu ile uygulamamızı çalıştırdığımda herhangi bir sorun yok. Video rahatlıkla oynuyor fakat kendi makinemde yaptığım testte CPU kullanımı %25 civarında geziyor. Acaba sadece bu videonun en azından tekrar boyutlandırma işini işlemciden alıp GPU'ya aktarsak işlemci kullanımı ne kadar düşer?

Silverlight içerisinde varsayılan ayarları ile GPU kullanımı gelmiyor. GPU kullanımını bir uygulamada açmak için söz konusu uygulamanın HTML sayfa içerisine yerleştirildiği OBJECT taglarına ekstra bir parametre geçmeniz gerekiyor.

[HTML]

        <object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">

            <param name="source" value="ClientBin/SilverlightApplication10.xap"/>

            <param name="onerror" value="onSilverlightError" />

            <param name="background" value="white" />

            <param name="minRuntimeVersion" value="3.0.40307.0" />

            <param name="autoUpgrade" value="true" />

            <param name="EnableGPUAcceleration" value="true" />

            <a href="http://go.microsoft.com/fwlink/?LinkID=141205" style="text-decoration: none;">

                 <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none"/>

            </a>

        </object>

Yukarıdaki HTML kodları arasında da görebileceğiniz üzere artık Silverlight uygulamamızı gösterecek olan OBJECT taglarına ek olarak bir de EnableGPUAcceleration parametresi veriyor ve söz konusu değeri de True olarak set ediyoruz. Böylece artık Silverlight uygulamamız GPU desteğine kavuşacak fakat ek olarak uygulama içerisinde hangi kontrollerin GPU tarafından oluşturulması gerektiğini de belirtmemiz gerekiyor. Bunun için kontrol bazında CacheMode ayarlanıyor. Şu anda sadece CacheMode olarak BitmapCache destekliyor. BitmapCache'in tam olarak ne yaptığına bir sonraki örneğimizde göz atacağız. Şimdilik MediaElement'imize nasıl BitmapCach uygulanır onu görelim.

[XAML]

<UserControl x:Class="SilverlightApplication10.MainPage"

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

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

   Width="400" Height="300">

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

        <MediaElement Source="Bear.wmv" >

            <MediaElement.CacheMode>

                <BitmapCache/>

            </MediaElement.CacheMode>

        </MediaElement>

    </Grid>

</UserControl>

Silverlight içerisinde tüm nesnelerin artık bir CacheMode özelliği var. Bu özelliğe bir BitmapCache vererek donanım desteğini arkanıza alabilirsiniz. Yukarıdaki örneği donanım desteği ile çalıştırdığımda benim makinemde CPU kullanımı %25'ten %20'lere indi. Tabi bu yaptığınız işleme ve GPU'nuzun gücüne göre değişecektir.

Silverlight'ta CPU kullanımı farkları.
Silverlight'ta CPU kullanımı farkları.

Unutmadan birkaç detaya göz atalım. Silverlight'ın donanım desteğini arkasına alabilmesi için hedef makinede DirectX9.0c yüklü olması gerekiyor. Eğer söz konusu yükleme makinede yok ise yine eski tarz yazılım bazlı sisteme geri dönülüyor. Mac ortamı için ekstra bir şart daha var; maalesef Mac ortamında donanım desteği sadece tam ekran modunda çalışabiliyor.

Şu an için GPU'ya aktarılan işlemler Transform, Rectangular Clipping ve Blending işlemleri. İleriki sürümlerde farklı işlemlerin de GPU tarafına aktarılabilmesine dair eklentiler vaatler arasında.

BitmapCache nedir?

Aslında BitmapCache çok dikkatli yaklaşılması gereken bir teknik. Örneğin her noktada BitmapCache kullanmaya başlarsak aslında performans kazanalım derken kaybetmemize de neden olabilir.

Bildiğiniz üzere Silverlight içerisinde sürekli olarak ekran vektörel olarak çiziliyor. Bazı durumlarda özellikle hızlı animasyonlarda anime ettiğiniz nesnenin görsel olarak sürekli yeniden çizilmesini istemeyebilirsiniz. Oysa o görsel vektörel bir tekrar çizme sürecinden geçirmek yerine daha önce çizilmiş vektörel sonucu bitmap olarak kullanmak performans noktasında ciddi katkı sağlayabilir.

BitmapCache'in performansa faydası.
BitmapCache'in performansa faydası.

Yukarıdaki grafik testini yaparken çok sayıda notka içeren bir vektörel çizim kullandım. Söz konusu vektörel çizim XAML kodunu makaleye yapıştırmıyorum çünkü hem anlamsız hem de gereğinden uzun :) Vektörel çizimi bir animasyon ile ekranda sürekli olarak büyütüp küçülttüm. Bu süreçte Normal şartlarda Silverlight her karede vektörel çizim vektör verisinden tekrar çizdi. BitmapCache ile GPU desteğini açtığımda ise ilk başta çizilen vektörel datadan bir Bitmap kopya alıp artık Bitmap koyayı büyütüp küçültmeye başladı. Aradaki %10'a yakın performans farkını yukarıdaki görebilirsiniz.

BitmapCache Mantığı.
BitmapCache Mantığı.

Tabi unutmamak gerek ki vektörel yerine bitmap işlemler yaptırdığınızda sistemin ilk aldığı bitmap içerisinde kalite geçerli olacaktır ve nesne büyüdükçe tekrar çizilmediği için normal animasyonlardaki kalite elde edilemeyecektir. Fakat bazı durumlarda bu kalite farkının kullanıcı tarafından algılanması pek mümkün olmayabiliyor. İşte böyle durumlarda BitmapCache hayat kurtarabilir.

Neresi Cache'den geliyor? Neresi gelmiyor?

BitmapCache ayarlarını yaptıktan sonra uygulamanızı test ederken gerçekten ekranda nerelerin Cache'lenip Cache'lenmediğini görmek isterseniz yine HTML OBJECT tagına geri dönüp aşağıdaki parametreyi ekleyebilirsiniz. Böylece artık Silverlight uygulamanız çalışırken Cache'lenmeye yerleri ayrı renklerde gösterecektir.

[HTML]

        <object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">

            <param name="source" value="ClientBin/SilverlightApplication12.xap"/>

            <param name="onerror" value="onSilverlightError" />

            <param name="background" value="white" />

            <param name="minRuntimeVersion" value="3.0.40307.0" />

            <param name="autoUpgrade" value="true" />

            <param name="EnableGPUAcceleration" value="true" />

            <param name="EnableCacheVisualization" value="true" />

            <a href="http://go.microsoft.com/fwlink/?LinkID=141205" style="text-decoration: none;">

                 <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none"/>

            </a>

        </object>

Hepinize kolay gelsin.

Friday, June 05, 2009 7:08:08 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Silverlight 3.0  | 
 Thursday, June 04, 2009

Bir web sitesi içerisinde birden çok Silverlight uygulamasını beraber kullanıyor olabilirsiniz veya belki de aynı web sitesinde sizin hazırladığınız bir Silverlight uygulamasının yanı sıra bir de başka bir yazılımcının/tasarımcının hazırladığı Silverlight uygulaması bulunuyordur. Peki hiç bu iki uygulamanın birbirinden haberdar olabileceğini ve karşılıklı haberleşebileceklerini düşündünüz mü?

Eminim ki hemen aklınıza gelecek çözüm sunucu tarafından uygulamaları birbiri ile konuşturmak olacaktır oysa artık Silverlight 3.0 ile beraber LocalMessaging denilen ve Named Pipes mantığına çok benzeyen bir sistem ile uygulamaları birbirleri ile haberleşebiliyorlar. Birden çok Silverlight uygulaması ister aynı sayfada, ister tarayıcınızın ayrı tablarında ister farklı tarayıcılarda olsun aynı makinede çalıştıkları sürece birbirleri ile sunucudan bağımsız olarak tamamen istemci tarafında konuşabiliyorlar.

Neler yapılabilir?

Aslında bu yapı ile bir çok farklı senaryo düşünülebilir. Her iki veya üç Silverlight uygulamasını da sizin hazırladığınızı düşünürsek belki de uygulamalar arasına interaksyon sağlayabilirsiniz. Kullanıcının bir uygulamada yaptığı değişiklik bir diğerinde de bazı farklı mekanizmaları tetikleyebilir.

Bir diğer senaryoda ise belki de hiç kodlarına sahip olmadığınız bir Silverlight uygulaması ile sizin uygulamanızın birbiri ile istemci tarafında konuşması gerektiğinde ortak belirlenen bir kanal üzerinden uygulamaların haberleşmesini sağlayarak entegrasyon sağlayabilirsiniz. Daha belki de benim aklıma gelmeyen birçok farklı mekanizma mümkün olabilir.

Mutfağa geçelim....

Teorik kısmı atlattıktan sonra gelin biraz da LocalMessaging sistemini nasıl kullanabileceğimize göz atalım. Özünde LocalMessaging yapısı Named Pipes mantığı ile işliyor. Yani iki tarafın bir biri ile haberleşmesi için bir kanala ihtiyacınız var. Kanallar String tipinde isimler ile birbirinden ayırt ediliyor. Kanal ismine göre dinleyici uygulama söz konusu kanalı dinlerken veri gönderecek uygulama da yine aynı kanal ismini kullanarak veri gönderme işlemini yapabiliyor.

Yapacağımız örnekte uygulamalarımızdan birinde yer alan düğmeye bastığımızda diğerinde bir animasyon çalıştıracağız. Bu sistemin gerçekleşebilmesi için iki numaralı uygulama "MesajKanali" adındaki bir kanalı dinlemeye başlarken aynı kanala bir numaralı uygulama ise iki numaralı uygulamanın anlayabileceği bir mesaj gönderecek. Gelin ilk olarak mesajı gönderecek olan uygulama ile işimize başlayalım.

[XAML]

<UserControl x:Class="SilverlightApplication6.MainPage"

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

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

   Width="400" Height="300">

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

        <Button Content="Mesaj Gönder" x:Name="btnGonder" Click="btnGonder_Click"/>

    </Grid>

</UserControl>

Uygulamamızın ekranında basit bir düğme bulunuyor. Söz konusu düğmeye basıldığında mesaj gönderme işlemini başlatacağız.

[VB]

    Private Sub btnGonder_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)

        Dim MesajGonderici As New Messaging.LocalMessageSender("MesajKanali", "localhost")

        MesajGonderici.SendAsync("BASLA")

    End Sub

[C#]

        private void btnGonder_Click(object sender, RoutedEventArgs e)

        {

            System.Windows.Messaging.LocalMessageSender MesajGonderici =

                new System.Windows.Messaging.LocalMessageSender("MesajKanali", "localhost");

            MesajGonderici.SendAsync("BASLA");

        }

Yukarıda gördüğünüz kod içerisindeki ilk satırda mesaj gönderebilmek için bir LocalMessageSender yaratıyoruz. Söz konusu sınıfın constructor'ı bizden kanal ismini ve mesajın gönderileceği domainin adını istiyor. Bizim kullanacağımız kanalı ismi şimdilik "MesajKanali" olacak. Kanalimizi ve mesaj göndericimizi yarattığımıza göre artık söz konusu mesaj göndericinin SendAsync metodu ile mesajımızı bu kanalı dinleyen herkese gönderebiliriz. SendAsync metodunun sonundaki Async uzantısından da anlayabileceğiniz üzere işlem asenkron olarak yapılacak. Yani mesaj gönderdildikten sonra durumdan haberdar olmak istiyorsanız LocalMessageSender'ın SendCompleted event'ını yakalamanız gerekiyor.

Eğer gönderdiğiniz mesaj herhangi bir hedef domain'e bakılmaksızın söz konusu kanalı dinleyen herkese gönderilsin istiyorsanız aşağıdaki şekilde ikinci parametre olarak Global değerini de verebilirsiniz.

[VB]

        Dim MesajGonderici As New Messaging.LocalMessageSender("MesajKanali", System.Windows.Messaging.LocalMessageSender.Global)

Gördüğünüz gibi mesaj gönderme işlemi epey basit. Maalesef ki mesajlar sadece String tipinde olabiliyor. Eğer .NET objeleri göndermek isterseniz uygun XML Serializer'ları kullanabilirsiniz. Örneğimizde biz "BASLA" mesajını gönderiyoruz. Tabi bu mesajın alıcı tarafında da alındıktan sonra bir anlam ifade etmesi gerekecek. Onun için de dinleyici tarafında uygun kodu birazdan yazacağız.

Dinleyici nerede?

Sıra geldi dinleyici uygulamayı hazırlamaya. Dinleyici uygulamamızda basit bir animasyon olacak ve kendisine diğer uygulamadan uygun mesaj gönderildiği anda bu animasyon başlatılacak. Siz örneklerinizde farklı senaryolar uygulayabilirsiniz.

[XAML]

<UserControl x:Class="SilverlightApplication7.MainPage"

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

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

   Width="400" Height="300">

    <UserControl.Resources>

        <Storyboard x:Name="Animasyon">

            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"

                                          Storyboard.TargetName="rectangle"

                                          Storyboard.TargetProperty="(UIElement.Opacity)">

                <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>

                <SplineDoubleKeyFrame KeyTime="00:00:01" Value="1" KeySpline="1,0,1,1"/>

                <SplineDoubleKeyFrame KeyTime="00:00:02" Value="0" KeySpline="0,1,1,1"/>

            </DoubleAnimationUsingKeyFrames>

        </Storyboard>

    </UserControl.Resources>

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

        <Rectangle Height="100" Width="100" Fill="Red" Opacity="0" x:Name="rectangle"/>

    </Grid>

</UserControl>

Yukarıda XAML'ını gördüğünüz uygulama Dinleyici uygulamamız. İçerisinde bir Rectangle ve basit bir de animasyon bulunuyor. Artık Dinleyici uygulamamız içerisinde de bir dinlayici nesnesi tanımlayıp mesajın gönderileceği kanalı dinlemeye başlamanın zamanı geldi.

[VB]

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

        Dim Gonderebilenler As New List(Of String)

        Gonderebilenler.Add("localhost")

        Dim MesajDinleyici As New Messaging.LocalMessageReceiver("MesajKanali", Messaging.ReceiverNameScope.Domain, Gonderebilenler)

        AddHandler MesajDinleyici.MessageReceived, AddressOf MesajDinleyici_MessageReceived

        MesajDinleyici.Listen()

    End Sub

[C#]

        public MainPage()

        {

            InitializeComponent();

            this.Loaded += new RoutedEventHandler(MainPage_Loaded);

        }

 

        void MainPage_Loaded(object sender, RoutedEventArgs e)

        {

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

            Gonderebilenler.Add("localhost");

            System.Windows.Messaging.LocalMessageReceiver MesajDinleyici =

                new System.Windows.Messaging.LocalMessageReceiver("MesajKanali",

                    System.Windows.Messaging.ReceiverNameScope.Domain, Gonderebilenler);

            MesajDinleyici.MessageReceived += new EventHandler<System.Windows.Messaging.MessageReceivedEventArgs>(MesajDinleyici_MessageReceived);

            MesajDinleyici.Listen();

        }

Uygulamamız açıldığı gibi hemen işlemlere başlıyoruz. İlk olarak dinlemek istediğimiz kanalların bulunduğu domain'lerin bir listesini yaratmamız gerek. Bunun için bir String listesi yeterli olacaktır. Bu liste içerisinde dinleyeceğiniz kanalın hangi domain'den geleceğini belirlemiş oluyorsunuz. Aksi halde herhangi bir domain'den yola çıkan ve aynı kanal ismini kullanan uygulamalar ile çakışmanız olası.

Elimizde dinlenecek domain listedi Gonderebilenler değişkeninde bulunduktan sonra MesajDinleyici'mizi yaratabiliriz. LocalMessageReceiver sınıfından aldığımız değişkenimizi yaratırken toplam üç parametre vermemiz gerek. Bunlardan ilki dinleyeceğimiz kanalı String adı, ikincisi ise dinleme işlemini hangi çapta yapmak istediğimiz. Global anlamda dinleyerek Gonderebilenler listemizdeki tüm alan adlarını dinleyebileceğimiz gibi Domain çapından ilerleyerek sadece bulunduğumuz alan adını dinleyebiliriz. Eğer sadece bulunduğunuz alan adını dinleyecekseniz aslında Gonderebilenler listesine gerek yok ama yine de örneğimizde eksik olmasın diye bir parametremiz Nothing/Null şeklinde geçmeyelim istedim.

Bir sonraki adımda MesajDinleyici nesnemizim MessageReceived event'ını da ayrı bir event-listener'a bağlamamız gerekiyor ki mesaj geldiğinde durumdan haberdar olabilelim. Tabi son olarak LocalMessageReceiver'ın Listen metodunu da çağırıyor ve dinleme işlemini başlatıyoruz.

[VB]

    Private Sub MesajDinleyici_MessageReceived(ByVal sender As Object, ByVal e As System.Windows.Messaging.MessageReceivedEventArgs)

        If e.Message = "BASLA" Then Animasyon.Begin()

    End Sub

[C#]

        void MesajDinleyici_MessageReceived(object sender, System.Windows.Messaging.MessageReceivedEventArgs e)

        {

            if (e.Message == "BASLA") Animasyon.Begin();

        }

Dinleme işlemine bağladığımız event-listener içerisinden yer alan MessageReceivedEventArgs üzerinden Message Property'si ile gelen mesajı alabilirsiniz. Bu mesajı farklı şekilerde Parse ederek anlam kazandırabileceğiniz gibi basit bir şekilde komut anlamında String karşılaştırma yaparak da yapacağınız işlemleri belirleyebilirsiniz. Biz örneğimizde BASLA mesajı geldiğinde elimizdeki animasyonu çalıştırıyoruz.

Bu noktadan sonra eğer Dinleyici uygulama hemen, anında mesajı gönderen kişiye bir cevap vermek isterse MessageReceivedEventArgs ile beraber gelen Response özelliğini kullanabilir. Böylece belki gelen komut için bir cevap belki de başka bir bilgi gönderilebilir.

[VB]

    Private Sub MesajDinleyici_MessageReceived(ByVal sender As Object, ByVal e As System.Windows.Messaging.MessageReceivedEventArgs)

        If e.Message = "BASLA" Then Animasyon.Begin()

        e.Response = "OLDU"

    End Sub

[C#]

        void MesajDinleyici_MessageReceived(object sender, System.Windows.Messaging.MessageReceivedEventArgs e)

        {

            if (e.Message == "BASLA") Animasyon.Begin();

            e.Response="OLDU";

        }

Burada tanımlanan Response değişkeni içerisinde bilgi otomatik olarak mesajı gönderen uygulamayı geri gidecektir. Geri giden bu veriyi mesajı gönderen uygulamanın yakalayabilmesi için kesinlikle kendi içerisinde LocalMessageSender'ın SendCompleted event'ını dinlemesi gerekir. Söz konusu event'ın SendCompletedEventArgs parametresinin Response Property'si dinleyiciden gönderilen mesajı içerecektir. Mesajın göndericisine ait örnek kodu aşağıda inceleyebilirsiniz.

[VB]

    Private Sub btnGonder_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)

        Dim MesajGonderici As New Messaging.LocalMessageSender("MesajKanali", "localhost")

        AddHandler MesajGonderici.SendCompleted, AddressOf x_SendCompleted

        MesajGonderici.SendAsync("BASLA")

    End Sub

 

    Private Sub x_SendCompleted(ByVal sender As Object, ByVal e As System.Windows.Messaging.SendCompletedEventArgs)

        If e.Response = "OLDU" Then MessageBox.Show("olmuş!")

    End Sub

[C#]

        void MesajGonderici_SendCompleted(object sender, System.Windows.Messaging.SendCompletedEventArgs e)

        {

            if (e.Response == "OLDU")

            {

                MessageBox.Show("olmuş!");

            }

        }

 

        private void btnGonder_Click(object sender, RoutedEventArgs e)

        {

            System.Windows.Messaging.LocalMessageSender MesajGonderici =

                new System.Windows.Messaging.LocalMessageSender("MesajKanali", "localhost");

            MesajGonderici.SendCompleted += new EventHandler<System.Windows.Messaging.SendCompletedEventArgs>(MesajGonderici_SendCompleted);

            MesajGonderici.SendAsync("BASLA");

        }

Böylece Silverlight uygulamaları tarafında aynı makinede çalışan, ister aynı tarayıcıda olsun, ister olmasın farklı domainlerdeki uygulamaların istenen güvenlik kısıları ile birbiriyle konuşabilmesini sağlamış olduk. Hem de tüm bunları tamamen istemci tarafında sunucumuzu hiç yormadan hallettik.

Hepinize kolay gelsin.

Thursday, June 04, 2009 4:11:59 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Silverlight 3.0  | 
 Wednesday, June 03, 2009

Web uygulamalarında olsun Windows uygulamalarında olsun belki de en sık başvurduğumzu şey MessageBox'tır :) Sürekli bir yerlerde uyarılar gösterir ve en basit şekilde kullanıcıya bir mesaj iletmek için MessageBox yapısını kullanırız. Web ortamında da JavaScript alert imdadımıza yetişir. Peki ya Silverlight içerisinde?

"Silverlight içinde de MessageBox var!" dediğinizi duyar gibiyim :) ama MessageBox'ın biraz modası geçmedi mi artık? Şöyle daha güzel şeyler yapsak? Bir de üstüne yeri geldiğinde sadece bir mesaj değil de belki Web'deki PopUp'lar tadında farklı işlevsellikler içerebilen ekranlar da açmak istesek ne yapacağız? Tek tek UserControl'ler hazırlayıp sahnede uygun gerlerde göstermekle uğraşamayacak kadar basit bir işlevsellikten bahsediyoruz aslında. Tamam, daha fazla uzatmayacağım...

Karşınızda : "ChildWindow"!

Herhangi bir Silverlight projesine Solution Explorer içerisinde sağ tıklayarak "Add New Item" menüsüne geçtiğinizde karşınıza gelecek seçenekler arasına artık "Silverlight Child Window" da eklendi.

Yeni bir nesne tipi : Child Window
Yeni bir nesne tipi : Child Window

Silverlight içerisinde "UserControl" haricinde yapılara pek alışık değildik :) Silverlight 3.0 ile beraber karşımıza farklı şeyler çıkıyor. Bunlardan biri de System.Windows.Controls.dll'i altında bulunan ChildWindow nesnesi. Basit bir şekilde projenize bir ChildWindow eklediğinizde zaten ChildWindow'un otomatik olarak belirli standart bir tasarım ile geldiğini de göreceksiniz. Bu tasarım tabi ki Expression Blend ile açılıp değiştirilebilecek bir tasarım. Kontrolün XAML kısmına bakarsanız iki tane Button göreceksiniz, söz konusu buttonların Click durumlarında da arka planda ChildWindow'un DialogResult özelliği set ediliyor.

[VB]

    Private Sub OKButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles OKButton.Click

        Me.DialogResult = True

    End Sub

 

    Private Sub CancelButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles CancelButton.Click

        Me.DialogResult = False

    End Sub

[C#]

        private void OKButton_Click(object sender, RoutedEventArgs e)

        {

            this.DialogResult = true;

        }

 

        private void CancelButton_Click(object sender, RoutedEventArgs e)

        {

            this.DialogResult = false;

        }

Tahmin edeceğiniz üzere ChildWindow'un kullanımı epey kolay. ChildWindow'unuzu herhangi bir UserControl'den bir Instance'ını alarak Show metodu ile çağırabiliyor veya aynı şekilde Close metodu ile de uzaktan kapatabiliyorsunuz. Örneğin aşağıda gördüğünüz kod herhangi bir UserControl içerisinde biraz önce yarattığımız ChildWindow'u çağıran kodun ta kendisi.

[VB]

        Dim Yeni As New ChildWindow1

        Yeni.Show()

[C#]

            ChildWindow1 Yeni = new ChildWindow1();

            Yeni.Show();

Sanırım daha basit olamazdı. Tabi söz konusu ChildWindow'u çağırdıktan sonra kapandığından da haberdar olup hatta kapanmadan önce en son aldığı DialogResult değerin de elde etmek gerekir. Bunun için ChildWindow'un Closed event'ını yakalamanız yeterli olacaktır.

[VB]

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

        Dim Yeni As New ChildWindow1

        AddHandler Yeni.Closed, AddressOf yeni_Closed

        Yeni.Show()

    End Sub

 

    Private Sub yeni_Closed(ByVal sender As Object, ByVal e As System.EventArgs)

        Dim Sonuc As Boolean = CType(sender, ChildWindow1).DialogResult

    End Sub

[C#]

        void MainPage_Loaded(object sender, RoutedEventArgs e)

        {

            ChildWindow1 Yeni = new ChildWindow1();

            Yeni.Closed += new EventHandler(Yeni_Closed);

            Yeni.Show();

        }

 

        void Yeni_Closed(object sender, EventArgs e)

        {

            Nullable<Boolean> Sonuc = ((ChildWindow1)sender).DialogResult;

        }

ChildWindow'u Show etmeden önce Closed eventını bir event-listener'a bağlarsanız sonrasında ChildWindow kapatıldığında rahatlıkla durumdan haberdar olabilirsiniz. Closed event'ı çalıştırıldığı anda sender olarak gelen objeyi kendi ChildWindow'umuza cast ederek DialogResult özelliğini alabiliyoruz. Eğer "Bana sadece Boolean sonuç döndürnek yetmez" diyorsanız aslında çözüm basit. Kendi tanımladığınız ChildWindow'un arkasında sınıfta yeni Property'ler tanımlayabilir ve bunları istediğiniz tipte yaratabilirsiniz. Böylece Closed event'ına gelen sender da aslında sizin tanımladığınız nesne olacağı için istediğiniz Property'sine erişebilirsiniz. Eğer ChildWindow kapanmadan önce sizin tanımladığınız Property'ye bir bilgi aktarırsa bu bilginin rahatlıkla ana UserControl'e gelebileceği anlamına gelir.

ChildWindow karşınızda.
ChildWindow karşınızda.

Gelin hızlı ve teorik bir örnek yapalım. Hazırlayacağımız yeni ChildWindow bizden bir Integer parametre alsın. Bu parametreyi aldıktan sonra zaten ChildWindow kendi içerisinde istediğini yapar. İsterse kendi içine konabilecek bir TextBlock'a bile yazdırabilir. (Uyarı mesajı olabilirdi bu parametre) Ama biz farklı birşey yapacağız. ChildWindow'un OK düğmesine basıldığında kendisine daha önce verilen sayıyıyı ikiyle çarpıp geri verecek, Cancel düğmesine basılınca ise ikiye bölecek. Böylece yeni bir ChildWindow yaratırken nasıl parametre geçebileceğimizi ve söz konusu parametre üzerinde ChildWindow değişiklik yaptıktan sonra parametreyi nasıl geri alabileceğimizi görmüş oluruz.

[VB]

Partial Public Class ChildWindow1

    Inherits ChildWindow

 

    Public Sub New(ByVal deger As Integer)

        InitializeComponent()

        Me.Deger = deger

    End Sub

 

    Private PDeger As Integer

    Public Property Deger() As Integer

        Get

            Return PDeger

        End Get

        Set(ByVal value As Integer)

            PDeger = value

        End Set

    End Property

 

    Private Sub OKButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles OKButton.Click

        Me.Deger *= 2

        Me.DialogResult = True

    End Sub

 

    Private Sub CancelButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles CancelButton.Click

        Me.Deger /= 2

        Me.DialogResult = False

    End Sub

 

End Class

[C#]

namespace SilverlightApplication5

{

    public partial class ChildWindow1 : ChildWindow

    {

        public ChildWindow1(int deger)

        {

            InitializeComponent();

            this.Deger = deger;

        }

 

        public int Deger { get; set; }

 

        private void OKButton_Click(object sender, RoutedEventArgs e)

        {

            this.Deger = this.Deger * 2;

            this.DialogResult = true;

        }

 

        private void CancelButton_Click(object sender, RoutedEventArgs e)

        {

            this.Deger = this.Deger / 2;

            this.DialogResult = false;

        }

    }

}

Yukarıda kalın yazılı olarak gördüğünüz yerler bizim eklememiz gereken satırlar. Hemen ChildWindow'umuzun Constructor'ı ile işe başlayalım. Artık elimizdeki Constructor bir parametre istiyor. Bu parametre ChildWindow'a aktarılacak olan veriyi temsil edecek. Bizim örneğimizde Integer olabilir fakat siz farklı nesne tiplerini ve hatta kendi tanımladığınız nesne tiplerini de parametre olarak geçebilirsiniz. Sonrasında söz konusu parametreyle gelen değişkeni elde saklayabilmek için bir de Property tanımlıyoruz, hatta constructor çalıştığında da kendisine gelen veriyi Property'e atamasını sağlıyoruz.

Artık verimizi rahat rahat elimize aldığımıza göre Ok ve Cancel düğmelerinde söz konusu veriyi istediğimiz gibi değiştirebiliriz. Farklı senaryolarda ChildWindow içerisinde koyduğunuz farklı kontrollerden gelen verilere göre bu şekilde farklı Property'leri değiştirebilirsiniz. Peki söz konusu ChildWindow'u çalıştıran UserControl buradaki Property'ye nasıl ulaşacak? Aslında çok kolay.

[VB]

    Private Sub yeni_Closed(ByVal sender As Object, ByVal e As System.EventArgs) Handles yeni.Closed

        Dim Sonuc = CType(sender, ChildWindow1).Deger

    End Sub

[C#]

        void Yeni_Closed(object sender, EventArgs e)

        {

            int Sonuc = ((ChildWindow1)sender).Deger;

        }

Gördüğünüz gibi ChildWindow kapandıktan sonra nasıl DialogResult değerini aldıysak aynı şekilde kendi tanımladığımız Property'lere de ulaşabiliyoruz. Ne de olsa sender'ı kendi tanımladığımız ChildWindow1 sınıfına cast ediyoruz. İşte bir ChildWindow'a parametre gönderip farklı veriler alabilmek bu kadar kolay.

Hmm iyiymiş...

Kesinlikle muhteşem! İster özel Property'ler tanımlayın ister ChildWindow'un constructor'larını değiştirip farklı bilgiler atayarak onları göstermesini sağlayın ChildWindow'lar kullanım açısından çok pratik ve şık. Otomatik olarak kendi kendileri ekranın geri kalanını yarı şeffaf bir perde ile kapatıp kullanılamaz hale getirmeler ve hoş bir animasyon ile ekrana gelip gitmeleri gerçekten iş uygulamalarında genel kullanıma çok uygun. Fakat tabi ki özel durumlarda, kullanıcı deneyiminin ve tasarımın daha öne çıktığı senaryolarda kesinlikle ChildWindow'ların özelleştirilmesi gerekiyor.

Aklıma gelmişken :) Ben bir projede ChildWindow'ları sadece Loading animasyonları göstermek için kullandım. Gerçekten pratik oluyor. Loading mesajını verirken istediğimiz şey zaten kullanıcının başka birşey yapamaması. Tabi bu amaçla yaratılan bir ChildWindow'un Close vs gibi düğmelerinin tasarımdan Blend aracılığı ile kaldırılması gerekiyor. Arka planda yükleme işlemi bittiğinde siz ChildWindow'un Close metodu ile pencereyi ekrandan kaldırabiliyorsunuz. Eh pencerenin için de bir de Indeterminate state'te ProgressBar varsa zaten yeme de yanında yat :)

Hepinize kolay gelsin...

Wednesday, June 03, 2009 9:38:50 PM (GTB Standard Time, UTC+02:00)  #    Comments [2]   Silverlight 3.0  | 
 Monday, June 01, 2009

Haziran ayı ile beraber yaz dönemi geldi. Tatile gidemeyenler olarak :) gelin bu dönemi yenilenme/gelişme dönemi olarak değerlendirelim :) Bu ayı "Silverlight Öğrenme Ayı" ilan ediyorum ;) ve 12 Seminerden oluşan Silverlight WebCast serimi duyuruyorum!

Silverlight'a Giriş - 8 Haziran, 21.00
Bu webinerde Silverlight nedir sorusuna cevap verirken mimari yapısına bakarak Silverlight dünyasına bir giriş yapacağız. Silverlgiht projeleri nasıl oluşturulur,nasıl yayınlanır, XAML nedir? ve "Neden Silverlight" gibi soruların cevapları da bu webinerde.
https://www.livemeeting.com/cc/usergroups/join?id=GFRH8M&role=attend

Silverlight ile Animasyonlar - 9 Haziran, 21.00
Animasyon mantığını kavrayacağımız bu webinerde ilk olarak Expression Blend arayüzüne bakarak animasyonlarının Blend tarafında nasıl oluşturulabildiğine göz atacağız. Sonra bu animasyonların programatik olarak yaratılmasını ve kontrollerini inceleyerek Silverlight tarafında bir animasyon uzmanı olacağız.
https://www.livemeeting.com/cc/usergroups/join?id=6GRJPM&role=attend

Layout Kontrolleri ve Dinamik Arayüzler, 10 Haziran, 21.00
Silverlight içerisinde istediğiniz görsel arayüzü oluşturabilmek için arayüz oluşturma esnasından kullanabileceğiniz Layout kontrollerini çok iyi tanımanız gerekir. Diğer yandan dinamik olarak tüm nesneleri yaratbilmeniz, bu nesnelerin birbiri ile konuşabilmesi ve projenizi doğru biçimde UserControl yapıları ile şekillendirmeniz de şart. İşte bu webinerde bu konulara değineceğiz.
https://www.livemeeting.com/cc/usergroups/join?id=PBRM8P&role=attend

Kontrol Şablonları ve VisualStateManager, 11 Haziran, 21.00
Standart kontroller arasında sıkışmayın ve kontrollerinizi tamamen baştan yaratın. Hatta bu tasarımları tekrar ve tekrar kullanın. Bu webinerde silverlight kontrollerinin şablonlarının değiştirilmesini, Template Binding mekanizmasını ve visual State Manager'ın kullanımını Custom State yapıları ile beraber inceleyeceğiz.
https://www.livemeeting.com/cc/usergroups/join?id=R8JSQH&role=attend

Silverlight içerisinde Veri Erişimi, 12 Haziran, 21.00
İster klasik web servisleri kullanın, ister WCF servisleri veya ister Socket üzerinden haberleşin hepsinin de kendine göre avantaj ve dezavantajları var. Gelin hepsinin de Silverlight tarafındaki detaylarını beraber inceleyip Silverlight uygulamalarımızı veri ile tanıştıralım.
https://www.livemeeting.com/cc/usergroups/join?id=GH9T5W&role=attend

Offline/Online/Desktop çalışma yapıları, 15 Haziran, 21.00
Silverlight 3.0 ile beraber uygulamalarınızı masaüstüne birkaç tıklama ile taşıyabilirsiniz. Peki hem masaüstünde hem webde hem online hem offline çalışırken tüm bu sistemi, verilerinizi nasıl kontrol edeceksiniz. Bu webinerde farklı çalışma yapılarını göz atarken verilerimizi IsolatedStorage içerisinde saklayacağız.
https://www.livemeeting.com/cc/usergroups/join?id=QTZH9R&role=attend

İş Uygulamaları Yaparken Silverlight, 16 Haziran, 21.00
Animasyonlar hoştur, güzeldir ama herşey değildir. DataGrid'ler, Comboboxlar, Calendar'lar bir araya girdiğinde hepsinden bir iş uygulaması arayüzü yaratıp kontrol etmeye ne dersiniz? Bu webinerde hep beraber bir iş uygulamasının ekranını hazırlayacağız.
https://www.livemeeting.com/cc/usergroups/join?id=BGN6BT&role=attend

RIA Services, 17 Haziran, 21.00
Farklı bir bakış açısı ile Silverlight uygulamalarınızdan veritabanına erişimine çocuk oyuncağına dönüştürmek ister misiniz? RIA Services sadece verinin taşınmasını değil aynı anda kendi kontrolleri ile düzenlenmesini de kolaylaştırıyor. Nasıl olacağını merak ediyorsanız bekleriz.
https://www.livemeeting.com/cc/usergroups/join?id=P9KQ6B&role=attend

Silverlight projelerinde SEO, 18 Haziran, 21.00
Arama motorlarına dair optimizasyonların çoğumuz için önemli. Silverlight projelerinde özellikle asenkron veri erişimi nedeniyle söz konusu veriye arama motorları ulaşamıyor. Peki ya bir çözümü varsa? Tabi ki o çözümü paylaşma vakti.
https://www.livemeeting.com/cc/usergroups/join?id=Q6H2G7&role=attend

Video Aşkına! Canlı/Cansız Yayın, 19 Haziran, 21.00
İnternette televizyoz izlemek veya cansız kayıtları yayınlamak, amacınız ne olursa olsun video konusunda Silverlight çok güçlü. Bu webinerde Silverlight ile canlı yayın, Media Services ayarları, Adaptive Streaming, cansız yayın ve Video Player hazırlanması gibi konulara değineceğiz.
https://www.livemeeting.com/cc/usergroups/join?id=BNPK36&role=attend

DeepZoom Uygulamaları, 22 Haziran, 21.00
Yaklaştıkça daha çok detay görmek isteyenlerin tarzı DeepZoom uygulamalarını hızlı bir şekilde geliştirmenin yanı sıra sıfırdan DeepZoom uygulamalarını yazmak, uygulamaları özelleştirmek ve veri bağlantısı yaparak dinamik DeepZoom projeleri yaratmak isteyenler bu webineri kaçırmasın.
https://www.livemeeting.com/cc/usergroups/join?id=BZFCH9&role=attend

Tarayıcı Entegrasyonu, 23 Haziran, 21.00
Silverlight ile tarayıcı arasındaki ilişkiyi inceleyeceğimiz bu webinerde Silverlight tarafındaki C#/VB kodunuz ile DOM entegrasyonunun yanı sıra tam ekran uygulamaları, ön yükleme ekranlarını ve RunTime yükleme ekranlarını özelleştirmeye göz atacağız.
https://www.livemeeting.com/cc/usergroups/join?id=3GZKMH&role=attend

Webinerlerle ilgili güncel gelişmeleri ve hatırlatmaları isterseniz twitter üzerinden beni takip ederek edinebilirsiniz. Herkese faydalı olması dileği ile ;)

P.S.: Unutmadan :) Mümkünse bu seriyi bloglarınızda, üye olduğunuz mail gruplarda paylaşabilirseniz çok sevinirim. "Silverlight'ı nereden öğrenebiliriz" şeklinde özellikle Anadolu'nun farklı köşelerinden mailler alıyorum. 12 seminerlik bu seriyi düzenlememin en büyük nedeni de budur. En azından bu sefer "haberimiz yoktu" durumlarının oluşmaması için duyuru konusunda desteğini rica ediyorum. Ne kadar çok kişi faydalanırsa o kadar mutluluk verci olur benim için, hepimiz için.

Monday, June 01, 2009 10:03:54 AM (GTB Standard Time, UTC+02:00)  #    Comments [21]   Silverlight 2.0 | Silverlight 3.0  | 
 Wednesday, April 29, 2009

Bu hafta sonu için sizlere iki ayrı etkinlik duyurum var. Her ikisi de Ankara'da! Cumartesi günü SQL Kampı etkinliğimizin Ankara ayağını yapıyoruz, Pazar günü ise benim de konuşmacı olacağım NedirTV'nin 3. yıldönümü kutlama etkinliği olacak. Her ikisinin de planını aşağıda bulabilirsiniz.

02.05.2009 / SQL Kampı
09.30-10.30 Silverlight 3.0 ve İş Uygulamaları - Daron Yöndem
10.30-12.00 SQL 2008 Platform ve Development Yenilikleri - Tarık Kranda
13.00-15.00 Index Strategies and Performance Tunning - Pamir Erdem
15.30-17.00 IIS 7 üzerinde PHP ve MSSQL - Muammer Benzeş

03.05.2009 / NedirTV Günü
09:45-11.15 ASP.NET MVC - Uğur Umutluoğlu
11:30-13.00 What is SharePoint? - Burak Batur
14:00-15.30 WCF 4.0 & WF 4.0 - Burak Selim Şenyurt
15:30-16.30 WPF ve MultiTouch Programlama – Daron Yöndem

Her iki etkinlik de Bilkent Üniversitesi'nde gerçekleştirilecek. Merkez Kampüs Rektörlük Binası Mithat Çoruh Amfi Salonu'nda olacağız. Hepinizi bekliyoruz!

Not: Etkinlik sonrası "Haberimiz olsa gelirdik" diyen her birey saçımdan bir tel daha kopartıp üzerinde tepinmeme neden oluyor :) O nedenle sizden ricam etrafınızda bu etkinlik ile ilgilenebilecek herkesi haberdar etmeniz. Görüşmek üzere...

Wednesday, April 29, 2009 1:17:35 PM (GTB Standard Time, UTC+02:00)  #    Comments [1]   ASP.NET 3.5 | MultiTouch | Silverlight 3.0 | SQL Server 2008 | WPF  | 
 Tuesday, April 28, 2009

Bugün Muğla Üniversitesi'ndeydim. LINQ, WPF, Silverlight ve AJAX oturumları yaparak yine bir günde bolca bilgi paylaşımı gerçekleşti diyebilirim. Üniversitedeki gençlerin özellikle sınav sonrası olmasına rağmen seminerlerdeki enerjileri beni hayran bıraktı diyebilirim. Ne kadar şanslı olduklarını pek farkında değiller belki ama Visual Basit.NET gördükleri için bence çok ayrıcalıklılar :) Uzun süreden sonra ilk defa demolarımı rahat rahat VB ile yapabildim ve rezalet bir şekilde C# alışkanlıkları edinip VB'ye bulaştırmaya başladığımı gördüm :) Gereksiz toString'ler falan :) Her neyse...

Muğla üniversiteden mezun olan bir gencin VB.NET, ADO.NET ve SQL bilerek mezun olduğunu görmek beni çok sevindirdi. Bu üçlünün bir biri ile bağlantısını da kurduktan sonra aslında yapamayacağınız pek bir şey kalmıyor gibi. Üzerine bir de seminerlerimizdeki konuları eklerseniz süper yazılımcılar olacağınızdan emin olabilirsiniz.

Muğla Üniversitesi Seminerlerim
Muğla Üniversitesi Seminerlerim

Buradan etkinlikteki katkılarından dolayı Hasan Burak Öztürk kardeşim ve sevgili hocalarımıza çok teşekkür ediyorum. Umarım herkes için faydalı bir etkinlik olmuştur. ;)

Tuesday, April 28, 2009 8:43:29 AM (GTB Standard Time, UTC+02:00)  #    Comments [1]   AJAX | LINQ | Silverlight 3.0 | WPF  | 

Bugün Konya, Selçuk Üniversitesi'ndeydim. WPF MultiTouch, Silverlight 3.0 ve AJAX oturumları ile yoğun ve bir o kadar da eğlenceli bir gün geçirdik. Yine efsane potlar kırdım ama bu sefer yazdığım kodlarla :) Her neyse, konuya dönersek... Benim için biraz da duygusal bir buluşma oldu bu sene Selçuk Üniversitesi'ni ziyaretim. Geçen seneki ziyaretimde dördüncü sınıf öğrencilerinden bir grup cengaveri çok net hatırlıyordum. İşte o grup bir gördüm ki kendi şirketini kurmuş ve yurt dışında Outsource yazılım satıyor! İşte budur! Özellikle Anadolu üniversitelerini ziyaretimde gençlerin "Bu meslekte İstanbul'a gitmek lazım" şartlanmasının anlamsız olduğunu kanıtlayan bu örneği özellikle sizlerle buradan paylaşmak istedim.

Konya, Selçuk Üniversitesi Seminerlerim
Konya, Selçuk Üniversitesi Seminerlerim

Etkinlikteki katkılarından dolayı sevgili Serkan Cura'ya ve Nevzat Örnek hocamıza buradan çok teşekkür ediyorum. Umarım katılan herkese olabildiğince faydalı olmuştur. Seneye tekrar görüşmek üzere ;)

Monday, April 27, 2009 11:18:43 PM (GTB Standard Time, UTC+02:00)  #    Comments [4]   AJAX | MultiTouch | Silverlight 3.0 | WPF  | 
 Sunday, April 26, 2009

Silverlight 3.0 Beta ile beraber gelen ilginç özelliklerden biri de herhangi bir Silverlight uygulamasını doğrudan masaüstüne alabiliyor olmamız. Gelin öncelikle hızlı bir demo ile yeni yarattığımız bir uygulamayı nasıl masaüstüne aldığımıza ve nasıl gözüktüğüne bakalım.

Yeni bir Silverlight uygulaması yarattıktan sonra tarayıcı içerisinde bu uygulamayı çalıştırıp üzerine farenizin sağ tuşu ile tıklarsanız gelen menüde ilginç bir komut dikkatinizi çekecektir.

Install onto this computer?
Install onto this computer?

Gördüğünüz üzere komutun anlamı aslında epey açık :) diyor ki "Bu uygulamayı bilgisayarına yükle!" Tabi şu an için bu komut kullanılamıyor çünkü aktif değil. Gelin hemen bu komutu nasıl aktif hale getirebileceğimize göz atalım.

AppManifest dosyamız burada.
AppManifest dosyamız burada.

Visual Studio içerisinde Solution Explorer'dan uygulamanızın AppManifest.xml dosyasına erişmemiz gerekiyor. Bunun için ilk olarak hemen Solution Explorer penceresinin en üstündeki ikinci düğme olan "Show all files" düğmesine tıklıyoruz ve "My Project" altında Manifest dosyamızı bulup açıyoruz.

[AppManifest.xml]

<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment"

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

>

    <Deployment.Parts>

    </Deployment.Parts>

 

    <!-- Uncomment the markup and update the fields below to make your application offline enabled

    <Deployment.ApplicationIdentity>

        <ApplicationIdentity

            ShortName="Out of Browser Silverlight Application"

            Title="Window Title of Your Silverlight Application">

            <ApplicationIdentity.Blurb>Description of your Silverlight application</ApplicationIdentity.Blurb>

        </ApplicationIdentity>

    </Deployment.ApplicationIdentity>

    -->

</Deployment>

Yukarıdaki manifest dosyasının içeriğini görebilirsiniz. Bu dosya içeriğinde "yorum" satırı olarak yerleştirilmiş olan yeşil kısımlar aslında uygulamanın "Out Of Browser" yani "Tarayıcı Dışı" moduna alınabilmesini sağlayacak olan kodların ta kendisi. Basit bir şekilde buradaki kodları yorum olmaktan çıkarmamız uygulamamızın tarayıcı dışına taşınabilmesini sağlayacaktır. Tabi bu süreçte var olan XML kodlarının anlamına da bir göz atalım.

[AppManifest.xml]

<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment"

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

>

    <Deployment.Parts>

    </Deployment.Parts>

 

    <Deployment.ApplicationIdentity>

        <ApplicationIdentity

            ShortName="Uygulama adı uraya gelir!"

            Title="Uygulama penceresinde gösterilecek metin burada!">

            <ApplicationIdentity.Blurb>Uygulamanın uzun uzun tanımı, açıklaması da buraya gelecektir.</ApplicationIdentity.Blurb>

        </ApplicationIdentity>

    </Deployment.ApplicationIdentity>

 

</Deployment>

ApplicationIdentity XML tagları aslında uygulamanızın kimliğini tanımlayacaktır. Bu kimlik tabi ki son kullanıcı tarafından farklı noktalarda uygulamanızın adını vs özelliklerini yansıtacak bilgileri barındırıyor. Örneğin ShortName özelliğine uygulama adını kısaca yazıyoruz, Title özelliğinde ise uygulama Windows içerisinde tarayıcı dışında çalıştırılırken sahip olacağı kendi penceresinin üstünde yazılacak metni tanımlıyor. Diğer yandan Blurb kısmında ise uygulama ile ilgili açıklayıcı bilgilere yer veriyoruz.

Tüm bu ayarları tamamladıktan sonra uygulamamızı çalıştırmanın zamanı geldi.

Uygulamamızı artık tarayıcı dışına alabiliyoruz!
Uygulamamızı artık tarayıcı dışına alabiliyoruz!

Artık uygulamamızın ekranında herhangi bir yere sağ tuş ile tıkladığımızda gelen menüdeki "Install" deyiminin bulunduğu komutun aktif olduğunu görebiliyoruz. Ayrıca bizim manifest içerisinde ShortName olarak tanımladığımız uygulama adının da hemen bu komut içerisinde uygun yere yerleştirildiğini gördük. Eh bu durumda söz konusu komuta tıklayalım bakalım neler oluyor.

Uygulamayı yükleme onay penceresi.
Uygulamayı yükleme onay penceresi.

Yükleme komutunu verdiğimiz anda karşımıza yukarıdaki ekran geliyor ve bizi manifest içerisinde Title olarak verdiğimiz değeri de gösterek söz konusu uygulamayı bilgisayarımıza yüklemek üzere olduğumuza dair uyarıyor. Yükleme süresince istersek uygulamanın birer kısayolunu masaüstüne ve/veya başlat menüsüne alabiliyoruz.

Yükleme işlemini onayladığımız gibi Silverlight uygulaması karşımıza normal bir Windows programıymış gibi geliyor. Kendi penceresine sahip, tamamen tarayıcıdan bağımsız olarak uygulamamız karşımızda.

Silverlight artık tarayıcının dışında!
Silverlight artık tarayıcının dışında!

Bu noktada dikkat etmeniz gereken noktalardan biri de uygulamanın penceresinde Title metninin yanı sıra uygulamanın yüklendiği adresin (bizim örneğimizde localhost) de aynı şekilde pencere adına eklenmiş olması. Böylece kullanıcılar her zaman uygulamayı hangi adresten yüklediklerine görebilecekler. Unutmayın artık uygulamamızın masaüstünde ve başlat menüsünde kısayolları var!

Silverlight uygulamamızın masaüstü ve başlat menüsünde!
Silverlight uygulamamızın masaüstü ve başlat menüsünde!

Yukarıdaki ekran görüntüsünde de görebileceğiniz üzere uygulamamızın bir kısayolu masaüstünde bulunuyor. Bu kısayolun adı manifest içerisinde ShortName. Aynı şekilde başlat menüsüne konan kısayolun adı da ShortName üzerinden gelirken bu kısayollar üzerinde fare ile durduğumuzda gelen açıklamada ise Blurb bilgisini görüyoruz. Artık Silverlight uygulamamızın söz konusu bilgisayarda internet bağlantısı olmasa da rahatlıkla bu kısayollardan çalıştırılabilecektir.

Eğer kullanıcılar bu programı bilgisayarlarından silmek isterlerse doğrudan programı çalıştırıp Silverlight uygulamasının üzerinde herhangi bir yerde sağ tuş ile gelen menüden  "Remove Application" komutunu seçmek durumundalar. Silverlight 3 Beta ile beraber gelen bu özellik ile bilgisayarlara yüklenen Silverlight uygulamaları Denetim Masası'nda "Program/Ekle Kaldır" bölümünde gözükmeyecektir.

Silvelright uygulamasının bilgisayardan kaldırmak isterseniz...
Silvelright uygulamasının bilgisayardan kaldırmak isterseniz...

Tüm bu manzara içerisinde belki de hemen değiştirmek isteyeceğiniz şeylerden biri uygulamanın her yerde gözüken varsayılan ikonu olacaktır. Hem masaüstündeki hem başlat menüsündeki hem de uygulama penceresindeki ikonların hepsini birden tek tek değiştirebiliyorsunuz.

Tek tek ayarlanmış logolar...
Tek tek ayarlanmış logolar...

Uygulamamızda kullanılmak üzere dört farklı boyutta logolar ayarlamamız gerekiyor. Bu logoların 16, 32, 64, 128 piksellik dosyalarını doğrudan PNG olarak kaydedebilirsiniz. Söz konusu dosyaları Silverlight projenize eklemek için Visual Studio içerisinde Solution Explorer'a sağ tıklayıp "Add Existing Item" diyebilirsiniz. Önemli olan tüm bu dosyaları projenize ekledikten sonra Build Action'larını Content olarak ayarlamanız.

PNG'lerinin hepsinin de Content olarak ayarlanması şart!
PNG'lerinin hepsinin de Content olarak ayarlanması şart!

Artık logolarımız hazır olduğuna göre geçip manifest dosyamızdaki ayarları yapabiliriz. Kabaca manifest dosyasında ApplicationIdentity tagına Icons adında bir seri ekleyip farklı boyutlar için hangi dosyaların kullanılması gerektiğini belirteceğiz.

[AppManifest.xml]

<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment"

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

>

  <Deployment.Parts>

  </Deployment.Parts>

 

  <Deployment.ApplicationIdentity>

    <ApplicationIdentity

        ShortName="Uygulama adı uraya gelir!"

        Title="Uygulama penceresinde gösterilecek metin burada!">

      <ApplicationIdentity.Blurb>Uygulamanın uzun uzun tanımı, açıklaması da buraya gelecektir.</ApplicationIdentity.Blurb>

      <ApplicationIdentity.Icons>

        <Icon Size="16x16">logo_16.png</Icon>

        <Icon Size="32x32">logo_32.png</Icon>

        <Icon Size="64x64">logo_64.png</Icon>

        <Icon Size="128x128">logo_128.png</Icon>

      </ApplicationIdentity.Icons>

    </ApplicationIdentity>

  </Deployment.ApplicationIdentity>

 

</Deployment>

Artık manifest dosyamızın son halini yukarıda inceleyebilirsiniz. Icons adındaki serinin içerisinde tek tek Icon'ları tanımladık ve her Size için ayrı ayrı hazırladığımız Content olarak uygulama içerisinde bulunan dosyaların adlarını verdik. Silverlight uygulamamızın bulunduğu sayfayı açıp uygulamayı masaüstüne kaydetmeye kalktığımızda artık bu ikonlar ile karşılaşacağız.

Yükleme esnasında özel logo! :)
Yükleme esnasında özel logo! :)

Uygulamanın kısayollarında özelleştirilmiş logolar...
Uygulamanın kısayollarında özelleştirilmiş logolar...

Uygulama penceresinde özel logo!
Uygulama penceresinde özel logo!

Here yere özel logolarımız geldiğinde göre artık uygulama kimliğini çok daha rahat bir şekilde müşteriye yansıtabiliriz. Hatta Windows Taskbar'da da söz konusu logomuz ayrıca gözüküyor.

Taskbarda özel logo.
Taskbarda özel logo.

Ya kullanıcılar sağ tıklamaz ise uygulamamıza?

Bu noktaya kadar hoş bir şekilde uygulamaya sağ tıkladık yükledik ve aynı şekilde sağ tıklayıp bilgisayarımızdan sildik. Diyelim ki silme işlemi çok önemli değil :) Kimsenin pek de uygulamamızı silmesini istemiyoruz fakat yükleme noktasında daha kolay bir kullanıcı deneyimi sağlamak zorundayız. İnsanların uygulamamıza sağ tıklayarak o yükleme komutunu keşfetlemelerini bekleyemeyiz. Böyle bir durumda basit bir şekilde uygulama içerisinde herhangi bir yere "Yükle" düğmesi koymak bile sorunumuzu çözebilir.

[VB]

        If App.Current.Detach Then

            MessageBox.Show("Oldu!")

        End If

Yukarıdaki kodu herhangi bir düğmenin arkasına yazabilirsiniz. App.Current.Detach metodu otomatik olarak daha önceki örneklerimizde gördüğümüz yükleme ekranını kullanıcının karşına getirecektir. Bu metod geriye bir Boolean döndürdüğü için kullanıcının yükleme işlemini yapıp yapmadığını da rahatlıkla öğrenebilirsiniz. Burada özellikle dikkat etmeniz gereken bir nokta var, App.Current.Detach metodunu kullanıcı tarafından başlatılmış metodlarda çağırabilirsiniz, yani Page.Load gibi istediğiniz yerlerde bu pencereyi açamazsınız :) Bilginize.

Derinlere dalalım!

Artık uygulamamızı kendi kendimize kod ile de tarayıcıdan bağımsız hale getirebiliyoruz fakat ortalık biraz karışık durumda. Kendi yarattığımız düğmeler aracılığı ile kod ile kullanıcı uygulamamızı masaüstüne alırsa biz de bundan haberdar olabiliyoruz fakat ya kullanıcı gibip sağ tuş ile gelen menüden uygulamayı bilgisayarına alırsa? Bazı durumlarda biz bundan da haberdar olmak isteyebiliriz. Peki neden mi haberdar olmak isteriz?

Unutmayın ki bir Silverlight uygulaması normal şartlarda Online çalışmak üzere hazırlanır ve programlanır. Örneğin veritabanından veri çekmek için sonuçta bir sunucuya bağlanırız. Uygulama masaüstüne alındıktan sonra ise Offline da çalışabilir hale geliyor. Yani artık Silverlight uygulamanız her çalıştığında internete erişimi olmayacak! Bu durumda bizim uygulamanın tarayıcı içerisinde mi çalıştırıldığını yoksa dışarıda mı olduğunu anlamamız gerekiyor. Diğer yandan uygulama tarayıcı dışında olabilir fakat internet bağlantısı da o an için makinede bulunabilir.

İnternet var mı?

İlk olarak gelin uygulama her nerede olursa olsun, ister tarayıcı içinde ister tarayıcı dışında, internet bağlantısının durumundan nasıl haberdar olabileceğimize bakalım. Söz konusu durumla ilgili bize bilgi verecek olan Event-Handler System.Net.NetworkInformation.NetworkChange olarak geliyor. Bu event'ı yakalayarak bir değişiklik olduğunda System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable komutu ile makinede net olup olmadığını anlayabilirsiniz.

[VB]

Partial Public Class MainPage

    Inherits UserControl

 

    Public Sub New()

        InitializeComponent()

        AddHandler System.Net.NetworkInformation.NetworkChange.NetworkAddressChanged, AddressOf Network_NetworkAddressChanged

    End Sub

 

    Private Sub Network_NetworkAddressChanged(ByVal sender As Object, ByVal e As System.EventArgs)

        If System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable Then

            MessageBox.Show("NET VAR!")

        Else

            MessageBox.Show("yok")

        End If

    End Sub

End Class

[C#]

    public partial class MainPage : UserControl

    {

        public MainPage()

        {

            InitializeComponent();

            System.Net.NetworkInformation.NetworkChange.NetworkAddressChanged +=

                new System.Net.NetworkInformation.NetworkAddressChangedEventHandler(NetworkChange_NetworkAddressChanged);

        }

 

        void NetworkChange_NetworkAddressChanged(object sender, EventArgs e)

        {

            if (System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable())

            {

                MessageBox.Show("NET VAR!");

            }

            else

            {

                MessageBox.Show("yok");

            }

        }

    }

Yukarıdaki kodumuzda ilk olarak sayfanın Init durumunda ihtiyacımız olan NetworkAddressChanged event'ını bağlıyoruz. Sonrasında ise söz konusu event her çağrıldığında GetIsNetworkAvailable ile internet bağlantısı olup olmadığını kontrol ediyoruz. Böylece siz de uygulamalarınızda İnternet bağlantısından haberdar olup farklı senaryolar oluşturabilir belki kullanıcılarının offline data üzerinde çalışabilmesini sağlayıp değişiklikleri de IsolatedStorage içerisinde bir süre için kaydedebilirsiniz.

Uygulamam nerede çalışıyor?

Silverlight uygulamalarımızı hiçbir değişiklik yapmadan masaüstüne taşıyabiliyor olsak da aslında uygulamanın masaüstündeki hali ile internet tarayıcı içerisinde hali arasında görsel anlamda farklılıklar da yaratmak isteyebilirsiniz. Özetle uygulamanızın tarayıcı içerisinde mi yoksa dışarısında mı çalıştırıldığını anlamanız gerekebilir. Bu gibi bir durumda basit bir şekilde App.Current.RunningOffline metodundan faydalanabilirsiniz. Bu metod size doğrudan bir Boolean döndürecektir. Eğer sonuç True olursa uygulamanın tarayıcı dışında, False olursa tarayıcı içerisinde olduğunu anlayabilirsiniz. Metodun ismi aklınızı karıştırmasın söz konusu metodun internet bağlantısı olup olmaması ile herhangi bir ilişkisi yok.

Update mekanizması ne durumda?

İnternet üzerinden bilgisayarına Silverlight uygulamamızı yüklemiz kişiler rahatlıkla offline olarak da çalışabilecekler fakat eğer biz sunucu tarafında bir değişiklik yapar ve Silverlight uygulamamızı değiştirirsek ne olacak? Aslında buradaki Update mekanizması .NET Framework içerisinde ClickOnce mekanizmasına çok benziyor. Silverlight uygulaması masaüstünden çalıştırıldığında hemen online olup olmadığını otomatik olarak kontrol ediyor ve eğer online ise webdeki sürüme bakıyor. Webdeki uygulama ile lokaldeli aynı ise zaten bir sorun yok demektir. Eğer webdeki daha yeni bir sürüm ise arkaplanda yeni sürüm bilgisayara indiriliyor ve uygulama tekrar çalıştırıldığında istemci tarafında artık yeni sürüm çalıştırılmış oluyor. Bu mekanizma bu şekilde ilerlerken biz de tabi ki bu işleyişten haberdar olabiliyoruz. Örneğin belki de yeni bir sürüm var ise kullanıcıyı uyarmak isteyebiliriz!

Uygulamanın update mekanizmasının işleyişinden haberdar olmak için Application (uygulama) bazındaki event'lardan birini kullanmamız gerekiyor. Söz konusu event'ın adı ExecutionStateChanged. Bu event'a tabi ki uygulama içerisinde App.XAML'ın arkasında kod dosyasından ulaşabiliyoruz.

[VB]

    Private Sub App_ExecutionStateChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.ExecutionStateChanged

        If App.Current.ExecutionState = ExecutionStates.DetachedUpdatesAvailable Then

            MessageBox.Show("Yeni sürüm var lütfen uygulamayı baştan başlatın!")

        End If

    End Sub

[C#]

        public App()

        {

            this.Startup += this.Application_Startup;

            this.Exit += this.Application_Exit;

            this.UnhandledException += this.Application_UnhandledException;

            this.ExecutionStateChanged += new EventHandler(App_ExecutionStateChanged);

            InitializeComponent();

        }

 

        void App_ExecutionStateChanged(object sender, EventArgs e)

        {

            if(App.Current.ExecutionState== ExecutionStates.DetachedUpdatesAvailable)

            {

                MessageBox.Show("Yeni sürüm var lütfen uygulamayı baştan başlatın!");

            }

        }

App.xaml arkasında ExecutionStateChanged event'ı çalıştığı anda o anki uygulamanın ExecutionState'ini kontrol ediyoruz. Eğer ExecutionState DetachedUpdatesAvailable olarak geliyorsa demek ki yeni bir update var. Bu durumda kullanıcıya gerekli uyarıyı gösterebiliriz.

Arkaplanda neler oluyor?

Peki nasıl oluyor da bizim Silverlight uygulaması offline çalışıyor? Aslında konu epey basit. Herhangi bir Silverlight uygulaması Detach edildiğinde uygulamanın XAP dosyasının bir kopyası kullanıcının bilgisayarına alınıyor.

C:\Users\KullaniciAdi\AppData\LocalLow\Microsoft\Silverlight\Offline   

Yukarıda gördüğünüz yolda bulunan klasörün altına Silverlight uygulamasının bulunduğu alan adına ait bir klasör açılır ve söz konusu klasörün içerisinde XAP dosyası bir takım başka bilgilerle beraber kaydedilir.

Silverlight uygulaması diskte saklı.
Silverlight uygulaması diskte saklı.

Yukarıdaki ekran görüntüsünde de görebileceğiniz üzere XAP dosyamız orada duruyor. İçerisindeki ikonlarımızdan bir ico dosyası yaratılmış ve dışarıya yerleştirilmiş. Böylece işletim sisteminde tüm kısayollarda bu ico dosyası kullanılıyor. Uygulamamızı host eden bir de HTML var :) Bu HTML'i kim açıp bize gösteriyor kısmına birazdan geliriz. Onun öncesinde metadata dosyasının içeriğine bir bakalım.

[metadata]

LaunchPath=C:\Users\KullaniciAdi\AppData\LocalLow\Microsoft\Silverlight\Offline\localhost.1\index.html

CustomIcon=1

SourceDomain=localhost

OriginalUri=http://localhost:1559/ClientBin/SilverlightApplication29.xap

AppID=localhost.1

Description=Uygulamanın uzun uzun tanımı, açıklaması da buraya gelecektir.

Title=Uygulama penceresinde gösterilecek metin burada!

Name=Uygulama adı uraya gelir!

Gördüğünüz gibi uygulama içerisinde bazı manifest bilgilerimiz buraya da alınmış. Önemli noktalardan biri OriginalUri bilgisinde uygulamanın orijinal adresinin saklanıyor olması. Sistem bu bilgi üzerinden yeni sürüm kontrolü yapabiliyor. Peki tüm bunları kim çalıştırıyor? Silverlight Runtime kullanıcıların bilgisayarına yüklenirken beraber bir de sllauncher.exe adında bir program yükleniyor. Bu program aldığı parametreye göre bilgisayarda yüklü Silverlight uygulamalarını birer masaüstü uygulamasıymış gibi çalıştırabiliyor.

Hmm bu Adobe AIR değil mi?

Adobe tarafını takip edenlerin hemen soracakları ilk soru eminim ki "Bu Adobe AIR değil mi?" olacaktır. Hayır, değil. Neden mi? İlk olarak aradaki en büyük fark Adobe AIR için bilgisayarınıza ayrıca AIR Runtime yüklemek zorunda olmanız. Silverlight tarafında böyle bir durum yok. Hali hazırda yüklü olan Silverlight Runtime ile hayatınıza devam edebiliyorsunuz. İkinci en büyük fark ise Silverlight'ın bir masaüstü uygulaması haline dönüşse de hala tarayıcının güvenlik sınırlarında çalışıyor olması. Yani masaüstüne alsanız da bir Silverlight uygulaması masaüstündeki dosyalara kafasına göre erişemez, donanıma erişemez! Nasıl ki tarayıcı içerisinden sadece OpenFileDialog vs kullanarak dosyalara erişebiliyorsak masaüstüne alınmış bir Silverlight uygulaması da aynı şartlara tabidir. Özellikle donanım erişimi konusunda farklı senaryoların sertifikasyonlarla belki sağlanabileceğini söylese de Microsoft şu ana kadar resmi bir duyuru yok. Adobe tarafında AIR uygulamaları tam donanım erişimi ile çalıştığı için o tarafta sertifikasyon şart oluyor ve Silverlight'ın masaüstüne alınabildiği gibi bir tıkla kullanıcıya farklı bir deneyim sağlamak mümkün olmuyor. Diğer yandan Adobe tarafında update mekanizması için de ayrı bir SDK paketinde gelen sistemi kullanmak gerekiyor, oysa Silverlight 3.0 içerisinde söz konusu sistem entegre olarak geliyor.

Sonuç olarak baktığınızda Adobe elindeki teknoloji ile bir Windows uygulama geliştirme platformu oluşturmaya çalışırken Microsoft ise Windows tarafında WPF gibi zaten kendini kanıtlamış ve arkasında .NET Framework olan yapısını korurken web uygulamalarının rahatlıkla Offline kullanıma açılmasını hedefleyen ve çok kolay kullanılan bir yapıyı hedefliyor.

Kapanış!

Özünde Silverlight 3'ün bu özelliği ile çok ilginç senaryolar üretilebilir. Online ve Offline senaryoların beraber kullanımının çok daha rahat ve hoş bir kullanıcı deneyimi sağlayacağı kesin. Tüm bunları yapabilmek için de birkaç satır koddan ödeye geçmenize gerek kalmıyor.

Hepinize kolay gelsin!

Sunday, April 26, 2009 5:29:59 PM (GTB Standard Time, UTC+02:00)  #    Comments [2]   Silverlight 3.0  | 
 Saturday, April 25, 2009

Silverlight içerisinde tamamen kod ile çizim yapmanın ötesinde bazı durumlarda sıfırdan bir Bitmap oluşturmak da isteyebilirsiniz. Bazı durumlarda ise belki de elinizde var olan bir Bitmap nesnesini değiştirmek isteyebilirsiniz. Bu gibi durumlarda Silverlight 2.0 içerisinde derdimize derman olabilecek hazır bir API gelmezken artık Silverlight 3.0 içerisinde WriteableBitmap nesnesi bulunuyor. WriteableBitmap ile beraber istediğimiz Bitmap'i sıfırdan kod ile yaratabiliyoruz. Hatta bununla kalmayıp çok güzel şeyler de yapılabiliyor ama tüm bu güzel şeyleri görebilmek için biraz daha makeleyi okumaya devam etmeniz gerekecek :)

Hadi sıfırdan Bitmap yaratalım?

Sıfırdan bir Bitmap yaratmak demek aslında bir resim veya fotoğraf yaratırken her bir pikselde yer alacak renge karar vermek demektir. Bununla ilgili kullanabileceğiniz çok ilginç algoritmalar oluşturabilirsiniz. (Mandelbrot :)) Örneğin Quake1 zamanında 3D bir oyun olarak gözükse de özünde bu şekilde ekrana Bitmap'ler pompalayan bir yapıdan farklı bir mimarisi yoktu. Neyse konumuza dönelim ve bakalım biz kendi bitmapimizi nasıl yaratabileceğiz.

[XAML]

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

        <Image

           x:Name="imgFoto" />

    </Grid>

Yukarıdaki şekli ile basit bir Image nesnesini Silverlight uygulamamızın sahnesine yerleştiriyoruz. Bundan sonraki kodlarımızda söz konusu nesnenin içerisine farklı Bitmap'ler oluşturarak yerleştireceğiz.

[VB]

        Dim bitmap As New Imaging.WriteableBitmap(100, 100, Media.PixelFormats.Bgr32)

[C#]

            System.Windows.Media.Imaging.WriteableBitmap bitmap =

                new System.Windows.Media.Imaging.WriteableBitmap(100, 100,

                    System.Windows.Media.PixelFormats.Bgr32);

WriteableBitmap nesnesini yaratırken oluşturacağımız Bitmap'in genişlik ve yükseklik değerlerinin yanı sıra bir de Piksel formatını tanımlıyoruz. Bu aşamada iki seçenek söz konusu. Eğer Bgr32 kullanırsanız resminizde R (Red), G (Green) ve B (Blue) kanalları yer alacaktır. Eğer Pbgra32 kullanırsanız RGB'ye ek olarak bir de Alpha (şeffaflık) kanalı kullanabilirsiniz. Yani özünde eğer oluşturacağınız resmin şeffaflığı olacak ise Pbgra32, olmayacaksa Bgr32 kullanmanız uygun olacaktır.

[VB]

        bitmap.Lock()

Lock ve Unlock metodlarını Bitmap yaratma işleminin başında ve sonunda kullanacağız. Bu metodların amacı Bitmap yaratılırken veya değiştirilirken söz konusu değişikliklerin görsel olarak ekrana yansımasını engellemek. Lock işlemini de yaptığımıza göre artık yavaş yavaş çizimimizi yapmaya başlayalım.

[VB]

        For y As Integer = 0 To 99

            For x As Integer = 0 To 99

                Dim renkler(3) As Byte

                renkler(0) = x 'B (Mavi)

                renkler(1) = x 'G (Yeşil)

                renkler(2) = y 'R (Kırmızı)

                bitmap((x * 100) + y) = BitConverter.ToInt32(renkler, 0)

            Next

        Next

[C#]

            for (int y = 0; y <= 99; y++)

            {

                for (int x = 0; x <= 99; x++)

                {

                    byte[] renkler = new byte[4];

                    renkler[0] = (byte)x; //B (Mavi)

                    renkler[1] = (byte)x; //G (Yeşil)

                    renkler[2] = (byte)y; //R (Kırmızı)

                    bitmap[(x * 100) + y] = BitConverter.ToInt32(renkler, 0);

                }

            }

WriteableBitmap içerisinde herhangi bir piksele ulaşmak gerçekten çok kolay. Ulaşmak istediğiniz bir pikselin indeks numarasını doğrudan bitmap(0) şeklinde vererek söz konusu piksele ulaşabiliyorsunuz. Indeks numarası sürekli soldan sağa giderek satır bittiğinde de bir alt satıra geçerek devam ediyor. Böylece iç içe iki döngü kullanarak rahatlıkla tüm pikselleri gezebiliyoruz. Piksellere renk atama işlemini yaparken üçlü bir byte yaratıp 255 üzerinden RGB değerlerini verip sonra da Byte'ımızı Int32'ye çevirip Bitmap'imize atıyoruz. Şimdilik örneğimizde döngülerden değerler alıp kullandık ki her seferinde biraz daha farklı bir renk yaratılsın.

[VB]

        bitmap.Invalidate()

        bitmap.Unlock()

        imgFoto.Source = bitmap

Son olarak Invalidate metodu ile verdiğimiz değerlerden Bitmap'in çizilmesini ve Unlock ile de gösterilebilir olmasını sağladıktan sonra XAML içerisinde tanımladığımız Image nesnesine kaynak olarak atıyoruz.

Yarattığımız Bitmap karşımızda!
Yarattığımız Bitmap karşımızda!

Yukarıdaki görselde gördüğünüz Bitmap'i tamamen kendi kodumuz ile yaratmış olduk. Bu şekilde Bitmap yaratma proseslerini Multithread olarak çalıştırarak farklı animasyonlar yapmak da mümkün.

Başka başka?

WriteableBitmap'in bir diğer özelliği ise ekrandaki herhangi bir Silverlight elementinin görselliğini alabiliyor olması. Böylece belki de ekranda gösterdiğiniz bir Grid'in görselliğini resim olarak alabilir veya belki de oynattığınız bir videonun o anki karesini yakalayabilirsiniz. Tüm bunları WriteableBitmap ile yapmak gerçekten çok kolay.

[XAML]

<UserControl

   x:Class="SilverlightApplication26.MainPage"

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

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

   Width="400"

   Height="300">

    <Grid

       x:Name="LayoutRoot"

       Background="White">

        <Image

           Height="100"

           HorizontalAlignment="Left"

           VerticalAlignment="Top"

           Source="Flower.jpg"

           Width="100"

           x:Name="imgFoto" />

        <Image

           Height="100"

           HorizontalAlignment="Right"

           VerticalAlignment="Top"

           Width="100"

           x:Name="imgFoto2" />

    </Grid>

</UserControl>

Yukarıdaki gibi iki adet Image kontrolümüzün olduğu bir uygulama düşünelim. Image'lardan birinde Flower.jpg adındaki dosya gösterilirken diğeri ise boş. Biz kodumuz ile bir Image kontrolündeki görselliği alıp diğerine kaynak olarak vereceğiz.

[VB]

        Dim bitmap As New Imaging.WriteableBitmap(100, 100, PixelFormats.Pbgra32)

        bitmap.Render(imgFoto, imgFoto.RenderTransform)

        imgFoto2.Source = bitmap

[C#]

               System.Windows.Media.Imaging.WriteableBitmap bitmap =

                   new System.Windows.Media.Imaging.WriteableBitmap(100, 100, PixelFormats.Pbgra32);

                bitmap.Render(imgFoto, imgFoto.RenderTransform);

                imgFoto2.Source = bitmap;

Gördüğünüz gibi WriteableBitmap nesnemizi her zamanki gibi yarattıktan sonra tek yaptığımız Render metodunu çağırmak. Render metodu ilk olarak görselliği alacağı Silverlight elementinin adını istiyor. İkinci parametre ise söz konusu kontrole uygulanmış olan RenderTransform özellikleri. Böylece Render işlemi esnasında tam olarak doğru koordinatlar yakalanabiliyor. Sonrasında elimizdeki bitmap'i başka bir Image nesnesine kaynak olarak verebiliyoruz. Bu işlemi belirli aralıkla yaparsanız kabaca WPF'teki VisualBrush efektini elde etmeniz de mümkün.

Hepinize kolay gelsin.

Saturday, April 25, 2009 1:03:15 AM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Silverlight 3.0  | 
 Friday, April 24, 2009

Pixel Shader efektleri genelde oyun programcılarının duyduğu fakat web veya windows programcılarının pek de haşır neşir olmadıkları bir alandır. Tabi bunun birçok nedeni var; Pixel Shader efektleri HLSL (High Level Shading Language) denilen  farklı bir dil ile yazılarak DirectX'in SDK'sı ile beraber gelen bir compiler ile kullanılabilir hale geliyor. Her ne kadar HLSL C tabanlı dillere benzese de özünde esas problem bu efektlerin kullanılacağı ortamların azlığıydı.

Bundan yaklaşık 9 ay önce WPF'e SP1 ile beraber Pixel Shader efektlerinin gelmesi sonrasında sizlerle bu konuda bir yazı paylaşmıştım. Bugün ise konumuz Silverlight içerisinde Pixel Shader efeklerinin kullanımı. Silverlight 3.0 ile beraber artık istediğiniz bir Pixel Shader efektini herhangi bir UIElement yani kontrole rahatlıkla uygulayabiliyorsunuz. Hatta bu konuda Runtime ile beraber gelen hazır iki efekt bulunuyor; DropShadow ve Blur.

[XAML]

        <Button

           Content="TIKLA"

           Height="100"

           Width="100">

            <Button.Effect>

                <BlurEffect

                   Radius="10" />

            </Button.Effect>

        </Button>

Yukarıdaki XAML kodu içerisinde gördüğünüz üzere basit bir Button nesnesine Blur efekti vermek aslında gerçekten çok kolay. Blur efektinin piksellere uygulanırken ne kadar uygulanacağı ile ilgili bir de yarıçap verebiliyoruz. Her efektin kendine göre farklı parametreleri olabiliyor. Verilen bu efektler tamamen gerçek zamanlı olarak uygulandığı için gerektiğinde kod ile yaratılabilir ve kontrol edilebilirler.

[XAML]

        <StackPanel>

            <Button

               Content="TIKLA"

               Height="100"

               Width="100">

                <Button.Effect>

                    <BlurEffect

                       x:Name="btnBlur"

                       Radius="10" />

                </Button.Effect>

            </Button>

            <Slider

               Minimum="0"

               Margin="10"

               Value="{Binding Radius, ElementName=btnBlur, Mode=TwoWay}" />

        </StackPanel>

Hali hazırda herhangi bir efektin farklı özelliklerine animasyonlar uygulayabileceğiniz gibi isterseniz bu özellikleri yine Silverlight 3.0 ile beraber yeni gelen ElementBinding sistemini kullanarak editlenebilir hale de getirebilirsiniz. Yukarıdaki örneğimizde özellikle gidip BlurEffect nesnemize btnBlur ismini veriyoruz. Böylece artık bu efektin özelliklerine hem programatik olarak hem de farklı Binding'lerde rahatlıkla ulaşabiliriz.

[XAML]

        <Storyboard

           x:Name="BlurOL">

            <DoubleAnimation

               Duration="00:00:01.000"

               From="0"

               To="10"

               Storyboard.TargetName="btnBlur"

               Storyboard.TargetProperty="(BlurEffect.Radius)" />

        </Storyboard>

Örneğimizdeki BlurEffect nesnesinin Radius özelliğini hedef alıp anime eden bir animasyonun XAML kodunu yukarıda inceleyebilirsiniz. Söz konusu animasyon çalıştırıldığında düğmemiz bir saniye içerisinde yavaşça netliğini kaybediyor.

Peki hepsi bu kadar mı?

DropShadow ve Blur efektleri sadece hazır gelenler. Oysa esas güzellik bizim de kendi Pixel Shader efektlerimizi yazabiliyor olmamız. Tabi bunun için öncesinde bilgisayarınıza DirectX SDK'sını yüklemeniz ve SDK içerisindeki fxc aracını kullanabiliyor olmanız şart. Kabaca aşağıdaki komutlar ile yazmış olduğunuz bir PixelShader FX dosyasını PS dosyasına compile edebilirsiniz.

fxc /T ps_2_0 /Fo DaronEfekt.ps DaronEfekt.fx

Eğer bilgisayarınıza DirectX SDK'sını yüklemek istemiyorsanız aşağıdaki adresteki Silverlight uygulamasını kullanarak PS dosyaları hazırlayabilirsiniz. Sayfa açıldığında karşınıza çıkan ilk kutucuğa almak istediğiniz PS dosyasının adını, ikinci kutucuğa ise HLSL kodunu koymanız yeterli olacaktır. Sistem otomatik olarak PS dosyasını compile edip download linki sunacaktır.

http://www.voxpeeps.com/slpixelshadercompiler/

Pixel Shader efekti nasıl yazılır?

Bu makalenin amacı Pixel Shader nasıl yazılır sorusuna cevap vermek olmadığı gibi ben de kişisel anlamda bir Pixel Shader uzmanı sayılmam. O nedenle olabildiğince basit bir şekillde kabaca konunun üzerinden geçeceğiz.

[HLSL]

sampler2D input : register(s0);

float4 main(float2 uv : TEXCOORD) : COLOR

{

    float4 Color;

    Color = tex2D( input , uv.xy);

    Color.r=1;

    return Color;

}

Yukarıda gördüğünüz Pixel Shader efektinin yaptığı tek şey kendisine verilen piksellerdeki kırmızı renk değerini maksimuma çekmek. input parametresi üzerinden piksel bilgisi, yani bir anlamda boyama fırçası gelirken uv parametresi ise offset koordinatları içeriyor. X ve Y koordinatı olarak gelen bu bilgiler 0 ile 1 arasında oluyor. Yani özünde tüm koordinatlar 0,0 ile 1,1 arasında diyebiliriz. Tüm bu bilgiler üzerinden fırçamızdaki uygun koordinatlardaki rengi almak için ise text2D metodunu kullanabiliyoruz. Elimize geçen renk değişkeni ARGB değerleri olan klasik bir Color nesnesi olarak geliyor. Böylece biz hem renk hem de koordinatlar üzerinden rahatlıkla oynama yapabiliyoruz.

Yukarıdaki efekt kendi içinde bir değer alarak kırmızı rengini 1 olarak eşitliyor. Oysa biz bu değerin yeri geldiğinde bu efekte parametrik olarak gönderilmesini de isteyebiliriz. Böyle durumlarda PixelShader kodumuzda söz konusu parametreyi de tanımlamamız şart.

[HLSL]

sampler2D input : register(s0);

int kirmizilik : register(c0);

 

float4 main(float2 uv : TEXCOORD) : COLOR

{

    float4 Color;

    Color = tex2D( input , uv.xy);

 

    Color.r=kirmizilik;

    return Color;

}

Değişkenimizi tanımlarken register ettiğimiz c0 noktasına dikkat. Bu nokta üzerinden söz konusu parametreye Silverlight tarafından da ulaşabileceğiz. Eğer farklı parametreler tanımlayaracak c1, c2 şeklinde devam edebilirsiniz. Gelin daha detalara girip makalemizi bir HLSL makalesine çevirmeden PixelShader efektimizin Silverlight tarafındaki kullanım şekline göz atalım.

PS dosyamızı Silverlight ile nasıl kullanırız?

PS dosyasını ilk olarak Silverlight projenize sağ tıklayarak gelen menüden "Add / Existing Item" diyerek eklemeniz gerekiyor. Sonrasında söz konusu PS dosyasını Solution Explorer içerisinde seçili tutup Visual Studio'nun Properties paneline geçerseniz Build Action adında bir özellik göreceksiniz. Söz konusu özelliğe "Content" değerini vererek PS dosyasının XAP içerisine alınmasını sağlayabilirsiniz. Eğer bu şekilde bir ayarlama yapmazsanız PS dosyası projede kalır fakat XAP dosyasına eklenmez ve uygulamanız da söz konusu Shader efektini kullanamaz.

[VB]

Public Class DaronEfekti

    Inherits Effects.ShaderEffect

 

    Public Sub New()

        Dim YeniEfekt As New Effects.PixelShader

        YeniEfekt.UriSource = New Uri("DaronEfekt.ps", UriKind.Relative)

        Me.PixelShader = YeniEfekt

    End Sub

 

End Class

[C#]

    public class DaronEfekti : System.Windows.Media.Effects.ShaderEffect

    {

        public DaronEfekti()

        {

            System.Windows.Media.Effects.PixelShader YeniEfekt = new System.Windows.Media.Effects.PixelShader();

            YeniEfekt.UriSource = new Uri("DaronEfekt.ps", UriKind.Relative);

            this.PixelShader = YeniEfekt;

        }

    }

Yukarıdaki kodlar içerisinde de görebileceğiniz üzere kendi Shader efektimizi yaratırken Silverlight ile beraber gelen ShaderEffect sınıfını inherit ediyoruz. Bu sınıfın kendi içerisinde zaten mevcut bir PixelShader Property'si var. Söz konusu Property'ye biz de kendi PixelShader efektimizi yaratarak veriyoruz. Kendi PixelShader efektimizi yaratırken de UriSource olarak Silverlight projemize eklediğimiz PS dosyasının relatif yolunu vermeyi unutmuyoruz. Aslında basit bir şekilde Silvelright için PixelShader efektimizi yaratma işlemini bitirdik fakat unutmayın ki bizim efektin bir de ekstra parametresi vardı. İşte bu parametre için Silverlight tarafında da bir Dependancy Property tanımlamamız gerekiyor. Özellikle Dependancy Property yaratmamızın nedeni aslında bu Property üzerinden gerektiğinde StoryBoard'ların da animasyon yapabilmesini sağlamak.

[VB]

    Public Property Kirmizilik() As Double

        Get

            Return Me.GetValue(KirmiziProperty)

        End Get

        Set(ByVal value As Double)

            Me.SetValue(KirmiziProperty, value)

        End Set

    End Property

 

    Public Shared ReadOnly KirmiziProperty As DependencyProperty = _

    DependencyProperty.Register("Kirmizilik", GetType(Double), _

                                GetType(DaronEfekti), _

                                New PropertyMetadata(0.0, PixelShaderConstantCallback(0)))

[C#]

        public double Kirmizilik

        {

            get { return (double)GetValue(KirmiziProperty); }

            set { SetValue(KirmiziProperty, value); }

        }

 

        public static readonly DependencyProperty KirmiziProperty =

            DependencyProperty.Register("Kirmizilik", typeof(double), typeof(DaronEfekti), new PropertyMetadata(0.0, PixelShaderConstantCallback(0)));

Yukarıdaki kodların normal bir Dependancy Property'den tek farkı Metadata kısmı! İşte tam da bu noktada bir PixelShaderConstantCallback görüyoruz. Söz konusu Callback nesnesine parametre olarak PixelShader HLSL kodumuzu yazarken parametremize verdiğimiz c0'ın sayısal kısmını veriyoruz :) Artık herşey hazır. Sıra geldi bu efektimizi XAML içerisinde kullanmaya.

Efektimizi tepe tepe kullanalım :)

Efektimizi kullanabilmek için ilk aşamada XAML tarafında uygulamamızın assemblysini bir XML namespace olarak tanımlıyoruz. Sonrasında söz konusu namespace üzerinden rahatlıkla efektimizi herhangi bir nesneye atayabiliyoruz. Tabi bu işlemleri C# veya VB kodu ile de yapabiliriz.

[XAML]

<UserControl

   x:Class="SilverlightApplication21.MainPage"

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

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

   xmlns:daron="clr-namespace:SilverlightApplication21"

   Width="400"

   Height="300">

    <UserControl.Resources>

        <Storyboard

           x:Name="BlurOL">

            <DoubleAnimation

               Duration="00:00:01.000"

               From="0"

               To="1"

               Storyboard.TargetName="Renk"

               Storyboard.TargetProperty="(DaronEfekti.Kirmizilik)" />

        </Storyboard>

    </UserControl.Resources>

    <Grid

       x:Name="LayoutRoot"

       Background="White">

        <StackPanel>

            <Button x:Name="btnTikla"

               Content="TIKLA"

               Height="100"

               Width="100">

                <Button.Effect>

                    <daron:DaronEfekti x:Name="Renk"></daron:DaronEfekti>

                </Button.Effect>

            </Button>

            <Slider

               Minimum="0"

               Maximum="1"

               Value="{Binding Kirmizilik, ElementName=Renk, Mode=TwoWay}" />

        </StackPanel>

    </Grid>

</UserControl>

Yukarıdaki XAML kodunda da inceleyebileceğiniz üzere hazırladığımız efektin özelliklerine bir Slider aracılığı ile erişebildiğimiz gibi gerekirse doğrudan söz konusu efektin bir özelliğine animasyon da verebiliyoruz. Örneğimizdeki animasyon Button kontrolünün görselliğindeki tüm kırmızılık renk değerlerini en düşük seviyesinden en yükseğe doğru anime edecektir.

Hazırı yok mu bunların?

Kendi Pixel Shader'larınızı yazmak her zaman doğru seçim olmayabilir :) Bazen başkaları yazsa da biz hemen kullansak hissiyatı kapılmamak elde değil. Esasen bu hissiyatın nedeni HLSL'in çoğumuza yabancı bir uzmanlık alanı olması. Bu çerçevede tabi ki konunun uzmanları "Özgür Yazılım" çerçevesinde :) codeplex'te açık kaynak kodları ile Silverlight için birçok Pixel Shader kütüphanesini paylaşmış durumdalar. Zaten bu kütüphanelerin neredeyse hepsi daha önce de WPF için downloada sunulmuştur.

http://wpffx.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=25285

Yukarıdaki adresten Silverlight 3.0 ile uyumlu Pixel Shader kütüphanelerini indirebilirsiniz.

Hepinize kolay gelsin.

Friday, April 24, 2009 12:53:19 AM (GTB Standard Time, UTC+02:00)  #    Comments [1]   Silverlight 3.0  | 
 Sunday, April 19, 2009

Sizinle yeni seminer kayıtları paylaşmayalı epey zaman geçmiş. Fırsat bulup uygun seminer ortamında kayıt almak emin olun pek kolay olmuyor :) Birçok ilginç problem ile karışlaşabiliyoruz. Sonunda Silvelright 3.0 Yenilikleri seminerimin sağlıklı bir kaydını almayı başardım. Umarım hepiniz için faydalı olur. Seminerde geçen konular ile ilgili zaman içerisinde daha detaylı blog yazıları yazacağım.

Yukarıdaki videoyu bilgisayarına indirmek isteyenler blogumun SeminerTV bölümünden bunu yapabilirler ;)

İyi seyirler...

Sunday, April 19, 2009 9:39:35 AM (GTB Standard Time, UTC+02:00)  #    Comments [4]   Seminer | Silverlight 3.0  | 
 Thursday, April 16, 2009

Bu makalemizde aslında uzun zamandır WPF tarafında bulunan fakat Silverlight 2.0'da olmayan bir özellikten bahsedeceğiz. Tabi bu özellikten bahsetmemizin nedeni ise artık Silverlight 3.0 ile söz konusu özelliği kullanabiliyor olmamız. Bahsettiğimiz özellik Element Binding özelliği. Element Binding'i kabaca bir kontrolün herhangi bir özelliğinin otomatik olarak başka bir kontrolün bir özelliğine bağlanması ve aradaki değer değişikliklerinin karşı tarafa otomatik olarak aktarılması şeklinde tanımlayabiliriz.

[XAML]

        <StackPanel>

            <TextBox x:Name="BirTextBox" ></TextBox>

            <Slider

               x:Name="BirSlider"></Slider>

        </StackPanel>

Ekranımızda yukarıdaki şekli ile bir TextBox ve bir de Slider kontrolü olduğunu düşünelim. İstediğimiz şey bu Slider kontrolündeki değerin sürekli olarak TextBox içerisinde gösterilmesi. Normal şartlarda böyle bir işlevsellik için basit bir şekilde Slider'ın ValueChanged durumunu yakalayıp sürekli olarak Slider'ın Value özelliğinden değer alıp, bunu da String'e çevirip TextBox'ın Text değerine eşitleyebilirdik. Eğer bu eşitlemenin çift taraflı olmasını istiyorsanız farklı taklalar ile TextBox'ın da TextChanged durumunu yakalayıp Slider'ın Value değerinin değiştirmeniz gerekecektir. Sonuç olarak her iki kontrolden birinde kullanıcı bir değişiklik yaptığında diğerine yansıyacak. Ama bu kadar kod yazmaktansa artık Binding tanımlayarak hızlıca ilerleyebiliyoruz.

[XAML]

        <StackPanel>

            <TextBox Text="{Binding Value, ElementName=BirSlider}" ></TextBox>

            <Slider

               x:Name="BirSlider"></Slider>

        </StackPanel>

Artık TextBox'ımızın Text özelliğine farklı bir değer veriyoruz. Bu değer özünde bir Binding nesnesi. Binding'imizi tanımlarken hedeflediğimiz Property olarak Value ve bu Property'nin alınacağı yer olarak da BirSlider adındaki Slider kontrolümüzü gösteriyoruz. Bu noktadan sonra projeyi çalıştırdığımızda Slider içerisinde kullanıcı tarafından bir değişiklik yapıldığında otomatik olarak TextBox'ın Text'inin de güncellendiğini görebiliriz. Oysa teknik açıdan baktığımızda birbirine Bind ettiğimiz Property'ler olan Text ve Value Property'lerinin tipleri farklı. Value bir Double olarak gelirken Text ise String olarak geliyor. Binding esnasında buna rağmen bir sorun yaşamıyoruz.

Diğer yandan eğer TextBox içerisindeki sayısal değeri el ile değiştirirsek bu değerin Slider'a yansımadığını görüyoruz. Bu sorunu halledebilmek için Binding tanımlarken özellikle Binding işleminin çift taraflı olarak yapılacağını da belirtmemiz gerekecek.

[XAML]

        <StackPanel>

            <TextBox

               Text="{Binding Value, ElementName=BirSlider, Mode=TwoWay}"></TextBox>

            <Slider

               x:Name="BirSlider"></Slider>

        </StackPanel>

Kod içerisinde de gördüğünüz üzere artık Binding'imizin modunu TwoWay olarak değiştirdik. Sonuç olarak kullanıcı TextBox içerisine sayısal bir değer girdiğinde bu değer otomatik olarak Slider'ın da Value özelliğine aktarılacaktır.

Peki ya bind edilecek tipler tutmazsa?

Eğer birbirine bind etmek istediğiniz Property'lerin tamamen birbiri ile alakasız ise bu sefer de Binding esnasında Converter yapılarını kullanabilirsiniz. IValueConverter arayüzünü implemente eden bir sınıf yaratıp rahatlıkla Binding esnasında gelen ve giden veriye müdahale edebilirsiniz.

Örneğin gelin bir Slider'ın Value'su ile bir Calendar kontrolünün SelectedDate özelliğini birbirine bağlamaya çalışalım. Normal şartlarda DateTime ile Double'ı birbirine bağlayamazsınız ayrıca Slider'ın başlangıç ve son değerleri yine birer Double iken belki biz farklı başlangıç ve son tarihleri arasında Slider'ın çalışmasını isteyeceğiz. İşte tam da bu noktada Sliderdan gelen değerleri birer tarih eşleştirirsek Calendar için de anlamlı bir veri kaynağı yaratmış oluruz.

[VB]

Public Class Cevirici

    Implements Data.IValueConverter

 

    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 Now.Date.AddDays(value)

    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, Date).Subtract(Now).TotalDays + 1

    End Function

End Class

[C#]

    public class Cevirici : System.Windows.Data.IValueConverter

    {

        public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)

        {

            return DateTime.Now.Date.AddDays((double)value);

        }

 

        public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)

        {

            return ((System.DateTime)value).Subtract(DateTime.Now).TotalDays + 1;

        }

    }

Yukarıda gördüğünüz Cevirici sınıfı IValueConverter arayüzünü implemente ettiği anda Convert ve ConvertBack metodlarına da sahip oluyor. Bu metodlar Binding esnasında gelen ve giden verinin farklı tiplere çevrimi için kullanılabilir. Bizim örneğimizde Convert metoduna gelen Double değerlerini o anki tarihe gün olarak ekliyoruz. Böylece Slider'dan 2 değeri geldiğinde aslında Calendar kontrolüne de bugünden 2 gün sonrasına ait tarihi vermiş oluyoruz. Eğer Calendar içerisinde bir seçim yapıldığında Slider'a da yansımasını istiyorsanız ConvertBack metodunu da yazmanız gerekiyor. Bu metoda da Calendar'dan SelectedDate geleceği için o anki tarih ile farkını bulup geri döndüyüroz.

[XAML]

<UserControl

   xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"

   x:Class="SilverlightApplication27.MainPage"

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

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

   xmlns:daron="clr-namespace:SilverlightApplication27"

   Width="400"

   Height="300">

    <UserControl.Resources>

        <daron:Cevirici

           x:Name="BirCevirici" />

    </UserControl.Resources>

    <Grid

       x:Name="LayoutRoot"

       Background="White">

        <StackPanel>

            <controls:Calendar

               SelectedDate="{Binding Value,

                               ElementName=BirSlider,

                               Converter={StaticResource BirCevirici},

                               Mode=TwoWay}"></controls:Calendar>

            <Slider

               x:Name="BirSlider"

               SmallChange="1"

               LargeChange="1"></Slider>

        </StackPanel>

    </Grid>

</UserControl>

Kod tarafında hazırladığımız Converter'ımızı XAML tarafında kullanabilmek için hemen daron adında bir XAML namespace'i tanımladım ve çalıştığım projenin Assembly'sine bağladım. Böylece söz konusu Assembly içerisindeki kontrolleri XAML tarafında kullanabileceğiz. Bir sonraki adımda kod tarafındaki Cevirici tanımından bir kopyayı UserControl'un Resource'larına alıyorum ve adını da BirCeviri koyuyorum. Artık BirCeviri nesnemizi Converter olarak istediğimiz Binding'lerde kullanabiliriz. Kendi Binding tanımımıza bu bilgiyi de ekledik mi işlem tamamdır. Unutmadan Binding'in modunu da TwoWay olarak ayarlayalım.

İşte bu kadar!

Gördüğünüz gibi kontrollerin farklı özelliklerini rahatlıkla birbirlerine gerektiğinde çift yönlü olarak da bağlayabiliyoruz. Bu bağlama işlemi esnasında çok farklı çeviri ve kontrol mekanizmaları oluşturabileceğimiz ValueConverter yapısına da göz attığımıza göre bir makalemizin daha sonuna geldik.

Hepinize kolay gelsin.

Thursday, April 16, 2009 8:40:27 AM (GTB Standard Time, UTC+02:00)  #    Comments [2]   Silverlight 3.0  | 
 Wednesday, April 15, 2009

DataForm kontrolüne hızlı bir şekilde göz atıldığında aslında ASP.NET'teki DetailsView kontrolüne benzetilebilir. Bu önyargı ile hızlıca konumuza giriş yaparken gelin ilk olarak bir Silverlight 3 projesine nasıl DataForm kontrolünü ekleyebiliriz inceleyelim.

DataForm kontrolü sahnede...

DataForm kontrolü Silverlight ile beraber gelen harici kontrollerden biri. Yani DataForm kontrolü Silverlight 3'ün RunTime'ında bulunmuyor. Öyle hemen istediğimiz yerde anında kullanamıyoruz. Eğer bizim uygulamamız bu kontrolü kullanmak istiyorsa ona ait DLL'i referans alarak kendi XAP dosyası içerisinde taşımak zorunda. Tabi bu harici DLL'ler ile ilgili cachleme vs işlemleri de mümkün fakat bu konuda makalemizin şimdilik dışında.

Durum böyle ise DataForm kontrolünü kullanabilmek için hemen  Visual Studio içerisinde yeni bir SL 3.0 projesi yaratıp projemize sağ tıklayarak "Add Reference" diyip System.Windows.Controls.Data.DataForm'ı referans olarak eklemeliyiz. Böylece artık bu DLL içerisindeki kontrolleri kullanabiliriz. Fakat daha işimiz bitmedi. Projemize eklemiş olduğumuz bu Assembly içerisindeki kontrolleri XAML tarafında veya Blend 3 içerisinde kullanabilmemiz için ayrıca XAML tarafına da DLL'imizi tanıtmamız gerekiyor.

[XAML]

<UserControl x:Class="SilverlightApplication10.MainPage"

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

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

   Width="400" Height="300"

   xmlns:dataControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm" >

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

        <dataControls:DataForm x:Name="birDataForm"></dataControls:DataForm>

    </Grid>

</UserControl>

Yukarıda gördüğünüz şekli ile DLL'imizi dataControls adı altında XAML tarafına da aldık. Sonrasında da içerisinde bulunduğumuz sayfadaki Grid'in içine yine dataControls XML namespace'i üzerinden giderek Assembly içerisinde DataForm sınıfından bir instance alıyoruz. Artık sahnede bir DataForm var. Tüm bu işlemleri otomatik olarak yapmak isterseniz Visual Studio içerisinde XAML dosyasını açarak araç çubuğundan da DataForm kontrolünü sürükleyip sahneye bırakabilirsiniz. Visual Studio sizin için otomatik olarak DLL'i referans alıp gerekli XAML tanımlamalarını da yapacaktır. Bu noktada ufak bir detaya dikkat çekmek istiyorum. Şu an Silverlight 3.0 Beta olduğu için Visual Studio içerisinde designer tarafında desteği biraz zayıf. O nedenle araç çubuğundan herhangi bir kontrolü XAML dosyanıza sürükleyip bırakmak isterseniz sadece kod tarafına bırakabileceğinizi söylemem gerek.

Hemen bir veri bağlayalım!

Kontrolümüz artık sahnede olduğuna göre hemen bir veri bağlayarak neler yapabiliyoruz göz atalım. İlk olarak bağlayacağımız veriyi temsil edecek olan sınıfımızı yaratalım.

[VB]

Public Class Entities

    Public Class Urun

 

        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 PSatis As Double

        Public Property Satis() As Double

            Get

                Return PSatis

            End Get

            Set(ByVal value As Double)

                PSatis = value

            End Set

        End Property

 

    End Class

End Class

[C#]

    public class Entities

    {

        public class Urun

        {

            private string PAdi;

            public string Adi

            {

                get { return PAdi; }

                set { PAdi = value; }

            }

 

            private double PSatis;

            public double Satis

            {

                get { return PSatis; }

                set { PSatis = value; }

            }

        }

    }

Gördüğünüz gibi sınıfımızın adı Urun ve içerisinde de iki adet farklı Property var. Bu sınıfımızdan yola çıkarak birden çok Urun yaratıp bunları da DataForm kontrolümüze veri kaynağı olarak vereceğiz. Ama gelin bunun öncesinde tek bir Urun sınıfı yaratarak DataForm'a verelim bakalım neler yapacak.

[VB]

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

        birDataForm.CurrentItem = New Entities.Urun() With {.Adi = "Ürün Adi", .Satis = 20}

    End Sub

[C#]

        void  MainPage_Loaded(object sender, RoutedEventArgs e)

        {

            birDataForm.CurrentItem = new Entities.Urun { Adi = "Ürün Adi", Satis = 20 };

        }

Daha önceden XAML tarafında yaratmış olduğumuz DataForm kontrolüne isim olarak birDataForm ismini verdiğimiz için kod tarafında da aynı isimle ulaşıp şimdilik DataForm'un CurrentItem özelliğine yeni bir nesne atıyoruz. Söz konusu nesne daha önce tanımlamış olduğumuz Urun sınıfının bir instance'ı.

DataForm içerisinde tek bir kaydın düzenlenmesi.
DataForm içerisinde tek bir kaydın düzenlenmesi

Gördüğünüz üzere DataForm otomatik olarak kendisine bağlanan veri içerisinde tüm Property'ler için hem Textblock ve Textbox'lar yerleştirebiliyor hem de söz konusu Property'lerin adlarını da yanına yazarak aslında kabaca bir veri giriş ve düzenleme ekranı oluşturuyor. DataForm içerisinde sol üstte bulunan Edit düğmesine tıklarsanız ortaya veriyi düzenleyebileceğiniz bir ekran gelirken "Save" düğmesi de hemen altta kendini gösteriyor. Tabi ki bu aşamadan sonra özelleştirilmesi gereken birçok nokta var. İşte biz de makalemizin devamında bu noktalara değineceğiz.

MetaData aşkı!

Eğer ASP.NET Dynamic Data projeleri üzerine biraz çalışma şansınız olduysa veri kaynağı olarak kullanılan sınıflara verilen MetaData'lar ile beraber birçok şeyin ayarlanabildiğini hatırlayacaksınız. DataForm ile beraber çalışırken de aslında çoğu kuralı MetaData'lar ile DataForm'a bağlayacağımız veri kaynağı seviyesinde ayarlamamız gerekiyor. Örneğin yukarıdaki projemizde Urun adındaki nesnemizi tanımlarken Adi ve Satis Property'lerinde tabi ki Türkçe karakterler kullanmadık. Oysa DataForm bu Property'leri istemci tarafına açarken belki de Türkçe karakterler de içeren düzgün isimleri açmalı değil mi?

[VB]

Public Class Entities

    Public Class Urun

 

        Private PAdi As String

        <System.ComponentModel.DataAnnotations.Display(Name:="Adı")> _

        Public Property Adi() As String

            Get

                Return PAdi

            End Get

            Set(ByVal value As String)

                PAdi = value

            End Set

        End Property

 

        Private PSatis As Double

        <System.ComponentModel.DataAnnotations.Display(Name:="Satış")> _

        Public Property Satis() As Double

            Get

                Return PSatis

            End Get

            Set(ByVal value As Double)

                PSatis = value

            End Set

        End Property

 

    End Class

End Class

[C#]

    public class Entities

    {

        public class Urun

        {

 

            private string PAdi;

            [System.ComponentModel.DataAnnotations.Display(Name = "Adı")]

            public string Adi

            {

                get { return PAdi; }

                set { PAdi = value; }

            }

 

            private double PSatis;

            [System.ComponentModel.DataAnnotations.Display(Name = "Satış")]

            public double Satis

            {

                get { return PSatis; }

                set { PSatis = value; }

            }

 

        }

    }

Yukarıdaki kod içerisinde Urun sınıfımızı tanımlarken neleri değiştirediğimize dikkat edelim. Her bir Property'i tanımlarken bir de MetaData veriyoruz. System.ComponentModel.DataAnnotations sınıfı altında kullanabileceğiniz bir çok farklı MetaData'yı bulabilirsiniz. Bunların bazılarına makalemiz içerisinde göz atacağız. Şu an için Display MetaData'sını vererek söz konusu Property'lerin DataForm tarafından hangi isimle gösterilmesi gerektiğini belirtiyoruz. Tabi ki bu noktada istediğimiz gibi Türkçe karakterler kullanabiliriz. Bundan sonraki örnek kodlarda Urun sınıfının tüm tanımını değil sadece MetaData koyduğumuz satırların etrafını yazacağım.

[VB]

        <System.ComponentModel.DataAnnotations.Display(Name:="Satış", Description:="Bu satış değeridir")> _

        Public Property Satis() As Double

Yukarıdaki şekli ile tanımlanan bir Display MetaData'sında ek olarak bir de Description verisi bulunuyor. Bu parametreye herhangi bir değer verilmesi halinde DataForm içerisinde bu Property'nin yanında bir ünlem işareti belirecektir. Kullanıcılar söz konusu ünlem işaretinin üzerine fareleri ile geldiklerinde ise açıklamanızı görebilirler.

MetaData içerisinde Description yer alırsa...
MetaData içerisinde Description yer alırsa...

Şu ana kadar yaptığımız işlemlerin hepsinde belki de en sinir bozucu noktalardan biri DataForm içerisinde tüm Property'lerin sınıf tanımındaki sıra ile gösteriliyor olmasıdır. Aslında bu sırayı da değiştirebiliyoruz.

[VB]

        Private PAdi As String

        <System.ComponentModel.DataAnnotations.Display(Order:=1)> _

        Public Property Adi() As String

            Get

                Return PAdi

            End Get

            Set(ByVal value As String)

                PAdi = value

            End Set

        End Property

 

        Private PSatis As Double

        <System.ComponentModel.DataAnnotations.Display(Name:="Satış", Description:="Bu satış değeridir", Order:=0)> _

        Public Property Satis() As Double

            Get

                Return PSatis

            End Get

            Set(ByVal value As Double)

                PSatis = value

            End Set

        End Property

Display MetaData'sını tanımlarken verebileceğiniz Order parametresi doğrudan bu Property'lerin hangi sıra ile DataForm içerisinde gösterileceğini belirliyor. Sıfırdan başlayarak vereceğiniz bu sayılarla küçükten büyüğe doğru gidecek sırada Property'ler ekrana getirilecektir. Bizim örneğimizde üstte Satis altta Adi parametreleri yer alıyor.

Binding yollarında...

İlk örneğimizde DataForm'a bağladığımız nesne kod tarafında içerisinde bulunduğu scope dışında yaşayamayacağı için pek anlamlı bir örnek olmamıştı. DataForm içerisinde edit işlemi yapılsa da söz konusu bağlı değişken arkada yaşamadığı için edit işleminin de anlamı kalmıyor. Fakat eğer DataForm'a bağladığınız değişkenleri kod tarafında sürekli canlı tutarsanız DataForm içerisindeki değişikliklerin doğrudan nesneye yansıtıldığını da görebilirsiniz. Duruma göre bu işlevselliği değiştirmek veya bazen arka taraftaki nesnede bulunan bazı Property'leri DataForm içerisinde göstermemek de isteyebilirsiniz. Örneğin bizim Urun nesnesinin bir PK property'si olsaydı kesin onu göstermek istemezdir.

[VB]

        <System.ComponentModel.Bindable(False)> _

        Public Property Adi() As String

Yukarıdaki gibi herhangi bir Property'yi doğrudan Bindable False olarak işaretleyebilirsiniz. Böylece söz konusu Property hiçbir şekilde bağlı olduğu kontroller tarafından gösterilmeyecektir. Diğer yandan bu Property'nin gösterilmesi fakat değerinin değiştirilemesini isterseniz bu sefer de aşağıdak şekilde Bindable'a ikinci bir parametre vererek tek yönlü veri bağlantısı oluşturabilirsiniz.

[VB]

        <System.ComponentModel.Bindable(True, ComponentModel.BindingDirection.OneWay)> _

        Public Property Adi() As String

Yukarıdaki şekli ile bu Property DataForm'un edit modunda gözükse de değeri değiştirilemeyecektir.

Ya Validation gerekirse?

Belki de DataForm kontrolünün en can alıcı noktalarından biri tüm validasyon işlemlerini de kısmen MetaData üzerinden yapabiliyor olmamız. Örneğin bazı Property'lerin kullanıcı tarafından boş geçilememesini isteyebilirsiniz. Bu ve bu gibi tüm Validation işlemleri için yine System.ComponentModel.DataAnnotations sınıfı altındaki tanımlardan faydalanabiliriz.

[VB]

        <System.ComponentModel.DataAnnotations.Required(ErrorMessage:="Boş geçilemez")> _

        Public Property Adi() As String

Yukarıdaki örnek kodda da görebileceğiniz üzere Required olarak işaretlediğimiz Adi Property'sine bir de ErrorMessage vermişiz. DataForm kontrolü bu Property ile ilgili gerekli validasyonları yapmakla kalmayacak, herhangi bir sorun olduğunda MetaData içerisinde tanımladığımız hatayı da güzel bir şekilde kullanıcıya gösterecek.

Basit bir validasyon örneği!
Basit bir validasyon örneği!

Validasyon yöntemlerinden bazıları bir önceki örneğimizdeki gibi hazır bir şekilde tanımlanmış bizim kullanımızı bekliyor. Bunlardan biri de doğrudan sayısal değerlerin alabilecekleri değer aralığını kontrol eden Range sınıfı.

[VB]

        <System.ComponentModel.DataAnnotations.Range(1, 10, ErrorMessage:="Aman!")> _

        Public Property Satis() As Double

Range MetaData'sını biz örneğimizde anlamlı olması açısından Satis Property'sine verelim. Artık yukarıdaki tanımlama ile beraber Satis Property'si kesinlikle 1 ile 10 arasında olmak zorunda. Aksi durumda MetaData içerisinde tanımladığımız ErrorMessage kullanıcıya gösterilecektir.

[VB]

        <System.ComponentModel.DataAnnotations.StringLength(10)> _

        Public Property Adi() As String

Hazır gelen kontrollerden biri de StringLenght kontrolü. Bu MetaData ile beraber söz konusu Property'e atanabilecek metin uzunluğunun en yüksek değerini tanımlamış oluyoruz. Böylece kullanıcılar daha yüksek değerler girdiğinde eğer tanımlanmış ise özel hata mesajları da gösterilebiliyor.

Eğer isterseniz tüm bu validasyonların yanı sıra daha karmaşık RegularExpression yapıları da kullanabilirsiniz. Bizim örneğimizde varsayalım ki kullanıcıların ürünlere isim verirken hep büyük harf ile başlamalarını istiyoruz. Bu durumda aşağıdaki gibi bir RegEx tanımlamasını işimizi görecektir.

[VB]

        <System.ComponentModel.DataAnnotations.RegularExpression("^[A-Z]+[a-zA-Z]*$", ErrorMessage:="Büyük harfle başlamalı")> _

        Public Property Adi() As String

Hardcore Validasyon?

Bazı durumlarda validasyon ile ilgili RegEx'ler de işinizi görmeyebilir. Bizim örneğimizde bu duruma uygun anlamlı bir senaryo yaratmak zor olsa da diyelim ki ürünlerimiz A harfi ile başlıyorsa fiyatlarının kesinlikle 10 ile 20 arasında olması gerekiyor. Bu senaryoyu gerçek hayata taşırken aslında farklı Property'lerin farklı iş kuralları ile kontrol edilmesi gerektiğini de düşünebilirsiniz. Böyle bir durumda tamamen farklı bir mekanizma kullanabilmek için CustomValidation yapısı bizi bekliyor.

[VB]

    Public Class Kontrol

        Public Shared Function UrunKontrol(ByVal BirUrun As Urun) As Boolean

            If BirUrun.Adi.StartsWith("A") Then

                If BirUrun.Satis > 10 And BirUrun.Satis < 20 Then

                    Return True

                Else

                    Return False

                End If

            Else

                Return True

            End If

        End Function

    End Class

[C#]

    public class Kontrol

    {

        public static bool UrunKontrol(Urun BirUrun)

        {

            if (BirUrun.Adi.StartsWith("A"))

            {

                if (BirUrun.Satis > 10 & BirUrun.Satis < 20)

                {

                    return true;

                }

                else

                {

                    return false;

                }

            }

            else

            {

                return true;

            }

        }

    }

Yukarıdaki gördüğünüz kod içerisinde Kontrol adında bir sınıfın içinde Shared / Static olarak tanımlanmış bir Validasyon metodu bulunuyor. Metod aslında alıştığımız .NET metodlarından farklı değil. UrunKontrol adını verdiğimiz bu metod kendisine parametre olarak verilen bir Urun nesnesini kontrol ederek geriye True veya False şeklinde Valid / Uygundur veya InValid / Uygun Değildir mesajı döndürmüş oluyor. Bu hazırladığımız validasyon metodunun herhangi bir Urun nesnesine uygulanması için ise Urun nesnesinin tanımına ufak bir MetaData eklemesi yeterli olacaktır.

[VB]

    <System.ComponentModel.DataAnnotations.CustomValidation(GetType(Kontrol), "UrunKontrol", ErrorMessage:="Olmadı!")> _

    Public Class Urun

[C#]

    [System.ComponentModel.DataAnnotations.CustomValidation(typeof(Kontrol), "UrunKontrol", ErrorMessage = "Olmadı!")]

    public class Urun

    {

    }

CustomValidation olarak tanımladığımız bu kontrol mekanizmasını sınıfımıza bağlarken ilk olarak validasyon yapacak olan sınıfın tipini veriyoruz. Sonrasında bu sınıf içerisinde hangi metodun kullanılacağını bir String parametre olarak verip bir de eğer istiyorsak validasyon işlemi False döndürdüğünde gösterilmek üzere ErrorMessage ayarlıyoruz. İsterseniz bu mekanizmayı sınıf bazında değil doğrudan Property bazında da kurabilirsiniz.

Herşey kontrol altında...

Tüm bahsettiğimiz validasyon sistemlerine ek olarak bazen bir ürün düzenlenmesi ile ilgili istemci tarafında DataForm ile yaratılan süreçlere müdahale etmek de isteyebilirsiniz. Örneğin bir ürün kaydedilirken adı boş ise belki ona varsayılan bir isim vermek isteyeceksiniz veya bir ürünün kayıt moduna geçildikten sonra herhangi bir değişiklik yapılmadan kayıt modundan çıkılmasını engellemek de isteyebilirsiniz. Bu gibi daha birçok senaryo olabilir.

[VB]

    Public Class Urun

        Implements ComponentModel.IEditableObject

 

        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 PSatis As Double

        Public Property Satis() As Double

            Get

                Return PSatis

            End Get

            Set(ByVal value As Double)

                PSatis = value

            End Set

        End Property

 

        Public Sub BeginEdit() Implements System.ComponentModel.IEditableObject.BeginEdit

 

        End Sub

 

        Public Sub CancelEdit() Implements System.ComponentModel.IEditableObject.CancelEdit

 

        End Sub

 

        Public Sub EndEdit() Implements System.ComponentModel.IEditableObject.EndEdit

 

        End Sub

    End Class

[C#]

        public class Urun : IEditableObject

        {

            private string PAdi;

            public string Adi

            {

                get { return PAdi; }

                set { PAdi = value; }

            }

 

            private double PSatis;

            public double Satis

            {

                get { return PSatis; }

                set { PSatis = value; }

            }

 

            public void BeginEdit()

            {

                throw new NotImplementedException();

            }

 

            public void CancelEdit()

            {

                throw new NotImplementedException();

            }

 

            public void EndEdit()

            {

                throw new NotImplementedException();

            }

        }

Kendi sınıfınıza IEditableObject interface'ini implemente ettiğiniz anda artık sınıfınızın BeginEdit, CancelEdit ve EndEdit gibi durumları olacak. Bu durumlara dair kod bloklarına yazacağınız kodlar edit işlemi başladığında, iptal edildiğinde veya bittiğinde çalıştırılacaktır. Böylece siz de bu süreçlerde nesne ile ilgili değişiklikleri rahatlıkla yapabilirsiniz.

Çoklu kayıt desteği de var.

DataForm kontrolünü epey karıştırmış olsak da aslında hiç bakmadığımız bir özelliği var. Örneklerimizin başından beridir sadece tek bir nesne yarattık ve DataForm'un CurrentItem'ına atadık. Oysa DataForm kontrolünün ItemsSource'una da birden çok nesne içeren listeler bağlanabilir.

 [VB]

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

        Dim liste As New System.Collections.ObjectModel.ObservableCollection(Of Entities.Urun)

        liste.Add(New Entities.Urun() With {.Adi = "Ürün Adi", .Satis = 20})

        liste.Add(New Entities.Urun() With {.Adi = "Ürün Adi2", .Satis = 30})

        liste.Add(New Entities.Urun() With {.Adi = "Ürün Adi3", .Satis = 40})

        birDataForm.ItemsSource = liste

    End Sub

[C#]

        void MainPage_Loaded(object sender, RoutedEventArgs e)

        {

            System.Collections.ObjectModel.ObservableCollection<Entities.Urun> liste = new System.Collections.ObjectModel.ObservableCollection<Entities.Urun>();

            liste.Add(new Entities.Urun { Adi = "Ürün Adi", Satis = 20 });

            liste.Add(new Entities.Urun { Adi = "Ürün Adi2", Satis = 30 });

            liste.Add(new Entities.Urun { Adi = "Ürün Adi3", Satis = 40 });

            birDataForm.ItemsSource = liste;

        }

Kodumuzda bu sefer tamamen deneme amaçlı olarak içerisinde birden çok Urun bulunan bir ObservableCollection kullanıyoruz. Uygulamamızı çalıştırdığımıda DataForm kontrlünün sağ üstünde navigasyon kontrolleri de yerini alıyor. Böylece artık DataForm kontrolü içerisinde birden çok nesnenin de gezilerek düzenlenebileceğini görmüş olduk.

Çoklu kayıt düzenleme ekranı.
Çoklu kayıt düzenleme ekranı.

Yukarıdaki ekran görüntüsünde sağ üst köşede gördüğünüz düğmeler sadece kayıtlar arasında gezintiyi sağlamıyor. Ayrıca bulunan kaydın düzenlenebilmesinin yanı sıra yeni bir kayıt eklenebilmesini hatta silinebilmesini de sağlayabiliyorlar. Hali hazırda nesnemize bir ObservableCollection bağladığımız için zaten tüm değişiklikler otomatik olarak nesneye de yansıyacaktır.

AutoGenerateFields ?

Sanırım ASP.NET ile biraz uğraşan herkes AutoGenerateFields deyince ne demek istediğimi anlayacaktır. Bir DataForm'un normal şartlarda kendisine verdiğimiz veriye uygun şekilde gerekli TextBox vs kayıt düzenleme kontrollerini otomatik olarak yaratmasını sağlayan özellik AutoGenerateFields'in varsayılan değeri olan "True" değerinde saklı. Eğer AutoGenerateFields özelliğini False olarak ayarlarsanız artık veri kaynağından gelen her verinin ne şekilde görsel ekrana yansıtılacağını tek tek sizin ayarlamanız gerekecektir. Tabi bu durum da bazı senaryolarda özelleştirme adına şart oluyor. Şimdi gelin bu konunun detaylarına bakalım.

[XAML]

        <dataControls:DataForm AutoGenerateFields="False" x:Name="birDataForm">

            <dataControls:DataForm.Fields>

                <dataControls:DataFormCheckBoxField />

                <dataControls:DataFormComboBoxField />

                <dataControls:DataFormDateField />

                <dataControls:DataFormTemplateField />

                <dataControls:DataFormTextField />

            </dataControls:DataForm.Fields>

        </dataControls:DataForm>

Yukarıdaki kod çalışır bir kod olmayacaktır. Fakat kabaca bir DataForm'un içerisinde gösterilecek verilerin ayarlanması ile ilgili kullanabileceğimiz Field tiplerini ve DataForm içerisine nasıl yerleştirebileceğimizi inceleyebilirsiniz. Bu Field tiplerinin her biri aslında farklı veri tiplerini hedefliyorlar. Örneğin CheckBoxField Boolean tipindeki verilerin gösterimi için rahatlıkla kullanılabilirken TextField ise metin tabanlı ve özünde TextBox olarak gösterilecek verileri temsil edecektir. Eğer bu Field tipleri size yeterli gelmiyorsa ve kendi özel Field tasarımınızı yaratmak istiyorsanız bu sefer de DataFormTemplateField'i kullanabilirsiniz.

[XAML]

        <dataControls:DataForm

           AutoGenerateFields="False"

           x:Name="birDataForm">

            <dataControls:DataForm.Fields>

                <dataControls:DataFormTextField

                   FieldLabelContent="Kayıt Adı"

                   Binding="{Binding Adi}" />

                <dataControls:DataFormTextField

                   FieldLabelContent="Satış Sayısı"

                   Binding="{Binding Satis}" />

            </dataControls:DataForm.Fields>

        </dataControls:DataForm>

Bir önceki adımda veri bağlantısını da yaptığımız DataForm üzerinden ilerlersek ilk olarak AutoGenerateFields özelliğini False olarak ayarlayıp sonrasında da iki tane TextField ekliyoruz. Her bir TextField'in veri kaynağından hangi veriyi alıp göstereceğini belirlemek için tabi ki veri kaynağımız olan nesnelerin property'lerine ait adları vermemiz gerekiyor. Bu aşamada her bir Field'in Binding özelliğine bir Binding atıyoruz ve Property adı ile veri bağlantısını da tamamlıyoruz. Son olarak yine her bir TextField'e de FieldLabelContent vererek bu Field'ler için yaratılacak TextBox'ların yanına konacak Label'ların da içeriğini belirlemiş oluyoruz. Ayrıca isterseniz FieldLabelPosition gibi Field'lere ayrı farklı özellikleri de değiştirerek ana görsel öğelerin dizilimine müdahale edebilirsiniz.

Eğer DataForm kontrolüne bağladığınız nesnelerin çok sayıda Property'si varsa bunları belirli gruplar ve altbaşlıklar ile de göstermek isteyebilirsiniz. Bunun için DataFormFieldGroup nesnesini kullanabilir hatta bu grupların arasına da DataFormSeparator'lar koyabilirsiniz.

[XAML]

        <dataControls:DataForm

           AutoGenerateFields="False"

           x:Name="birDataForm">

            <dataControls:DataForm.Fields>

                <dataControls:DataFormHeader

                   Content="Düzenlenecek şeyler" />

                <dataControls:DataFormSeparator />

                <dataControls:DataFormFieldGroup

                   Orientation="Horizontal">

                    <dataControls:DataFormTextField

                       FieldLabelPosition="Top"

                       FieldLabelContent="Kayıt Adı"

                       Binding="{Binding Adi}" />

                    <dataControls:DataFormTextField

                       FieldLabelPosition="Top"

                       FieldLabelContent="Satış Sayısı"

                       Binding="{Binding Satis}" />

                </dataControls:DataFormFieldGroup>

            </dataControls:DataForm.Fields>

        </dataControls:DataForm>

Yukarıdaki kod içerisinde ilk olarak bir FormHeader bulunuyor. Sonrasında bir Separator da kullandıktan sonra elimizdeki TextField'leri bir Fieldgroup içerisine aldık. FieldGroup'un Orientation özelliğini de Horizontal yaptığımızda artık bu grup içerisindeki kayıt düzenleme nesneleri yan yana gösterilecektir.

Özelleştirilmiş bir DataForm kontrolü.
Özelleştirilmiş bir DataForm kontrolü.

Daha da özelleştirelim, daha da!

Eğer yukarıdaki Field yapılarını özelleştirmek sizin ihtiyaçlarınızı gidermediyse aslında bir adım daha ileri giderek tüm görsel yapıyı değiştirme şansınız da var. Bunun için artık DataForm'un DisplayTemplate ve EditTemplate özelliklerine eğilmemiz şart.

[XAML]

        <dataControls:DataForm

           AutoGenerateFields="False"

           x:Name="birDataForm">

            <dataControls:DataForm.HeaderTemplate>

                <DataTemplate></DataTemplate>

            </dataControls:DataForm.HeaderTemplate>

            <dataControls:DataForm.DisplayTemplate>

                <DataTemplate></DataTemplate>

            </dataControls:DataForm.DisplayTemplate>

            <dataControls:DataForm.EditTemplate>

                <DataTemplate></DataTemplate>

            </dataControls:DataForm.EditTemplate>

            <dataControls:DataForm.InsertTemplate>

                <DataTemplate></DataTemplate>

            </dataControls:DataForm.InsertTemplate>

        </dataControls:DataForm>

Yukarıda gördüğünüz şekilde düzenlenen Template / Şablon yapıları birer DataTemplate olarak tanımlanır ve bu şablonlar içerisinde tanımlı görsel tasarımlar doğrudan DataForm kontrolü tarafından kullanılır.

[XAML]

        <dataControls:DataForm

           AutoGenerateFields="False"

           x:Name="birDataForm">

            <dataControls:DataForm.DisplayTemplate>

                <DataTemplate>

                    <StackPanel>

                        <TextBlock

                           Text="{Binding Adi}"></TextBlock>

                        <TextBlock

                           Text="{Binding Satis}"></TextBlock>

                    </StackPanel>

                </DataTemplate>

            </dataControls:DataForm.DisplayTemplate>

            <dataControls:DataForm.EditTemplate>

                <DataTemplate>

                    <StackPanel>

                        <TextBox

                           Text="{Binding Adi}"></TextBox>

                        <TextBox

                           Text="{Binding Satis}"></TextBox>

                    </StackPanel>

                </DataTemplate>

            </dataControls:DataForm.EditTemplate>

        </dataControls:DataForm>

Yukarıdaki XAMl içerisinde DataForm kontrolümüzün DisplayTemplate ve EditTemplate şablonlarına basit birer StackPanel yerleştiriyoruz. Bu StackPanel'ler içerisinde DisplayTemplate'te TextBlock'lar var, EditTemplate'te ise TextBox'lar var. Tüm bu kontrollerin Text özellikleri veri kaynağından uygun Property'lere bağlı durumda. Veri bağlama işlemini yine klasik Binding sistemi ile yapıyoruz. Kontrol normalde TextBlock'ları gösterirken düzenleme moduna geçince ise EditTemplate içerisindeki TextBox'ları gösterecektir. Tabi siz örneklerinizde basit birer StackPanel yerine çok daha özelleştirilmiş görsel tasarımlar kullanabilirsiniz.

Sonuç

Görüldüğü üzere DataForm kontrolü kendisinden beklenenden çok daha fazlasını sunabilecek bir kontrol olarak karşımızda. İş uygulamalarında sürekli hazırladığınız çoğu formun Silverlight içerisinde rahatlıkla ve en önemlisi de hızlı bir şekilde oluşturulabilmesini sağlıyor. İster basit ister karışık validasyon kurallarınız olsun, ister basit ister karışık ve özelleştirilmiş bir tasarımınız olsun DataForm kontrolü her durumda size uyum sağlayabilecek şekilde tasarlanmış. Eh hadi ;) Sıra sizde...

Hepinize kolay gelsin....

Wednesday, April 15, 2009 9:40:37 AM (GTB Standard Time, UTC+02:00)  #    Comments [4]   Silverlight 3.0  | 
 Tuesday, April 14, 2009

Bugün Elazığ, Fırat Üniversitesi'ndeydim. İlk önce bir WPF oturumu yaptık ve ufak bir mola sonrasında da Silverlight 3.0 dünyasına giriş yaptık. Özellikle öğrencilerin çoğunun .NET dillerinden en azından biri ile haşır neşir olduğunu görmek beni çok mutlu etti. Her zamanki gibi anlatacak şeyler bitmedi de bitmedi. Bir ara sanırım epey hızlı konuşmaya başladım ki görevli arkadaşlar beni gözleri ile uyardı :)

Elazığ, Fırat Üniversitesi, Silverlight 3.0 ve WPF Seminerlerim
Elazığ, Fırat Üniversitesi, Silverlight 3.0 ve WPF Seminerlerim

Buradan özellikle sevgili IEEE ekibine ve İbrahim Rıza Hallaç'a çok teşekkür ediyorum. Hocalarının da desteği ile gerçekten güzel bir şekilde ev sahipliği yaptılar diyebilirim. Umarım bir dahakine daha uzun süre kalma şansım olur ve LAB'lar da yaparak mutfakta ellerimizi kirletiriz biraz :)

Hepinize öpücükler...

Tuesday, April 14, 2009 10:13:12 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Silverlight 3.0 | WPF  | 
 Monday, April 13, 2009

Bugün Antalya'nın güzel havasında Akdeniz Üniversitesi'nin düzenlediği Internet Haftası etkinliğindeydim. Etkinliğin son oturumunda genç kardeşlerimle "Kullanıcı Deneyimi"nin yazılım dünyasındaki öneminden bahsettik. Bu çerçevede hızlı örnekler ile hem WPF hem de Silverlight 3.0'a göz gezdirerek hali hazırda bilgi birikimimiz ile daha zengin kullanıcı deneyimlerini nasıl sağlayabiliriz sorusuna cevap aradık.

Antalya, Akdeniz Üniversitesi, Silverlight 3.0 Seminerim
Antalya, Akdeniz Üniversitesi, Silverlight 3.0 Seminerim

Etkinlikte ben yılmadan dinleyen :) herkese buradan defalarca teşekkürler. En kısa zamanda daha geniş çaplı teknik ağırlıklı bir etkinlik ile tekrar görüşmek ümidi ile...

Monday, April 13, 2009 10:10:48 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Silverlight 3.0  | 
 Sunday, April 12, 2009

Bu hafta sonu bildiğiniz üzere INETA NEXT HİT'deydik! İlk gün sevgili Mehmet Nuri Çankaya'nın Windows 7 oturumundan sonra ben de ilk Silverlight 3.0 seminerimi sunmuş oldum. Sonrasında Doruk.NET'ten Vahriç Muhtaryan ve MVP'lerimizden Muammer Benzeş Hyper-V oturumda bizimle oldular. İkinci günün programındaki sürprizi ilk günün sonunda açıklamıştım :) ve ikinci güne "WPF ve MultiTouch" semineri ile başladık. Biraz donanım tabanlı sorunlar yaşamamıza rağmen hem MultiTouch hem de MultiPoint programlamaya bakma şansımız oldu. Bir sonraki oturumda yine Muammer bizimle oldu ve PHP On Windows oturumunu sundu. Öğle yemeğinden sonra yine MVP'lerimizden Uğur Umutluoğlu ASP.NET 4.0 ile gelen yeniliklerden Visual Studio 2010 üzerinde bahsederken Burak Selim Şenyurt da WCF ve WF 4.0'a hızlı bir giriş sağladı.

INETA NEXT'in sonundan bir kare...
INETA NEXT'in sonundan bir kare...

Tüm bu maraton içerisinde eminim ki hatalarımız da olmuştur. Umarım özünde yararlı bir hafta sonu ile herkes mutlu mesut ayrılmıştır.  Kendi adıma gerçekten güzel ve zevkliydi. Tüm oturumlarımda yaptığım örnekleri ve ek dosyaları topladım, hepsini aşağıdan indirebilirsiniz.

Silverlight 3.0 Sunumu ve Örnekleri (12042009_2.rar - 33,4 MB)
WPF ve Multitouch Sunumu ve Örnekleri (12042009_2.rar - 31,7 MB)

Sunday, April 12, 2009 8:52:10 AM (GTB Standard Time, UTC+02:00)  #    Comments [2]   Silverlight 3.0  | 
 Saturday, April 11, 2009

Bugün INETA NEXT'in ilk gününde Silverlight 3.0 oturumunu gerçekleştirdim. Benim de ilk Silverlight 3.0 oturumumda. Zamanlama açısından içeriği tam oturtsam da akıcılık açısından istediğimi yakaladım diyemem. Neyse zamanla hallolur o konu da :) SL 3.0 daha çok yeni.

Artık önümüzdeki günlerde Silverlight 3.0 ile ilgili makalelerimi yazmaya başlayacağıma dair müjdeyi sizlerle paylaşırken bu yazımda hafif bir giriş yaparak sonraki günlerde üzerinde çalışacağımız konularla ilgili gerekli altyapıyı bilgisayarınızda nasıl oluşturabileceğinize değineceğim.

Şu an yayında bulunan Silverlight 3.0 sürümünün Beta'sıdır, yani bir anlamda test sürümüdür diyebiliriz. Hali hazırda bu sürüm ile hazırlanmış Silverlight uygulamalarını çalıştırabilmeniz için de tabi ki Silverlight 3.0 RunTime'ın bilgisayarınızda kurulu olması gerekiyor. İşte tam da bu noktada aslında Microsoft bize ufak ip uçları veriyor. Şu anda Silverlight 3.0'ın RunTime'ını haricen indirebileceğiniz herhangi bir adres yok. Runtime sadece Silverlight 3.0'ın SDK'sını da içeren yazılımcılar için hazırlanmış yükleme paketi içerisinde bulunuyor. Silverlight 2.0 zamanlarında da bildiğimiz üzere bu şekilde yazılımcılar için hazırlanmış paketlerin içindeki RunTime'lar da aynı şekilde "Developer Runtime" olarak karşımıza çıkıyordu ve yazılımcılar için debugging vs gibi konularda yardımcı olacak altyapılar sunuyordu. Sözün özü şudur ki; Silverlight 3.0'ın şu anda sadece Developer Runtime'ı var! Yani bu uygulamalarını dağıtmak mümkün değil. Zaten lisans olarak da şu anda Go-Live lisansı yok, hazırladığınız uygulamaları müşterilerinize açmanız, satmanız vs resmi olarak mümkün değil.

Tüm bu ticari detayları atladıktan sonra gelelim bir de teknik detaylara. Silverlight 3.0 Beta olarak geldiğine göre tüm beta yazılımlarda olduğu üzere Silverlight 3.0'ı da normal kullandığınız makineye yüklememenize fayda var. Beta yazılımların yüklenmesinden sisteminize gelebilecek olası zararlarda Microsoft dahil kimse sorumluluk kabul etmez. Diğer yandan eğer ki hali hazırda Silverlight 2.0 uygulamaları geliştiriyorsanız zaten Silverlight 3.0 yüklemelerini de kesinlikle yapmamaız gerekiyor. Beta aşamasında Silverlight 3.0 ile 2.0 arasında bir "targeting" sistemi yok. Bilgisayarınız SL 3.0 yüklerseniz artık Visual Studio içerisinde 2.0 projeleri yaratamazsınız. Bu konuda farklı taktikler kullanmak mümkün olsa da benim kişisel tavsiyem SL 3.0'a harici bir işletim sisteminde denemenizdir.

Neler yüklemek lazım?

Silverlight 2.0 zamanlarından da alışık olduğunuz üzere yine Visual Studio 2008 SP1 üzerine kurulması gereken bir Tools paketimiz var. Bu paket içerisinde Silverlight 3.0 için gerekli proje şablonları, Visual Studio intellisense desteği, SDK ve Developer Runtime bulunuyor. Söz konusu tüm yüklemeleri aşağıdaki adresten bulabilirsiniz.

http://silverlight.net/getstarted/silverlight3/default.aspx

Silverlight 3 Beta Tools for Visual Studio yüklemesini tamamladıktan sonra sıra geliyor Expression Blend 3 ile ilgili yüklemelere. Hemen Blend 3'ün şu an yayında olan MIX Preview sürümünü de indirerek bilgisayarınıza yükleyin. Aksi halde SL 3.0 projelerini eski Blend 2.0 ile açarsanız maalesef SL 3.0'ın özelliklerine dair destek bulamazsınız.

Tüm bunların haricinde yapabileceğiniz birkaç minik ayrı yükleme daha var fakat onlar farklı senaryolara özel olduğu için bu yazıda bahsetmeyeceğiz. İleriki yazılarda gerektikçe o yüklemeleri de ele alır ve kullanımlar ile beraber inceleriz.

Hadi bakalı, hazır mısınız? :)

Saturday, April 11, 2009 10:01:08 PM (GTB Standard Time, UTC+02:00)  #    Comments [7]   Silverlight 3.0  | 
 Friday, April 03, 2009

İki gündür Adana, Çukurova Üniversitesi'ndeydim. İlk gün Microsoft'un Gençsen Geleceksin etkinliğinde konuşmacı olduktan sonra ikinci gün de teknik oturumlar ile Silverlight 3.0 ve WPF üzerine eğildik. Sanırım Türkiye'de ilk defa Silverlight 3.0 seminerini de kısmen Adana'da yapmış olduk fakat itiraf etmem gerek :) daha anlatmadığım çok şey var.

Adana, Çukurova Üniversitesi
Adana, Çukurova Üniversitesi

Benim için gerçekten çok eğlenceli ve tabi ki bol bol "Adana" yenilen bir etkinlik oldu. Ayrıca şehrin ve üniversitenin etrafındaki doğaya hayran kaldığımı da söylemem gerek. Muhteşem fotoğraflar çekme şansım oldu :)

Adana'dan bir manzara!
Adana'dan bir manzara!

Buradan özellikle etkinlik boyunca beni yalnız bırakmayan hatta muhteşem bir şekilde ağırlayan IEEE ekibine binlerce teşekkürler. Sevgili Erdil Tengiz kardeşime de özellikle öpücükler :) Aklınızda bulunsun Adana'da kebapçıya giderseniz Erdil veya IEEE kelimelerini kullanırsanız hesap ödemiyorsunuz :) İşin güzel tarafı Erdil de ödemiyor :D

Neyse :) etkinliğe katılan tüm dostlara ve kardeşlerime çok teşekkür ediyorum, umarım olabildiğince faydalı bir etkinlik olmuştur. En kısa zamanda tekrar görüşmek üzere...

Friday, April 03, 2009 12:41:05 AM (GTB Standard Time, UTC+02:00)  #    Comments [5]   Silverlight 3.0 | WPF  | 
 Wednesday, April 01, 2009

2009 Silverlight MVP'liği!Geçen yıl bugünlerde sizlerle ASP.NET alanında MVP olmamın müjdesini paylaşmıştım. Bu süreçte şöyle kabaca baktığımda son bir yılda 300'ü aşkın blogpost, 100'ü aşkın teknik makale ile bildiklerimi paylaşmaya çalışırken seminerler ile de hesabıma göre 15.000 kişiye ulaşmışım. İtiraf etmek gerekirse "önümüzde yılda daha fazlasını yapacağım" gibi bir iddiada bulunamam :) ama aynı tempo devam edecek diyebilirim.

Silverlight MVP'si oldum!

Bu yazıyı yazmamın esas nedeni sanırım belli oldu :) 2009 yılında da MVP ödülüne layık görüldüm. Bu sefer ödülü aldığım alan Silverlight oldu. Biraz şanslı bir olarak dünyadaki ilk Silverlight MVP'lerindenim çünkü bu uzmanlık alanı daha yeni açıldı :) Ortadoğu-Afrika bölgesinde ise en azından bu dönemde aldığım duyumlara göre tek Silverlight MVP'si olmuşum. Bu durum özellikle bölgede Türkiye'nin önderliği adına, kendi kendime mutlu olduğum bir detay oldu diyebilirim.

Hepinize bu süreçte gösterdiğiniz destek için çok teşekkür ediyorum.

Wednesday, April 01, 2009 10:53:32 PM (GTB Standard Time, UTC+02:00)  #    Comments [32]   Silverlight | Silverlight 2.0 | Silverlight 3.0  | 
 Tuesday, March 31, 2009

Uzun bir aradan sonra tekrar güzel bir etkinlik ile karşınızdayız. Bundan yaklaşık iki ay önce sizlere erken duyurusunu yaptığım NEXT HIT etkinliğimizin programını duyurmanın zamanı geldi!

11 Nisan

09.30-10.00    Açılış
10.00-10.45    Windows 7 Deep-Dive  - Mehmet Nuri Çankaya
11.00-13.00    Silverlight 3.0 - Daron Yöndem
14.00-15.30    Bulut Bilişimi ve Azure - Panel
16.00-17.00    PHP On Windows ve Hyper-V - Muammer Benzeş

12 Nisan

10.00-12.00    Sürpriz Oturum - Daron Yöndem
13.00-15.00    ASP.NET 4.0 - Uğur Umutluoğlu
15.15-17.00    WCF 4.0 ve WF 4.0 - Burak Selim Şenyurt

Programımız epey yoğun ve muhteşem yenilikler var. Konularımızın hepsi taze. Eminim ki hemen akıllarda "Süpriz Oturum nedir?" sorusu belirecek. Süpriz oturumumuzu şu anda açıklayamıyoruz. Süprizimizi ancak 11 Nisan günü açıklayabileceğiz.

Katılım için ne yapmam gerek?

Aktivitemiz Yıldız Teknik Üniversitesi, Beşiktaş Kampüsü Oditoryumu'nda gerçekleşecek. Katılım için inetatr.org adresinden kayıt yaptırmanız yeterli. Bir önceki Professional Hit'te olduğu gibi yer sıkıntımız yok, veya size tekrar kayıt olmanız için ikinci mailler atmayacağız. Bu konuda geçen sefer yaşanan sıkıntıları çok net biliyorum ve bu neden özellikle üzerinde durarak bu sıkıntıların tekrar yaşanmayacağı konusunda emin olmanızı istiyorum.

Hepinize 11-12 Nisan'da görüşmek üzere! Unutmadan, güzel hediyelerimiz var :) bu sefer "su" sıkıntımız da yok :)

Dipnot!

Bu aktivite için blogumu okuyan herkesten duyuru anlamında yardım rica ediyorum. "Haberimiz yoktu" diyerek aktivite sonrası mail atan çok kişi oluyor. O nedenle elinizden geldiğince duyuru konusunda yardımcı olabilirseniz herkesin rahatlıkla faydalanmasını sağlayabiliriz. Şimdiden hepinize çok teşekkürler.

Tuesday, March 31, 2009 12:14:45 AM (GTB Standard Time, UTC+02:00)  #    Comments [7]   Silverlight 3.0 | WCF | WF | Windows 7 | WPF | ASP.NET 4.0  | 
 Wednesday, March 18, 2009

Ve MIX'in ilk günü bitti :) Muhteşem haberler ve gelişmeler var paylaşmak istediğim fakat maalesef gün içinde bloga yazı yazacak zamanım olmadı. Oturum aralarında en fazla 20 dakika var ve bu sürede bir sonraki salona koşmak ile bir şeyler içmek arasında pek zaman kalmıyor diyebilirim. Diğer yandan tabi 16 saatlik uçak yolculuğunu da katarsak halimi tahmin edebilirsiniz :) Neyse, konu ben değilim, konu MIX'in 1. Günü...

Scott Guthrie sahnede!
Scott Guthrie sahnede!

ASP.NET MVC 1.0 Yayınlandı

MVC yapısının ASP.NET'e kazandıracakları çok fazla. Bu konuda yakınlarda yoğun bir bilgi aktarımına başlıyor olacağım ayrıca :) çünkü her yeni şeye olduğu gibi bir anda MVC'ye de ilginç bir ilgi/alaka gösterimi olacaktır ve bu çerçevede etrafta "her şeyi MVC" ile yapmaya çalışanlar :) veya "MVC yaramaz" diyenler olacaktır. MVC'nin web yazılım geliştirme mimarileri arasında bir yeri var ve bu konu maalesef bu yazımızın konusu değil :) İsteyenler hemen ASP.NET MVC 1.0'ı aşağıda adresten bilgisayarlarına indirebilirler.

http://www.asp.net/mvc/

Virtual Earth Silverlight Map Control CTP oldu!

Virtual Earth'ün Silverlight ile beraber kullanımı özellikle kullanıcı deneyimi açısından çığır açıyor. Bu çerçevede yazılımcıların işini kolaylaştırma amacı ile hazırlanan Map Control'ün CTP sürümü yayınlandı. Aşağıdaki adresten ulaşabilirsiniz ;)

http://connect.microsoft.com/silverlightmapcontrolctp

Silverlight 3 Beta

Silverlight ile ilgili o kadar çok gelişme var ki! Kesin bazılarını yazmayı unutacağım. Fakat önümüzdeki dönemde Silverlight 3 ile ilgili bolca içerik paylaşacağımdan emin olabilirsiniz. Aklımda kalan ana noktalara bakmak gerekirse ilk defa Microsoft Silverlight'ın dağıtımı ile ilgili sayısal bilgi verdi diyebilirim.... 350 milyon kullanıcıda Silverlight yüklü! Diğer yandan Silverlight 3 ile ilgili özelliklere bakarsak;

  • Multitouch desteği geliyor. (Sadece Windows 7'de)
  • MPEG, AAC, H.264 desteği geliyor. İsterseniz kendi codec mekanizmanısı da yazabiliyorsunuz.
  • IIS Media Services üzerinden Smooth Streaming'in Live sürümü geliyor. Artık canlı yayında da bant genişliği yayın esnasında dinamik olarak değiştirilebiliyor.
  • İş uygulamaları için Data kontrolleri geliyor...
  • .NET RIA Services ile Silverlight ve ASP.NET arasındaki veri bağlantısı kolaylaştırılıyor.
  • Offline çalışma desteği geliyor. Artık herhangi bir Silverlight uygulamasını Desktop'a normal program gibi alabiliyorsunuz. Mac'te de aynı şekilde çalışıyor. Kendini otomatik update ediyor vs vs :)
  • SaveFileDialog ve bir çok yeni kontrol geliyor; WrapPanel, DockPanel, ViewBox, AutoCompleteBox, TreeView, Label...
  • Lokalde Assembly önbellekleme desteği geliyor.
  • Easing kütüphanaleri ve text animasyonları geliyor.
  • Pixel Shader efektleri geliyor; DropShadow vs... Kendi efektinizi yazabiliyorsunuz.
  • Perspective 3D!
  • GPU destekli video oynatma!
  • DeepZoom projelerinde GPU kullanımı!
  • Network bağlantısını algılayabilme.

Silverlight 3 Beta ve Visual Studio Tools paketini aşağıdaki adresten indirebilirsiniz. Fakat unutmayın, söz konusu paket Beta! ve bu Beta paketini yüklemeniz halinde yüklediğiniz makinede artık Silverlight 2 projeleri geliştiremiyorsunuz! Silverlight 3'ün şu anda sadece "Developer Runtime"ı var. Normal Runtime yok! Zaten Go-Live lisansı da olmadığı için uygulamalarınızı yayına almanız yasak.

Silverlight 3 Beta Tools
Silverlight 3 Beta SDK 

En büyük sürprizi sona sakladım! Tüm bu yeni özelliklerle beraber Silverlight 3 Runtime paketinin büyüklüğü sizce ne oldu? :) Büyümüştür değil mi? :) Hayır! Hattta 40KB azalmış durumda :)

 Expression Blend 3 Beta

Silverlight 3.0 ile beraber Expression Blend'in 3. sürümü geliyor. Expression Blend gerçekten çok kuvvetleniyor. Dikkatimi çekenler arasında belki de en önemlisi doğrudan Photoshop ve Illustrator Import komutları. Tüm bu harici dosyaları katmanları ile beraber Blend'e alabiliyoruz. Başka neler mi var?

  • Source Control - TFS desteği.
  • XAML, VB ve C#'da Intellisense
  • Behavior - Tasarımcılara kolay işlevsellikler yaratabilme olanağı.
  • SketchFlow - Uygulamaların ön tasarımı ve planına dair altyapı :) Bu apayrı ve detaylı bir konu.

http://www.microsoft.com/expression/blendpreview

Web Platform Installer

Microsoft'un uzun süre önce yayına aldığı WPI (Web Platform Installer) sisteminin amacı Web uygulaması geliştirmek isteyenler onlarca yerden onlarca şeyi bulup yüklemeleri gerek kalmadan her şeyi merkezi bir yükleme haline getirmek. Örneğin bugün en basit sistemde bile VS 2008, SP1, Silverlight Tools, MVC şeklinde devam eden bir çok yükleme gerekebiliyor. WPI'ın ikinci sürümünde sizleri şaşırtabileceğini düşündüğüm noktalar ise PHP yüklemeleri :) WPI içerisinde isterseniz doğrudan PHP ile yazılım geliştireceğiniz ortamları da geliştirebiliyorsunuz. Biliyorsunuz Expression Web 2 zaten PHP destekliyordu.

WPI ile beraber bir de Application Gallery denilen bir sistem açılıyor ve burada Open Source bazı projeler otomatik yüklenebilir şekilde sunuluyor. Örneğin BlogEngine, DasBlog, DotNetnuke bunlardan bazıları. Ayrıca WordPress de pakete dahil :) Daha detaylı bilgi ve download için aşağıdaki adresi deneyebilirsiniz.

http://www.microsoft.com/Web/downloads/platform.aspx

Expression Web 3

Expression Web ürünü de giderek kuvvetleniyor. Özellikle Cross-Browser site tasarımı konusuna eğilen programa SuperPreview denilen bir özellik ekleniyor. Tahmin edebileceğiniz üzere hazırladığınız tasarımı ön izleyebileceğiniz bir ortam sağlayan SuperPreview'ün güzel yanı sisteminizde yüklü tüm tarayıcıların altyapısını kullanarak ayrı ayrı tarayıcılardaki sonucu karşılaştırmalı olarak gösterirken aradaki farkları da yakalamanızı sağlaması. SuperPreview Expression Web'den ayrı olarak da indirilebilir bir download olarak sunuluyor.

http://www.microsoft.com/expression/try-it/superpreview/

1. Günün Sonu

"Adamlar yapmış" deriz ya :) aynen öyle. Gerçekten özellikle Silverlight ve Expression tarafında muhteşem yenilikler var! Sizlere bu yenilikleri paylaşmak için can atıyorum :) Yazılacak birçok yazı, verilecek bir çok seminer oluştu bile kafamda :) Hem media tarafında hem de iş/data uygulamaları tarafında Silverlight'a öyle yenilikler geliyor ki... ASP.NET artık sadece SL için bir business/data layer olarak kalacak gibi gözüküyor. Bakalım yarın neler olacak ;) 

Wednesday, March 18, 2009 6:05:51 AM (GTB Standard Time, UTC+02:00)  #    Comments [13]   Expression Blend | Expression Web | MVC | Silverlight 3.0  | 
 Tuesday, March 17, 2009

Bu hafta çok hareketli bir hafta olacak ve bundan sonraki günler de aynı şekilde :) Şu anda sizlere Atatürk Hava Limanı StarBucks'tan yazıyorum. Birazdan uçağa binip New York oradan da Las Vegas'a geçeceğim. Neden mi bunları anlatıyorum? Çünkü MIX'e gidiyorum ve ben gidiyorum demek bloğumu okuyan herkes gidiyor demektir :)

MIX Nedir?

MIX Microsoft'un web yazılımcıları ve tasarımcıların için her yıl bir defa düzenlediği en büyük konferanstır. Toplam üç gün süren MIX boyunca her zaman yeni ürünler yayınlanmış, tanıtılmıştır. Örneğin MIX'in geçmişine bakarsak 2007 yılında Silverlight (WPF/E) adı ile karşımıza çıktı :) MIX 2008'de Silverlight 2.0 Beta geldi. MIX 2009'da da da da da :) ....!!!! :) Silverlight 3 Beta!

MIX 2009'da daha birçok güzel yenilik gelecek, bunlardan biri de Internet Explorer 8.

Eh biz gelemiyoruz!

Siz gelemiyorsanız ben MIX'i size getireceğim :) İlk olarak arada hatırlatmadan geçmiyim, 11-12 Nisan'da INETA NEXT etkinliğimiz olacak. Bu konuda ayrıca duyurular yapacağız MIX sonrasında :) Ama şimdiden kaydolabilirsiniz. MIX içeriğini aynı kalitede İstanbul'da taşıyacağız.

Diğer yandan "Ben MIX'i size getireceğim" derken de şaka yapmadım :) MIX boyunca tüm gelişmeleri her gün bloğumdan sizlerle paylaşacağım. Ayrıca Twitter üzerinden de en güncel yenilikleri takip edebilirsiniz.

Önümüzde yine çok hareketli bir dönem var ;) Süper!

Tuesday, March 17, 2009 11:08:28 AM (GTB Standard Time, UTC+02:00)  #    Comments [9]   IE 8.0 | Silverlight 3.0  | 
Copyright © 2010 Daron Yöndem. Tüm hakları saklıdır.