Makale Özeti

Bu yazımızda Silverlight 4 Beta ile beraber gelen farenin sağ tuşu ile ilgili desteğe göz atarken örnek bir de ContextMenü kontrolünü inceliyoruz.

Makale

Silverlight 3 zamanlarında gelen en önemli isteklerden biri farenin sağ tuşu ile ilgili eventları yakalayabilmekti. Biliyorsunuz normal şartlarda herhangi bir Silverlight uygulamasına sağ tuş ile tıklarsanız karşınıza "Silverlight" diye bir menü seçeneği geliyor ve burada da Silverlight Runtime ile ilgili ayarlara ulaşılabiliyor. Uzun bir süre sağ tuş implementasyonunun pek de mümkün olmadığı ve güvenlik nedeni ile Microsoft'un bu gibi bir şeye izin vermeyeceği konusunda yorumlar internette gezindi. İtiraf etmek gerekirse ben de konsept olarak pek olası bir çözüm öngöremiyordum.

Silverlight 4'te fareye sağ tuş desteği!

Oysa bir de baktık ki Silverlight 4 ile (Beta) farenin sağ tuşuna dair eventleri de ayrıca yakalayabiliyoruz. Bu destek özellikle iş uygulamalarında çok anlamlı bir boşluğu dolduruyor. Kullanıcılara hali hazırda web ortamında bir "thinclient" deneyimi sunan ve sanki windows ortamındaki programları kullanıyormuşcasına zengin deneyimler sağlayabilen Silverlight'ın aynı hissiyatı devam ettirebilmesi adına farenin sağ tıklamalarına da uygun tepkileri verebilmesi çok önemliydi. Bu açığın kapatılıyor olduğunu görmek çok sevindirici.

Peki nasıl?

Aslında konu epey basit. Artık Silverlight içerisindeki tüm kontrollerin MouseLeftButtonDown ve MouseLeftButtonUp gibi birer de MouseRightButtonDown ve MouseRightButtonUp eventları bulunuyor. Söz konusu eventları tarayıcı ve platform bağımsız olarak yakalayıp istediğiniz işlevselliği sunabilirsiniz.

Silverlight 4'te sağ tuş desteği gelmesine rağmen varsayılan ayarlarla hala sağ tuş ile tıkladığınızda eksi klasik Silverlight menüsü gelecektir. Bu menünün gelmesini engellemek için tabi ki kendi implementasyonunuzu yapmanız gerekiyor. Eğer bu menünün gelmesini istemiyorsanız hemen herhangi bir kontrolün MouseLeftButtonDown eventını yakalayarak event listener'a gelen argüman üzerindeki Handled Property'sini kullanabilirsiniz.

[XAML]

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

   mc:Ignorable="d"

   d:DesignHeight="300" d:DesignWidth="400">

 

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

        <TextBox x:Name="txtMetin" Height="100" />

        <Grid x:Name="ContextMenu" Visibility="Collapsed" HorizontalAlignment="Left"

             VerticalAlignment="Top" Height="50" Width="150" Background="Red">

            <TextBlock Text="Başardın!" />

        </Grid>

    </Grid>

</UserControl>

Yukarıdaki XAML kodunda basit bir TextBox ve bir de Visibility özelliği Collapsed olarak ayarlanmaış Grid görüyorsunuz. Amacımız TextBox'a sağ tuş ile tıklandığında bir ContextMenu kıvamında Grid'imizi kullanıcıya göstermek. Grid içerisinde ben deneme amacı ile bir TextBlock koydum fakat siz hem bu Grid'in içerisinde duruma göre programatik olarak doldurabilir hem de içerisine düğmeler koyup düğmelerin click eventlarını yakalayabilirsiniz. Sistemi geliştirmek size kalmış.

[VB]

    Private Sub txtMetin_MouseRightButtonDown(ByVal sender As Object, ByVal e As System.Windows.Input.MouseButtonEventArgs) Handles txtMetin.MouseRightButtonDown

        e.Handled = True

    End Sub

