22.05.2012

Silverlight 2.0 harici Class Libary yapılarının asenkron kullanımı


Silverlight projeleriniz büyüdükçe projenin bazı bölümlerini sonradan istemci tarafına aktarmayı daha uygun bir seçenek olarak görebilirsiniz. Bu gibi durumlarda acaba ayrı bir XAP dosyası yapsak da onu haricen istemciye yüklesek diye düşünürseniz maalesef söz konusu XAP dosyasını kendi kodlarınız ile ZIP şeklinde açmanız ve içerisindeki Manifest.xml'i yine kendi kodunuz ile okuyup tek tek DLL'leri yüklemeniz gerekecektir. Bu konuda detaylı bir makaleye buradan ulaşabilirsiniz.
Bu zorluklarla uğraşmadan hızlı bir şekilde belki de sadece bir UserControl'ü haricen sonradan yüklemek istiyorsanız aslında çok daha pratik ve hızlı bir yöntem de kullanılabilir. Bu yönteme sadece UserControl'ler değil harici olarak yazılan sınıflar da dahil. Gelin daha fazla teorik konuşma yerine bir örnek üzerinden ilerleyelim.
Haricen yüklenecek içeriği hazırlayalım....
İlk olarak ana Silverlight uygulamamıza sonradan yüklenecek olan içeriği hazırlayalım. Bunun için Visual Studio içerisinde "File / New Project" dedikten sonra "Silverlight" seçeneği altındaki "Silverlight Class Library" proje tipini seçiyoruz. Bu proje tipinde doğrudan tüm proje içeriği bir DLL içerisine konacak fakat bu DLL ayrıca bir XAP dosyası içerisinde sıkıştırılmayacak. Böylece biz de Silverlight ile istemci tarafında bir XAP dosyası açmak veya Manifest ile uğraşmak zorunda kalmayacağız.
Normal şartlarda Silverlight Class Library projesi yarattığınızda proje içerisinde sadece bir CS veya VB dosyası görebilirsiniz. Oysa bu projelere de isterseniz XAML dosyaları ile beraber UserControl'ler eklenebilir. Projenize sağ tuş tıklayarak Solution Explorer içerisinden "Add New Item" demeniz ve gelen seçeneklerden de "Silverlight User Control"ü seçmeniz yeterli olacaktır. Artık isterseniz bu projeyi Blend içerisinde de açıp normal bir Silverlight projesindeki gibi animasyonlar vs kullanabilirsiniz.
Örnek olarak projemize bir resim dosyası ekleyerek UserControl'ümüz içerisinde de onu gösterebilir. Unutmayın ki resim dosyasını projeye "Add Existing Item" diyerek eklerseniz artık bu resim de DLL'inizin içerisine dahil edilecektir.
[XAML]
<UserControl x:Class="SilverlightClassLibrary1.SilverlightControl1"
   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 Margin="71,47,97,122" Source="Forest.jpg"/>
    </Grid>
</UserControl>
Yukarıdaki şekli ile UserControl'ümüz hazır olduktan sonra projemizi Build ederek DLL'imizi yaratmış oluyoruz. Bu DLL'i bir sonraki adımda yaratacağımız Silverlight projesinin XAP dosyası ile aynı konuma koyabilirsiniz. Silverlight projemiz içerisinden bu DLL'i istemciye asenkron olarak download ederek sahneye DLL içerisindeki UserControl'ü yükleyeceğiz.
Gelelim Silverlight projemize...
Tertemiz bir Silverlight projesi yarattıktan sonra proje ile beraber gelen ASP.NET sitesi içerisinde ClientBin klasörüne bir önceki adımda yarattığımız DLL dosyasını kopyalayalım. Böylece projeyi Build ettiğimiz aynı konuma otomatik olarak kopyalanacak olan XAP dosyası üzerinden DLL'e de rahatlıkla ulaşabiliriz.
Yeni Silverlight projemizin ana Page.XAML dosyasına bir Button ve bir de Canvas ekleyelim. Böylece düğmeye basıldığında harici DLL'i yükleyecek ve DLL içerisindeki UserControl'ümüzü de Canvas içerisine yerleştireceğiz.
[XAML]
<UserControl x:Class="SilverlightApplication5.Page"
   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 x:Name="btnTikla" Height="19" HorizontalAlignment="Right" Margin="0,0,18,17" VerticalAlignment="Bottom" Width="89"Content="Button"/>
        <Canvas x:Name="Icerik" Margin="13,13,18,54"/>
    </Grid>
</UserControl>
Düğmeye tıklandığı anda hemen bir WebClient yaratarak download işlemimizi başlatalım.
[VB]
Private BirAssembly As System.Reflection.Assembly

Public Sub New()
    InitializeComponent()
    AddHandler Me.btnTikla.Click, AddressOf btnTikla_Click
End Sub

Private Sub btnTikla_Click(ByVal sender As ObjectByVal e As RoutedEventArgs)
    Dim Yukleyici As New WebClient()
    AddHandler Yukleyici.OpenReadCompleted, AddressOf Yukleyici_OpenReadCompleted
    Dim Yol As String = System.Windows.Application.Current.Host.Source.AbsoluteUri.Replace("SilverlightApplication5.xap","SilverlightClassLibrary1.dll")
    Yukleyici.OpenReadAsync(New Uri(Yol, UriKind.Absolute))
