Makale Özeti

Bu yazımızda Silverlight 2.0 uygulamalarında harici bir XAP (Silverlight 2.0) uygulamasını dinamik olarak istemci tarafına yükleyerek kullanımını inceliyoruz.

Makale

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.

Daron Yöndem
MVP, MCT, MCPD, MCITP, MCTS
MCSD, MCAD, MCDBA, MCP, ACP, ICSD
Blog: http://daron.yondem.com
Sorularınız için: http://daron.yondem.com/tr/sorusor