Makale Özeti

Windows 7 ile beraber Multitouch desteğinin de gelmesi artık Multitouch donanımlar üzerinden farklı uygulamalar geliştirebilme olanağı tanıyor. Böylece uygulamalarımızın kullanıcılarına çok daha farklı bir deneyim yaşatabiliyoruz. Bu yazımızda WPF ile Windows7 üzerinde Multitouch programlamaya bir giriş yapacağız.

Makale

Windows 7 ile beraber gelen Multitouch desteği bugünlerde çok yeni donanımlar ile daha da hayatımızın içine giriyor. Artık birden çok dokunma noktasını destekleyen ekranlar dizüstü bilgisayarlara kadar girdi ve Windows 7'nin yardımı ile donanımdan bağımsız olarak Windows7 API'lerini kullanarak Multitouch desteğine sahip uygulamalar yazabilir durumdayız. Bu yazımızda Multitouch dünyasına bir giriş yaparak Multitouch uygulama geliştirirken Windows7 API'lerini nasıl kullanabileceğimize göz atacağız.

Windows7 ile beraber gelen Multitouch API'lerine ait managed wrapper'ları aşağıdaki adresten bilgisayarınıza indirebilirsiniz. İndirme işlemi sonrası paket içerisinden Windows7.Multitouch.dll ve Windows7.Multitouch.WPF.dll dosyalarını yeni yaratacağımız bir WPF uygulamasına referans alarak hemen Multitouch programlamaya geçebiliriz.

http://code.msdn.microsoft.com/Project/Download/FileDownload.aspx?ProjectName=WindowsTouch&DownloadId=5038

Donanım desteği var mı?

DLL'lerimizi referans aldığımıza göre uygulamamızı yazmaya başlamadan önce ilk yapmamız gereken uygulamanın çalışacağı hedef bilgisayarda Multitouch donanım var mı, yok mu sorusuna bir cevap bulmak. Belki de Multitouch donanım yoksa programımızı çalıştırmanın hiçbir anlamı kalmayacaktır o nedenle kullanıcılara uygun bir uyarı vererek programımızın Multitouch donanım gerektirdiğini söyleyebiliriz.

[VB]

    Private Sub Window1_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded

        If Not Windows7.Multitouch.TouchHandler.DigitizerCapabilities.IsMultiTouchReady Then

            MsgBox("Multitouch yok!")

            Environment.Exit(1)

        End If

    End Sub

