Silverlight 2.0 içerisinde harici dinamik XAP (Silverlight 2.0) uygulamalarının asenkron yüklenmesi.

0 dakikada yazıldı

7223 defa okundu

Düzenle

Dinamik olarak Silverlight 2.0 uygulamalarında farklı XAP dosyalarını
istemci tarafına yükleyerek ana XAP içerisinde gösterebiliyor olmak
kullanıcı deneyimi açısından büyük önem taşıyor. İçerisinde yoğun
animasyonların ve belki de harici kontrollerin bulunduğu bir arayüzü
Silverlight uygulaması istemciye ilk gönderildiğinde topluca göndermek
doğru olmayabilir. Bu durum hem ana Silverlight uygulamasının istemcide
açılma süresini uzatacak hem de belki kullanıcının hiç kullanmayacağı
uygulama bölümlerinin gereksiz yere kullanıcıya gönderilmesine neden
olacaktır. Tüm bu nedenlerle dinamik olarak harici XAP dosyalarını, yani
Silverlight uygulamalarını başka bir Silverlight uygulaması içerisine
yükleyerek çalıştırabilmek büyük önem taşıyor. Sonraki örneğimizde
dinamik XAP yükleme işlemini nasıl yapabileceğimizi inceleyeceğiz.

Projemizi hazırlayalım...

İlk olarak Visual Studio içerisinde yeni bir Silverlight projesi
yaratarak beraberinde bir de ASP.NET uygulaması yaratılması için ilk
açılışta gelen uyarı kutusunda gerekli işaretlemeyi yapalım. Bu standart
prosesi atlattıktan sonra artık Visual Studio içerisindeki
Solution'ımıza yeni bir Silverlight projesi daha eklememiz gerekiyor.
Toplam olarak Solution içerisinde bir ASP.NET ve iki Silverlight projesi
bulunacak.

Solution'a Solution Explorer içerisinde sağ tuş ile tıklayarak gelen
menüden "Add / New Project" yeni bir Silverlight projesi seçerek
ekleyebilirsiniz. Ekleme işlemini yaparken size aşağıdaki şekilde bir
pencere ile eklenen Silverlight uygulamasının Solution içerisinde bir
ASP.NET sitesi ile ilişkilendirip ilişkilendirilmeyeceği sorulacaktır.
Bu noktada var olan uygulama ile yeni Silverlight'ımızı
ilişkilendirmemiz gerekiyor, böylece Visual Studio içerisinde F5'e
bastığımızda bu Silverlight uygulaması da compile edilerek ASP.NET
sitesi içerisine kopyalanacaktır.

İkinci Silverlight uygulamamızı Solution içerisine eklerken...
İkinci Silverlight uygulamamızı Solution içerisine eklerken...

Sonradan yüklenecek Silverlight uygulamamızı hazırlayalım...

İlk olarak uzaktaki (remote) Silverlight uygulamamızı hazırlayalım.
Örnek olması ve konudan çok uzaklaşmamak adına uygulamamız çok basit
olacak fakat unutmayın ki uzaktaki Silverlight uygulaması video gösteren
veya belki de harici kontroller (DataGrid) kullanan bir uygulama da
olabilirdi. Böyle bir durumda karşıdan yüklenecek uygulamamız çok daha
büyük bir boyuta sahip olurdu.

Biz şimdilik uygulama içerisinde toplama işlemi yapan iki TextBox ve bir
de Button bulunsun. Böylece ana uygulamada toplama işlemi yapılacağı
zaman bu harici uygulamayı yükleyerek kullanıcının istediğini yapmasını
sağlayalım.

[XAML]

<UserControl
x
:Class="SilverlightRemote.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">

        <TextBox
Height
="40" HorizontalAlignment="Left"
Margin
="40,40,0,0" VerticalAlignment="Top"
Width
="120" Text="TextBox"
TextWrapping
="Wrap" x:Name="Kutu1"/>

        <TextBox
