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.