İlk olarak TextBox kontrolümüzün MouseRightButtonDown eventını yakalayarak burada Handled özelliğine True değerini veriyoruz. Böylece TextBox'ın üzerine sağ tuş ile tıklandığı anda söz konusu senaryoyla bizim ilgilendiğimizi sisteme belirtmiş oluyoruz ve artık Silverlight Runtime kendi menüsünü göstermiyor. Normal şartlarda Context menülerin gösterimi farenin sağ tuşu kaldırıldığında yapılır. Yani tuşa bastığınızda değil tuştan parmağınızı çektiğinizde context menü gösteriliyor. Biz bu event ile tuşa basıldığı anda "Hop! Ben halledeceğim bu işi" :) şeklinde bir mesaj ile Silverlight Runtime'ı pinglemiş oluyoruz ve tabi ki MouseRightButtonUp durumunu yakalayıp kendi menümüzü göstermek de bize kalıyor.

[VB]

    Private Sub txtMetin_MouseRightButtonUp(ByVal sender As Object, ByVal e As System.Windows.Input.MouseButtonEventArgs) Handles txtMetin.MouseRightButtonUp

        ContextMenu.Margin = New Thickness(e.GetPosition(Me).X, e.GetPosition(Me).Y, 0, 0)

        ContextMenu.Visibility = Windows.Visibility.Visible

    End Sub

Sıra geldi olası bir ContextMenu yapısını kullanıcıya göstermeye. XAML kısmından hatırlarsanız bizim elimizde adı ContextMenu olan bir Grid vardı. Amacımız bu Grid'i farenin sağ tuşu ile tıklandığında tıklanan noktada göstermek. Bu nedenle yine TextBox'ın MouseRightButtonUp eventını yakalayarak bu elimizdeki ContextMenu'nün Margin'ini ayarlıyoruz. ContextMenu işlevselliği görecek Grid'imiz bir başka Grid içerisinde yer aldığı için pozisyonunu değiştirebilmek adına Margin vermemiz şart. Dikkat etmeniz gereken nokta Grid'in XAML içerisinde pozisyonlandırma olarak sol üste yaslanmış olması. O nedenle sol üst köşeden mesafalelerini vermemiz pozisyonlandırma için yeterli olacaktır. Yine event listener'a gelen argüman üzerinden GetPosition metodunu kullanarak farenin tıklandığı yerin X ve Y koordinatlarını alarak Margin olarak Grid'imize veriyoruz. Böylece ContextMenu'müz artık istediğimiz yere geldi ve gösterime hazır. Bu durumda Visibility'sini de Visible yaparak kullanıcıya gösterebiliriz.

[VB]

    Private Sub MainPage_MouseLeftButtonDown(ByVal sender As Object, ByVal e As System.Windows.Input.MouseButtonEventArgs) Handles Me.MouseLeftButtonDown

        ContextMenu.Visibility = Windows.Visibility.Collapsed

    End Sub

Unutmadan! Uygulama içerisinde bir yerlere farenin sol tuşu ile tıklandığında ContextMenu'üyü tekrar görünmez yapmayı atlamayın yoksa context menü sürekli gözükecektir. Bunun için basit bir şekilde root elementin MouseLeftButtonDown özelliğini yakalayıp orada Grid'imizin Visibility'sini Collapsed yapabilirsiniz.

Daha kolay yolu yok mu ContextMenü meselesinin?

Farkındayım :) Kendi kendinize "Ya tamam da bir ContextMenü için bu kadar uğraşmak doğru mu? Yok ContextMenü kontrolü?" şeklinde sordunuz :) Maalesef yok! Yani en azından Silverlight 4 Beta ile şu anda gelmiyor. Ama güzel bir haberim var. Silverlight Program Manager'lardan Jesse Bishop'un yazmış olduğu bir kontrol var. Söz konusu kontrolü kullanarak rahatlıkla uygulamalarınıza ContextMenu'ler ekleyebilirsiniz. Gelin basit bir şekilde bu implementasyonun nasıl yapıldığına bir göz atalım.