Height
="40" HorizontalAlignment="Right"
Margin
="0,40,40,0" VerticalAlignment="Top"
Width
="120" Text="TextBox"
TextWrapping
="Wrap" x:Name="Kutu2"/>

        <Button
HorizontalAlignment
="Stretch"
Margin
="160,120,160,140" VerticalAlignment="Stretch"
Content
="Button" x:Name="Dugme"/>

    </Grid>

</UserControl>

Uygulamamızın XAML kodu yukarıdaki şekilde sonlanıyor. Şimdi geçelim
arkaplanda çalışan ve basit bir şekilde toplama işlemi yaparak sonucu
düğmenin üzerine yazdıran koda.

[VB]

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

        Dugme.Content = CInt(Kutu1.Text) + Kutu2.Text

    End Sub

[C#]

        public Page()

        {

            InitializeComponent();

            Dugme.Click += new RoutedEventHandler(Dugme_Click);

        }

 

        void Dugme_Click(object sender, RoutedEventArgs e)

        {

            Dugme.Content = int.Parse(Kutu1.Text) + int.Parse(Kutu2.Text);

        }

Ana Silverlight uygulamamızı hazırlayalım...

Sıra geldi gerektiğinde uzaktaki Silverlight uygulamamızı yükleyerek
kullanıcının kullanmasını sağlayacak olan ana uygulamamızı geliştirmeye.
Bunun için yine basit bir örnek olarak uygulamamız içerisine bir
StackPanel yerleştirelim. Söz konusu StackPanel içerisinde şimdilik
uzaktaki uygulamamızın yüklenmesini sağlayacak olan bir düğme yer
alacak.

[XAML]

<UserControl
x
:Class="SilverlightApplication36.Page"

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

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

   Width="400"
Height
="300">

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

        <Button
Content
="TOPLAMA işlemi için
tıkla"
x:Name="Dugme"/>

    </StackPanel>

</UserControl>

Uygulamamızın tasarımı da hazır olduğuna göre artık düğmeye basıldığında
çalışacak kod kısmına geçebiliriz. Düğmemize tıklandığında bir WebClient
kullanarak sunucudan diğer uygulamanın XAP dosyasını istemci tarafına
yüklememiz gerekiyor.

[VB]

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

        Dim Yukleme As New
WebClient

        AddHandler
Yukleme.OpenReadCompleted, AddressOf
Yukleme_OpenReadCompleted

 

        Yukleme.OpenReadAsync(New
Uri("SilverlightRemote.xap",
UriKind.Relative))

    End Sub

[C#]

        public Page()

        {

            InitializeComponent();

            Dugme.Click += new RoutedEventHandler(Dugme_Click);

        }

 

        void Dugme_Click(object sender, RoutedEventArgs e)

        {

            WebClient Yukleme =
new WebClient();

            Yukleme.OpenReadCompleted += new OpenReadCompletedEventHandler(Yukleme_OpenReadCompleted);

            Yukleme.OpenReadAsync(new
Uri("SilverlightRemote.xap", UriKind.Relative));

        }

Kodumuzun ilk satırında bir WebClient yarattıktan sonra WebClient'ın
OpenReadCompleted event'ını da bir event-handler'a bağlıyoruz.
Sonrasında da OpenReadAsync metodu ile uzaktaki XAP dosyasını
istemciye indirmeye başlıyoruz. İndirme işlemi tamamlandığında
event-handler kodumuz çalışacak. Şimdi esas işlemleri yapacağımız, XAP
dosyası istemciye indiğinde çalışacak olan event-handler kodumuza
geçelim.

[VB]

Dim UygulamaManifesti As String
= New
IO.StreamReader(Application.GetResourceStream(New
Windows.Resources.StreamResourceInfo(e.Result, Nothing), New Uri("AppManifest.xaml",
UriKind.Relative)).Stream).ReadToEnd()

[C#]

string UygulamaManifesti = new System.IO.StreamReader(Application.GetResourceStream(new System.Windows.Resources.StreamResourceInfo(e.Result, null), new
Uri("AppManifest.xaml", UriKind.Relative)).Stream).ReadToEnd();

Buradaki kod ilk bakışta biraz karışık gelebilir fakat aslında çok
basit. Her Silverlight uygulaması içerisinde (XAP dosyası içerisinde)
bir AppManifest.xaml bulunur. Bu dosya içerisinde tek tek söz konusu
uygulamada kullanılan Assembly'lerin adları ve ilişkili DLL dosyalarının
adları bulunur. Bizim de hedefimiz uzaktaki uygulamada kullanılmış tüm
Assembly'leri bularak istemci tarafında belleğe yüklemek. Aslında bizim
örneğimizde tek bir Assembly olduğunu biliyoruz fakat eğer örnek farklı
olsaydı ve harici kontroller kullanılmış olsaydı doğal olarak birden çok
Assembly olacaktı. O nedenle biz daha genel geçer bir taktik kullanarak
esnek olalım.

Yukarıdaki kod içerisinde bir StreamResourceInfo nesnesine
e.Result ile indirdiğimiz XAP dosyasını aktarıyoruz. Bu
StreamResource içerisinden de Application.GetResourceStream ile
AppManifest.xaml dosyasını istiyoruz. Dosyayı aldığımızda
Stream'ini de bir StreamReader ile sonuna kadar ReadToEnd
ile okuyoruz. Böylece artık elimizde AppManifest.xaml var.

[VB]

Dim Dagitim As Deployment = CType(Markup.XamlReader.Load(UygulamaManifesti),
Deployment)

[C#]

Deployment Dagitim =
System.Windows.Markup.XamlReader.Load(UygulamaManifesti) as Deployment;

Bir sonraki adımda AppManifest.xaml içerisinde tanımlı olan
Deployment şeklini bir Deployment nesnesine eşitlememiz
gerekiyor. Bunu aslında elimizde String olarak var olan bir XAML'ı .NET
nesnesine çevirme olarak değerlendirebilirsiniz.
Markup.XamlReader.Load metodu ile elimizdeki dosyayı okutarak gelen
nesneyi de Deployment'a Cast ediyoruz. Zaten uzaktaki (remote)
uygulamamızı Compile ettiğimizde oluşan XAP dosyasının içerisine girerek
AppManifest.xaml'ına baktığımızda da aşağıdaki XAML ile karşılaşıyoruz.

[XAML] AppManifest.xaml

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

  <Deployment.Parts>

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

  </Deployment.Parts>

</Deployment>

Sıra geldi artık Deployment içerisindeki tüm Assembly'leri gezerek
hepsini istemci tarafında hafızaya yüklemeye. Tüm bu yüklemeleri
yaparken aralarından SilverlightRemote.dll adında olanı bir kenara
çekerek referansını ayrı bir değişkende tutacağız. Bunun yapmamızın
nedeni ise bu DLL içerisindeki Page sınıfından bir adet türeterek
sahneye almak zorunda olmamız. Assembly'leri hafızaya yüklesek de işin
görsel kısmını sahneye almamız lazım. Bunun detaylarına birazdan
değineceğiz, önce bir Assembly'leri tek tek yükleyelim.

[VB]

        Dim GorselAssembly As System.Reflection.Assembly = Nothing

 

        For Each BirAssembly As AssemblyPart In Dagitim.Parts

            Dim AssemblyDLLAdi As String
= BirAssembly.Source

            Dim StreamBilgi As Windows.Resources.StreamResourceInfo =
Application.GetResourceStream(New
Windows.Resources.StreamResourceInfo(e.Result, "application/binary"), New Uri(AssemblyDLLAdi, UriKind.Relative))

 

            If AssemblyDLLAdi = "SilverlightRemote.dll" Then

                GorselAssembly = BirAssembly.Load(StreamBilgi.Stream)

            Else

                BirAssembly.Load(StreamBilgi.Stream)

            End If

        Next

[C#]

            System.Reflection.Assembly GorselAssembly = null;

 

            foreach (AssemblyPart BirAssembly in Dagitim.Parts)

            {

                string AssemblyDLLAdi
= BirAssembly.Source;

                System.Windows.Resources.StreamResourceInfo StreamBilgi = Application.GetResourceStream(new System.Windows.Resources.StreamResourceInfo(e.Result, "application/binary"), new Uri(AssemblyDLLAdi, UriKind.Relative));

 

                if (AssemblyDLLAdi ==
"SilverlightRemote.dll")

                {

                    GorselAssembly =
BirAssembly.Load(StreamBilgi.Stream);

                }

                else

                {

                    BirAssembly.Load(StreamBilgi.Stream);

                }

            }

Satır satır yukarıdaki kodumuzu inceleyelim. İlk satırda Assembly
tipinde bir değişken yaratıyoruz. Bu değişken gerektiğinde bizim
uzaktaki uygulama içerisinde görsel arayüzü tanımlayan Page
sınıfının bulunduğu DLL'in referansını taşıyacak. Sonrasında hemen
Deployment nesnemiz olan Dagitim'ın Parts dizisinde bir
ForEach döngüsü başlatarak tüm Assembly'leri gezmeye başlıyoruz. Her
Assembly'nin Source'unu yani tam DLL dosyasının adını alarak bu
dosyaları tek tek StreamBilgi adındaki değişkenimize yüklüyoruz. XAP
dosyası içerisinden DLL'leri alırken aynı AppManifest.Xaml
alırkenki tekniği kullanıyoruz. Son olarak hemen o an için üzerinde
çalıştığımız Assembly'nin görsel arayüzümüzün bulunduğu Assembly olup
olmadığını kontrole diyoruz. Bunun için doğrudan Assembly'e ait DLL
adını karşılaştırıyoruz.

Burada hemen bir dipnot geçelim. Bir Silverlight uygulaması içerisinde
harici kontrolleri içeren veya farklı sınıfları ve kodları barındıran
DLL'ler bulunabilir. Bunların haricinde bir de her XAML (görsel) dosya
arkasındaki UserControl tipindeki sınıflar vardır. Biz yukarıdaki
kodumuzda içerisinde UserControl bulunan DLL'i ayırarak bir kenara
referansını kaydediyoruz çünkü bu UserControl'ları sahneye almamız
gerekiyor. Oysa diğer Assembly'leri kullanabilmek için sadece belleğe
yüklememiz yeterli.

IF koşulumuz içerisinde gerekli kontrolleri de yaptıktan sonra normal
Assembly'leri Load metodu ile Stream'ini vererek belleğe yüklerken
içerisinde UserControl'lerimizin bulunduğu Assembly'i yüklerken
GorselAsssembly değişkenine de bir referansını alıyoruz.

[VB]

        Dim Arayuz As UIElement = CType(GorselAssembly.CreateInstance("SilverlightRemote.Page"), UIElement)

        LayoutRoot.Children.Add(Arayuz)

[C#]

            UIElement Arayuz =
GorselAssembly.CreateInstance("SilverlightRemote.Page") as UIElement;

            this.LayoutRoot.Children.Add(Arayuz);

Sıra geldi GorselAssembly içerisindeki ana UserControl'ümüz olan
Page sınıfından bir instance olarak sahneye yerleştirmeye. Bunun
için Assembly'nin CreateInstance metoduna sınıfımızın tam yolunu
vererek bir kopyasını alıyor ve UIElement tipine cast ediyoruz.
Sonrasında da elimizdeki nesneyi uygulamamızdaki StackPanel içerisine
ekliyoruz.

Artık uygulamamızı çalıştırabiliriz. İlk Silverlight uygulaması
istemciye yüklendikten sonra düğmeye bastığınızda ikinci uygulama
sunucudan alınarak içerisinde UserControl sahneye yerleştirilecektir.
Böylece rahatlıkla bir Silverlight projesini parçalar şeklinde
geliştirebilir ve uygulamanın bölümlerini gerektikçe istemci tarafına
aktarabilirsiniz.

Hepinize kolay gelsin.