[C#]

        public Window1()

        {

            InitializeComponent();

            this.Loaded += new RoutedEventHandler(Window1_Loaded);

        }

 

        void Window1_Loaded(object sender, RoutedEventArgs e)

        {

            if (!Windows7.Multitouch.TouchHandler.DigitizerCapabilities.IsMultiTouchReady) {

                MessageBox.Show("Multitouch yok!");

                Environment.Exit(1);

            }

        }

Hemen referans aldığımız Assembly'lerden birindeki IsMultiTouchReady propertysine göz atarak eğer geriye False dönüyorsa kullanıcıya uygun mesajı gösterip programımızı kapatıyoruz. Bu kadarını yaptıktan sonra uygulamamızın Multitouch dokunuşlarını algılayabilmesi için gerekli Stylus Event'larını da geçerli hale getirmek için aşağıdaki kodu hemen uygulamanın Multitouch kullanacak olan penceresinin Loaded event listener'ına ekliyoruz.

[VB]

        Windows7.Multitouch.WPF.Factory.EnableStylusEvents(Me)

EnableStylusEvents derken parametre olarak eventların yaratılacağı Window nesnesi isteniyor biz de kendi elimizdeki ana pencereyi kendisine aktarıyoruz. Bu noktadan sonra artık her dokunuş (Stylus) ile ilgili eventları yakalayabiliriz. Şimdilik bu örneğimizde kullanacağımız event'ın adı StylusMove olacak. Amacımız kullanıcıların uygulamamız üzerinde herhangi bir parmaklarını gezdirdikçe arkada bir iz bırakıyormuş gibi çizgi çizdirmek. Böylece kullanıcıların birden çok parmak kullanarak aynı anda birden fazla çizgi çizebilecekler.

[VB]

    Private Sub Window1_StylusMove(ByVal sender As Object, ByVal e As System.Windows.Input.StylusEventArgs) Handles Me.StylusMove

        Dim Daire As New Ellipse

        Daire.Fill = New SolidColorBrush(Colors.Red)

        Daire.Height = 10

        Daire.Width = 10

        Daire.HorizontalAlignment = Windows.HorizontalAlignment.Left

        Daire.VerticalAlignment = Windows.VerticalAlignment.Top

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

        Me.Root.Children.Add(Daire)

    End Sub

[C#]

        void Window1_StylusMove(object sender, StylusEventArgs e)

        {

            Ellipse Daire = new Ellipse();

            Daire.Fill = new SolidColorBrush(Colors.Red);

            Daire.Height = 10;

            Daire.Width = 10;

            Daire.HorizontalAlignment = HorizontalAlignment.Left;

            Daire.VerticalAlignment = VerticalAlignment.Top;

            Daire.Margin = new Thickness(e.GetPosition(this).X, e.GetPosition(this).Y, 0, 0);

            this.Root.Children.Add(Daire);

        }

Yukarıdaki kod içerisinde konumuzla ilgisiz olan kodları gri renkte yazdım. Geri kalan kısma bakarsak aslında yaptığımız şey StylusMove eventını yakalayarak bir Ellipse nesnesi yaratıp onun da pozisyonunu ayarlamak. Bu event her Stylus için çalışacağı için basit bir şekilde o anda gelen Stylus'ın pozisyonunu alarak elimizde yeni yarattığımız Ellipse'e set ediyoruz. İşte bu kadar! Aynı normal bir fare imlecinin pozisyonunu yakalamak gibi.

Eğer burada konuyu biraz daha ilerleterek StylusMove'da o anki parmakları birbirinden ayırt edip farklı renklerde daireler koymak isterseniz tabi ki bu da mümkün. Fakat o noktada ufak bir uyarıda bulunmam gerek. Birincisi ekrana tıklayan parmakları aslında tanıma şansımız yok. Yani şu anda dokunan parmak ile biraz sonra dokunan parmak aynı mı yoksa değil mi hiçbir zaman bilemeyiz. Aynı şekilde ekrana o anda dokunmakta olan bir parmak varsa ikinci gelen parmağın sadece ikinci olduğunu algılayabiliriz. Sonrasında o ikinci parmak gibip başka bir ikinci parmak gelirse bu ikisinin de aynı olup olmadığını hiçbir zaman bilemeyiz. O nedenle konunun özüne dönersek bizim kodumuzun algılayabileceği şey ancak ekrana tıklayan parmakların kaçıncı dokunan parmak oldukları. Bu çerçevede StylusMove gibi Stylus eventların da birer StylusID gönderiliyor.

[VB]

    Private Sub Window1_StylusMove(ByVal sender As Object, ByVal e As System.Windows.Input.StylusEventArgs) Handles Me.StylusMove

        Dim Daire As New Ellipse

        If e.StylusDevice.Id = 10 Then

            Daire.Fill = New SolidColorBrush(Colors.Red)

        Else

            Daire.Fill = New SolidColorBrush(Colors.Green)

        End If

        Daire.Height = 10

        Daire.Width = 10

        Daire.HorizontalAlignment = Windows.HorizontalAlignment.Left

        Daire.VerticalAlignment = Windows.VerticalAlignment.Top

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

        Me.Root.Children.Add(Daire)

    End Sub

[C#]

        void Window1_StylusMove(object sender, StylusEventArgs e)

        {

            Ellipse Daire = new Ellipse();

            if (e.StylusDevice.Id == 10)

            {

                Daire.Fill = new SolidColorBrush(Colors.Red);

            }

            else

            {

                Daire.Fill = new SolidColorBrush(Colors.Green);

            }

            Daire.Height = 10;

            Daire.Width = 10;

            Daire.HorizontalAlignment = HorizontalAlignment.Left;

            Daire.VerticalAlignment = VerticalAlignment.Top;

            Daire.Margin = new Thickness(e.GetPosition(this).X, e.GetPosition(this).Y, 0, 0);

            this.Root.Children.Add(Daire);

        }

Yukarıda görebileceğiniz şekilde Stylus eventlarımıza gelen argümanlar üzerinden StylusDevice'a ve onun ID'sine ulaşabiliyoruz. Bu noktada ID'ler 10'dan başlıyor ve aynı anda dokunan her parmak için birer artarak devam ediyor. Bizim basit örneğimizde her zaman ilk dokunan parmak için kırmızı daireler koyarken ikinci dokunan parmak için de yeşil daireler koyarak bu her iki parmağı ayrı ayrı algılayabildiğimizi görebiliyoruz.

Manipulation İşlemleri

Bir sonraki aşamada birden çok dokunma noktası ile yapılabilen manipülasyon işlemleri göz atacağız. Bu çerçevede en tanıdık örnek bir ekranda var olan resmin kullanıcının iki parmağı ile tekrar boyutlandırılabiliyor, yer değiştiriyor ve döndürülebiliyor olması. Tüm bunlar için artık elimizde her dokunuş koordinatlarının olduğunu düşünürsek geriye sadece gerekli hesaplamaları yaparak ekrandaki bir resme veya farklı bir nesneye sonucu yansıtmak kalıyor. Fakat merak etmeyin, o hesaplamaları da bizim yapmamıza gerek yok. Bunun yerine Managed Wrapper'lar ile beraber gelen ManipulationProcessor sınıfıdan faydalanabiliriz.

Yeni bir WPF projesi yarattıktan sonra gerekli DLL'lerimizi de referans alarak sonrasında hemen projemize bir resim dosyası ekleyelim. Bu arada bir sonraki adımda ihtiyacımız olacağı için System.Drawing assemblysini de projeye referans olarak eklemeyi unutmayın. Projeye eklediğimiz resmi göstermesi için XAML tarafına da bir Image nesnesi koymayı unutmayalım. Tabi bu Image nesnesini yerine göre tekrar boyutlandıracağımız, ekrandaki yerini değiştireceğimiz veya belirli bir açıya göre döndüreceğimiz için Image nesnesinin içine de gerekli Transform objelerini şimdiden koymakta fayda var.

[XAML]

<Window x:Class="Window1"

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

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

   Title="Window1" Height="300" Width="300">

    <Grid>

        <Image Source="Koala.jpg" RenderTransformOrigin="0.5, 0.5">

            <Image.RenderTransform>

                <TransformGroup>

                    <TranslateTransform x:Name="Konum" />

                    <RotateTransform x:Name="Aci" />

                    <ScaleTransform x:Name="Boyut" />

                </TransformGroup>

            </Image.RenderTransform>

        </Image>

    </Grid>

</Window>

Transform nesnelerine kod tarafından rahat ulaşabilelim diye şimdiden yukarıdaki gibi birer isim verebiliriz. Sıra geldi arka tarafta yazacağımız kodlara. Her zamanki gibi ilk olarak Stylus event'larını Enable edelim ve bu sefer StylusMove, StylusDown ve StylusUp eventlarının hepsini de yakalayalım. Bizim tüm manipülasyon işlemlerini yapacak olan nesneyi de yaratarak gerekli tüm eventlarda bilgileri hesaplama için kendisine aktarmamız gerekecek.

[VB]

    Private Sub Window1_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded

        Windows7.Multitouch.WPF.Factory.EnableStylusEvents(Me)

    End Sub

 

    WithEvents ManiProc As New Windows7.Multitouch.Manipulation.ManipulationProcessor(Windows7.Multitouch.Manipulation.ProcessorManipulations.ALL)

[C#]

        private void Window1_Loaded(object sender, System.Windows.RoutedEventArgs e)

        {

            Windows7.Multitouch.WPF.Factory.EnableStylusEvents(this);

        }

 

        Windows7.Multitouch.Manipulation.ManipulationProcessor ManiProc = new Windows7.Multitouch.Manipulation.ManipulationProcessor(Windows7.Multitouch.Manipulation.ProcessorManipulations.ALL);

ManipulationProcessor nesnemizi yaratırken parametre olan bir Enum alıyor ve söz konusu Enumaration içerisindeki seçeneklerimiz bizim ne tür manipülasyon hesaplaması istediğimizi belirtiyor. Sadece tekrar boyutlandırma veya sadece döndürme ile ilgili manipülasyonlar da isteyebiliriz. Bizim örneğimizde tüm manipülasyonları kullanacağımız için ALL diyerek devam ediyoruz. Sıra geldi daha önce yakaladığımız Stylus eventlarında gerekli datayı alıp ManipulationProcessor 'a aktarmaya.

[VB]

    Private Sub Window1_StylusDown(ByVal sender As Object, ByVal e As System.Windows.Input.StylusDownEventArgs) Handles Me.StylusDown

        ManiProc.ProcessDown(e.StylusDevice.Id, e.GetPosition(Me).ToDrawingPointF())

    End Sub

 

    Private Sub Window1_StylusMove(ByVal sender As Object, ByVal e As System.Windows.Input.StylusEventArgs) Handles Me.StylusMove

        ManiProc.ProcessMove(e.StylusDevice.Id, e.GetPosition(Me).ToDrawingPointF())

    End Sub

 

    Private Sub Window1_StylusUp(ByVal sender As Object, ByVal e As System.Windows.Input.StylusEventArgs) Handles Me.StylusUp

        ManiProc.ProcessUp(e.StylusDevice.Id, e.GetPosition(Me).ToDrawingPointF())

    End Sub

[C#]

        private void Window1_StylusDown(object sender, System.Windows.Input.StylusDownEventArgs e)

        {

            ManiProc.ProcessDown((uint)e.StylusDevice.Id, e.GetPosition(this).ToDrawingPointF());

        }

 

        private void Window1_StylusMove(object sender, System.Windows.Input.StylusEventArgs e)

        {

            ManiProc.ProcessMove((uint)e.StylusDevice.Id, e.GetPosition(this).ToDrawingPointF());

        }

 

        private void Window1_StylusUp(object sender, System.Windows.Input.StylusEventArgs e)

        {

            ManiProc.ProcessUp((uint)e.StylusDevice.Id, e.GetPosition(this).ToDrawingPointF());

        }

Yukarıdaki kodda da görebileceğiniz üzere aslında bizim yaptığımız birşey yok. Gelen StylusID ile pozisyonu doğrudan Processor'ımıza aktarıyoruz. Processor hesaplamaları tamamladıktan sonra kendine özel ayrı bir event çalıştırarak bize sonucu aktaracak.

[VB]

    Private Sub ManiProc_ManipulationDelta(ByVal sender As Object, ByVal e As Windows7.Multitouch.Manipulation.ManipulationDeltaEventArgs) Handles ManiProc.ManipulationDelta

        Konum.X += e.TranslationDelta.Width

        Konum.Y += e.TranslationDelta.Height

 

        Aci.Angle += e.RotationDelta * 180 / Math.PI

 

        Boyut.ScaleX *= e.ScaleDelta

        Boyut.ScaleY *= e.ScaleDelta

    End Sub

[C#]

        private void ManiProc_ManipulationDelta(object sender, Windows7.Multitouch.Manipulation.ManipulationDeltaEventArgs e)

        {

            Konum.X += e.TranslationDelta.Width;

            Konum.Y += e.TranslationDelta.Height;

 

            Aci.Angle += e.RotationDelta * 180 / Math.PI;

 

            Boyut.ScaleX *= e.ScaleDelta;

            Boyut.ScaleY *= e.ScaleDelta;

        }

İşte geldik en can alıcı noktaya. Processor'ımızın ManipulationDelta event'ı çalıştığında artık gerekli hesaplamalar yapılmış demektir. Geriye kalıyor hesaplamaları uygun şekilde ekrandaki resmimize yansıtmak. Bunun için zaten daha önce resmimize ait Transform nesnelerine özel isimler vermiştik. Bu durumda eldeki Delta değerlerini doğrudan uygun Transform nesnelerinin özelliklerine ekleyebiliriz.

Uygulamamız bitti. Artık programı çalıştırarak iki parmağınız ile rahatlıkla resmi tutup sürükleyebilir veya boyutlandırabilir, döndürebilirsiniz. Herşey bu kadar basit.

Hepinize kolay gelsin.

Örnek uygulamalara ait kaynak kodlar.