Makale Özeti

Tarayıcı içerisinde de olsa istemci tarafında çalışan uygulamalarda adres barının değişmemesi sorunundan tutun tarayıcı geçmişinin tutulamamasına kadar birçok sorunu çözen Navigation API'yı inceliyoruz.

Makale

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