End Sub
[C#]
        System.Reflection.Assembly BirAssembly;

        public Page()
        {
            InitializeComponent();
            this.btnTikla.Click += new RoutedEventHandler(btnTikla_Click);
        }

        void btnTikla_Click(object sender, RoutedEventArgs e)
        {
            WebClient Yukleyici = new WebClient();
            Yukleyici.OpenReadCompleted += new OpenReadCompletedEventHandler(Yukleyici_OpenReadCompleted);
            string Yol = System.Windows.Application.Current.Host.Source.AbsoluteUri.Replace("SilverlightApplication5.xap","SilverlightClassLibrary1.dll");
            Yukleyici.OpenReadAsync(new Uri(Yol, UriKind.Absolute));
        }
Yukarıdaki kodu dikkatli incelemek gerekirse ilk adımda en üstteki Assembly tipindeki BirAssembly adındaki değişkenimizi açıklamak gerekecek. Kodumuz sunucudan bir DLL indirecek ve içindeki UserControl'ü sahneye koyacak. Aslında DLL'i indirdikten sonra içerisinden UserControl1 sınıfından bir instance alarak sahneye koyacağız. Eğer bu işlemi yaptıktan sonra başka instance'lara da ihtiyacımız olursa tekrar DLL'i indirmemek için eldeki Assembly'yi bir değişken olarak tutmak daha mantıklı olacaktır. O nedenle en üstteki BirAssembly değişkenimiz şimdiden yerini almış durumda.
Button'umuzun Click koduna baktığımızda bir WebClient yarattığımızı ve OpenReadCompleted event listener'ını da başka bir koda bağladığımızı görebilirsiniz. Sunucudan bir dosya indireceği ve indirme işlemi bittiğinde de başka işler yapacağız. O nedenle bu event'ları yakalayabiliyor olmak çok önemli. İsteyenler WebClient'ın DownloadProgressChanged event'ını da yakalayarak download durumu ile ilgili yüzde üzerinden ne kadarının indirildiğine dair bilgileri de ekranda gösterebilirler.
Sunucudan indireceğimiz dosyanın tam yolunu verebilmek için şu anki XAP dosyasının tam yolunu alıp sadece dosya adını değiştiriyoruz. Bizim örneğimizde saten her şeyin yeri ve dosya adları belli olduğu için herhangi bir sorun olmayacaktır.
Son olarak OpenReadAsync metoduna da indirilecek olan dosyanın yolunu verip download işlemini başlatıyoruz. Peki ya bu işler bitince çalışacak olan Yukleyici_OpenReadCompleted metodunda neler yapacağız?
[VB]
Private Sub Yukleyici_OpenReadCompleted(ByVal sender As ObjectByVal e As OpenReadCompletedEventArgs)
    Dim GelenAssembly As New AssemblyPart()
    BirAssembly = GelenAssembly.Load(e.Result)
    Dim Kontrol As UserControl = DirectCast(BirAssembly.CreateInstance("SilverlightClassLibrary1.SilverlightControl1"), UserControl)
    Me.Icerik.Children.Add(Kontrol)
End Sub
[C#]
            AssemblyPart GelenAssembly = new AssemblyPart();
            BirAssembly = GelenAssembly.Load(e.Result);
            UserControl Kontrol = (UserControl)BirAssembly.CreateInstance("SilverlightClassLibrary1.SilverlightControl1");
            this.Icerik.Children.Add(Kontrol);
Download işlemi bittiği anda bir AssemblyPart değişkeni yaratarak onun da Load metodunu kullanıyoruz. Load metoduna e.result ile aslındaYukleyici_OpenReadCompleted event-listener'ına gelen argüman üzerindeki datayı almış oluyoruz. Yani özünde sunucudan indirdiğimiz DLL'inStream'i e.result içerisinde saklanıyor ve biz de bu AssemblyStream'i doğrudan bir AssemblyPart üzerinden Load ederek Assemblytipindeki BirAssembly değişkenimize yüklüyoruz. Hatırlarsanız zaten bu değişkenimiz de global anlamda sürekli hafızada tuttuğumuz bir değişkendi. Bir sonraki adımda bir UserControl değişkeni tanımlayarak bunu da Assembly'miz içerisinde SilverlightControl1'e eşitlememiz gerekiyor.
Assembly üzerinden CreateInstance metodu bizden yaratılacak nesnenin TypeName'ini istiyor. Silverlight Class Library projesinin içerisindeki UserControlümüzün tipinin adını full path olarak veriyoruz. Bunu zaten UserControl'ün XAML dosyasının en üstünden de bulabilirsiniz. Artık elimizdekiKontrol değişkeni yine elimizdeki BirAssembly'nin içerisinden SilverlightControl1'in bir instance'ıdır. Herhangi bir UserControl gibi bu da alıp sahnede istediğimiz yere yerleştirebiliriz.

Hiç yorum yok:

Yorum Gönder