İlk olarak Jesse Bishop'un yazmış olduğu kontrolün kaynak kodlarını sitesinden indirerek compile etmeniz gerek veya üşeniyorsanız ben sizin için compile ettim :) Aşağıdaki linkten doğrudan Assembly'leri indirebilirsiniz.

Silverlight 4 Beta için ContextMenu Kontrolleri - 21112009_1.zip (19,11 KB)

Referans alma işlemini tamamladıktan sonra Assembly içerisindeki kontrolleri kullanabilmek adına Assembly'yi XML NameSpace ile XAML tarafına import etmemiz gerek.

[XAML]

<UserControl x:Class="SilverlightApplication13.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:context="clr-namespace:ContextMenuControls;assembly=ContextMenuControls"

   mc:Ignorable="d"

   d:DesignHeight="300" d:DesignWidth="400">

Import işlemi de tamamlandığına göre artık bu Assembly içerisindeki tüm kontrolleri projemizde kullanabiliriz. Bir önceki örneğimizdeki TextBox ile devam edelim. Amacımız bu TextBox'a yine bir ContextMenü atamak.

[XAML]

        <TextBox x:Name="txtMetin" Height="100" >

            <context:ContextMenu.ContextMenu>

                <context:ContextMenu>

                    <context:MenuItem Text="Hede" Tag="1" Click="MenuItem_Click" />

                    <context:MenuItem Text="Hödö" Tag="2" Click="MenuItem_Click" />

                    <context:MenuItem Text="Bödö" Tag="3" Click="MenuItem_Click" />

                </context:ContextMenu>

            </context:ContextMenu.ContextMenu>

        </TextBox>

Gördüğünüz gibi olay epeyce basit aslında. TextBox için bir ContextMenu atadıktan sonra içerisine de istediğimiz kadar MenuItem koyabiliyoruz. Ben örnek olarak tüm MenuItem'ları aynı event-listener'a bağladım. Söz konusu event listener içerisinde de gelen MenuItem'ın Tag'ına göre senaryolarınızı ayrıştırabilirsiniz. Aslında söz konusu kontroller Silverlight 4 ile beraber gelen Commanding yapısına da destek veriyor fakat şimdilik daha commanding kısmı ile ilgili makale yazmadığım için konuyu oralara genişletmiyorum.

[VB]

    Private Sub MenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

        MessageBox.Show(CType(sender, ContextMenuControls.MenuItem).Tag)

    End Sub

Eğer ContextMenu'leri kod ile yaratıp kontrollere atamak isterseniz tabi ki bu da mümkün. Bunun için örneğimizdeki TextBox'a ait ContextMenu Assembly'sinden gelen bir attached property'yi set ederek ilerleyebiliriz.

[VB]

        Dim CMenu As New ContextMenuControls.ContextMenu

        Dim Item As New ContextMenuControls.MenuItem

        Item.Text = "Gel Tıkla"

        AddHandler Item.Click, Sub()

                                   MessageBox.Show("ok")

                               End Sub

        CMenu.Items.Add(Item)

        ContextMenuControls.ContextMenu.SetContextMenu(txtMetin, CMenu)

Yukarıdaki kod içerisinde bir ContextMenu yaratıp içerisine de bir MenuItem yerleştiriyoruz. Basit bir şekilde şimdilik MenuItem'a tıklandığında bir MessageBox gösteriyoruz. Önemli olan en sonda ContextMenu sınıfı üzerinden SetContextMenu ile TextBox'a elimizdeki menüyü ataçlamamız. Böylece herşey tıkırında çalışacaktır.

Buradan bu güzel kontrol için Jesse'ye teşekkür ediyorum :) Umarım yakında Silverlight Toolkit'te görürüz bu kontrolü.

Hepinize kolay gelsin.