Ana Sayfa | English Blog | Seminer TV | Dil Cookie Sil  Blog'u Mail ile takip et!       
Daron Yöndem - August, 2009
bir yazılımcının tasarıları...
 Sunday, August 23, 2009

Blend içerisinde kullanılabilecek Behavior çeşitlerinden biri de Trigger'lı Behavior'lar. Bu tip Behavior'lara genelde Action da denebiliyor. Normal Behavior'lardan farklı olarak Trigger'lar da kendi içlerinde yaratılırken Inherit ettikleri sınıfa göre değişebilirler. Bu yazımızda TriggeredAction Behavior'larına göz atacağız.

Ne işe yarar?

TriggeredAction behaviorları sonuç itibari ile normal Behavior'lar gibi bir aksyon almayı hedefler fakat bu aksyon yine kullanıcı tarafından belirlenen bir durumu takiben işleme alınır. Normal şartlarda bir TriggeredAction kendi Parent kontrolünü hedef alarak onun herhangi bir event'ı çalıştığında çalışacak şekilde kenarda durur.

Örneğimizde basit bir MessageBox gösteren TriggerAction tanımlayacağız. Diyelim ki herhangi bir kontrole tıklandığında veya farklı durumlarda MessageBox'lar göstermek istiyoruz. Normalde yapmamız gereken kesinlikle kod tarafına geçip uygun kodu yazmak olurdu. Oysa hazırlayacağımız TriggerAction herhangi bir kod yazmadan tasarımcının istediği event ve zaman için istediği mesajı MessageBox ile gösterebilmesini sağlayacak.

Her zamanki gibi yeni bir Silverlight projesi yarattıkta sonra System.Windows.Interactivity.dll'i referans olarak alıyoruz. Projemize ekleyeceğimiz yeni bir VB/CS dosyasının içerisinde Behavior'ımızı tanımlayacak sınıfı yazarken TriggerAction sınıfnı da inherit ediyoruz.

[VB]

Public Class MsgBoxTrigger

    Inherits Interactivity.TriggerAction(Of UIElement)

 

    Protected Overrides Sub Invoke(ByVal parameter As Object)

 

    End Sub

End Class

[C#]

    public class MsgBoxTrigger : System.Windows.Interactivity.TriggerAction<UIElement>

    {

        protected override void Invoke(object parameter)

        {

        }

    }

Gördüğünüz üzere hemen anında Invoke adında bir de metoda sahip olduk. TriggerAction sınıfında Invoke "MustInherit" olarak tanımlandığı için söz konusu metodu bizim yazmamış şart. Zaten bu metod da bizim en çok işimize yarayacak metod olacak. Herhangi bir şekilde bizim Trigger'ımızda kullanıcı tarafından ayarlanmış event çalıştığında buradaki Invoke metodu çalışacak. Anlayacağınız TriggerAction'ımızın tüm işlevselliğini buraya yazacağız.

[VB]

    Private PText As String

    Public Property MessageText() As String

        Get

            Return PText

        End Get

        Set(ByVal value As String)

            PText = value

        End Set

    End Property

[C#]

        public string MessageText { get; set; }

Tasarımcımızdan aynı anda MessageBox ile gösterilecek metni de almamız gerek. O nedenle hemen bir de Property tanımlayarak sınıfımıza ekliyoruz. Bir sonraki adımda basit bir şekilde Invoke metodunda bu mesajı göstermemiz yeterli olacaktır.

[VB]

    Protected Overrides Sub Invoke(ByVal parameter As Object)

        MessageBox.Show(MessageText)

    End Sub

[C#]

        protected override void Invoke(object parameter)

        {

            MessageBox.Show(MessageText);

        }

Gördüğünüz gibi sistem epey basit. Her zamanki gibi isterseniz AssociatedObject'e de ulaşarak TriggerAction'ın bağlı olduğu kontrolü de bulabilirsiniz. Artık elimizdeki Behavior'ı rahatlıkla tasarımcılar da Blend içerisinde kullanarak istedikleri durumlarda MessageBox gösterebilirler.

Özel TriggerAction Behavior'ımız karşınızda.
Özel TriggerAction Behavior'ımız karşınızda.

XAML tarafına baktığımızda her zaman olduğu gibi tüm Trigger yapısı XML ile tanımlanmış durumda. Gerekli XML NameSpace'ler de eklendikten sonra hazırladığımız TriggerAction rahatlıkla kullanılabiliyor.

[XAML]

<UserControl

   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:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:local="clr-namespace:Triggers_VB" x:Class="Triggers_VB.MainPage"

   d:DesignWidth="640" d:DesignHeight="480">

  <Grid x:Name="LayoutRoot">

 

      <Rectangle Fill="Red" Stroke="Black" HorizontalAlignment="Left" Margin="157,184,0,215" Width="159">

          <i:Interaction.Triggers>

              <i:EventTrigger EventName="MouseLeftButtonDown">

                  <local:MsgBoxTrigger MessageText="Selamlar!"/>

              </i:EventTrigger>

          </i:Interaction.Triggers>

      </Rectangle>

 

  </Grid>

</UserControl>

Hepinize kolay gelsin.

Örneğe ait kaynak kodlar - 23082009_2.rar (49,3 KB)

Sunday, August 23, 2009 1:44:20 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Expression Blend | Silverlight 3.0  | 
 Saturday, August 22, 2009

Silverlight ilk çıktığı günden itibaren video konusunda ayrı bir iddaya sahip oldu. 1.0 sürümünden başlayarak Silverlight'ın CPU ve RAM kullanımındaki başarısı ister HD ister FullHD videoların oynatılmasında olsun süper bir performans ile bizleri şaşırtmayı başardı. Bazen keşke bu başarıyı Media Player'da da görsek diyerek kendi kendime serzenişlerde bulunmuyor değilim.

Konumuza dönersek, Silverlight ile beraber gelen bir diğer ilginç video çözümlerinden biri de Smooth Streaming adını taşıyor.  Peki nedir bu Smooth Streaming? Elinizde var sayalım ki normal bir video dosyası var ve bunu internetten paylaşmak istiyorsunuz. Fakat videonuzu paylaştığınız kullanıcıların bir kısmı belki de 1Mbit bağlantıya sahipken diğerleri çok daha yüksek hızlı bağlantılara sahipler. Videomuzun kalitesini ayarlarken hangi kitleyi hedefleyeceğiz? Acaba bağlantısı sağlam arkadaşlara göre mi encode etsek? Bu durumda 1Mbit ile internete bağlananlar videoları neredeyse izleyemeyecektir. Peki gibi de 1Mbit'e göre ayarlasak? Bu sefer de bağlantısı sağlam olan kişilere gerekli kalitede görüntüyü aktaramamış olacağız.

İşte bu iki ucu kokulu değnekten sağ salim kurtulmanın yolu Smooth Streaming. Gelin bir hayal kuralım ve diyelim ki kullanıcıların standart bağlantı hızlarını da es geçip o anki bağlantı hızlarına göre video kalitesini ayarlayabiliyoruz. Hatta ilk başka kullanıcılar videoyu ilk izlemeye başlarken kalitesiz video ile buffer'ı tamamlayıp hemen videoyu oynatsak sonrasında kullanıcının hattı sağlam ise kaliteliye doğru geçip yapsak. En önemlisi tüm bunlar gerçekleşirken kullanıcı hiçbirşeyin farkında olmasa ve videosunu izlemeye rahat bir şekilde devam etse? Güzel bir hayal değil mi? Ama hayal değil, bunların hepsi Smooth Streaming ile bir gerçek.

Nasıl çalışıyor?

Normalde video dosyalarını bir web sunucusu veya Windows Media Services gibi özel altyapılar ile yayınlarız ve bu altyapılar tek bir video dosyasını alarak yayınlamaya uygun şekilde düzenlenmiştir. Oysa elimizde aynı videonun farklı bitrate'ler ile encode edilmiş hali olsa kullanıcılara seçme şansı tanıyabiliriz. Fakat iki soru var; birincisi bant genişliği konusunda kullanıcı seçim yapmamalı. Söz konusu bant genişliği seçimi otomatik olmalı. Bunun için Silverlight tarafında biraz kod yazmak yeterli olacaktır. İkinci sorun ise bant genişliği ile ilgili seçimi sadece video başlarken değil video oynatılırken de kullanıcı farkında olmadan yapabilmek isteriz. Bant genişliği değiştikçe oynatıcı anında daha kaliteli veya kalitesize hızlı bir şekilde geçmeli. İşte bu noktada farklı bir taktik uygulama zorunda olacağımız kesin.

Smooth Streaming ile beraber video dosyaları birden çok bitrate'lerde encode edilerek paketlenip sunucuya yerleştiriliyor. Bu süreçte ayrıca bahsedeceğiz. Sonrasında bu paket IIS üzerine yüklenen IIS Media Services içerisinde Smooth Streaming sistemi tarafından tanınıyor ve kullanıcıdan gelen isteği göre uygun video gönderiliyor. IIS Media Services sadece bu kadarı ile kalmıyor ve video verilerimiz artık 2 ile 4 saniye arasındaki HTTP paketleri ile gönderiliyor. Böylece duruma göre gönderilecek olan 2. veya 4. paket farklı bitrate ile encode edilmiş farklı dosyalardan olabiliyor.

Smooth Streaming mantığı.
Smooth Streaming mantığı.

Yukarıdaki görselde de inceleyebileceğiniz üzere videonun sunucudan istemciye yolculuğunda eski taktik alt kısımda gösterilmiş durumda. Üst kısımdaki ise video 2sn'lik HTTP paketleri şeklinde gönderiliyor. Bu paketli sistemin ilk avantajı arada kalitenin değiştirilebilmesi iken ikinci bir avantaj ise bu paketlerin http paketlerini önbellekleyen bir başka sunucu tarafından önbelleğe alınabilmesi. Böylece son kullanıcıya aynı paketlerin aktarılması için bir noktaya kadar hız avantajı sağlanabiliyor.

Peki Nasıl Yaparız?

İlk olarak tabi ki sunucunuzda IIS Media Services kurulu olmalı. Doğrudan IIS7 üzerine yüklenen bu eklenti ile beraber artık sunucunuza Smooth Streaming desteği gelecektir. Apache tarafında da buradaki downloadları deneyebilirsiniz. Bir sonraki adımda gerekli olan şey elimizde Smooth Streaming'e uygun şekilde paketlenmiş birden çok bitrate'te kaydedilmiş video dosyaları. Bu şekilde bir paket hazırlamanın en kolay yolu Expression Encoder 3'ten geçiyor. Expression Endoer 3 varsayılan ayarları ile Smooth Streaming tanıyor.

Expression Encoder 3 içerisinde Smooth Streaming ayarları.
Expression Encoder 3 içerisinde Smooth Streaming ayarları.

Expression Encoder içerisine bir video ekledikten sonra video profillerinden Smooth Streaming altındaki profillerden birini seçebilirsiniz. Söz konusu seçimi yaptıktan sonra encode etmek istediğiniz ve kullanıcılarınıza ayrı ayrı sunmak istediğiniz her bandwidth'i listeye eklemeniz gerekiyor. Ayarları tamamladıktan sonra video dosyasını encode edebilirsiniz. Sonuçta elinize ism uzantısı ile başlayan dosyalar gelecektir. Yapmış olduğunuz ayarlara göre her encode edilen video dosyası ayrı bir ISMV dosyasında tutulabileceği gibi hepsi bir dosyada da tutulabilir. Bunu Expression Encoder içerisinde "Create separate file per stream" seçeneği ile ayarlayabilirsiniz.

[ISM Dosyası]

<?xml version="1.0" encoding="utf-16"?>

<!--Created with Expression Encoder version 3.0.1332.0-->

<smil xmlns="http://www.w3.org/2001/SMIL20/Language">

  <head>

    <meta

      name="clientManifestRelativePath"

      content="Live stream archive 17.08.2009 15.07.ismc" />

  </head>

  <body>

    <switch>

      <video

        src="Live stream archive 17.08.2009 15.07_305.ismv"

        systemBitrate="305000">

        <param

          name="trackID"

          value="2"

          valuetype="data" />

      </video>

      <video

        src="Live stream archive 17.08.2009 15.07_230.ismv"

        systemBitrate="230000">

        <param

          name="trackID"

          value="2"

          valuetype="data" />

      </video>

      <audio

        src="Live stream archive 17.08.2009 15.07_1644.ismv"

        systemBitrate="64000">

        <param

          name="trackID"

          value="1"

          valuetype="data" />

      </audio>

    </switch>

  </body>

</smil>

Yukarıdaki XML yapısı ISM dosyasının içinde saklı. Yapı ile beraber bu pakette bulunan videoların bitrate'leri ve dosyaların adları bulunuyor. Böylece IIS Media Services rahatlıkla bu dosya üzerinden bilgileri alabilecek. Ayrıca istemci tarafında kullanılacak bilgileri içeren ayrı bir manifest dosyası da ISMC uzantısı ile kayıtlıdır.

[ISMC Dosyası]

<?xml version="1.0" encoding="utf-16"?>

<!--Created with Expression Encoder version 3.0.1332.0-->

<SmoothStreamingMedia

  MajorVersion="2"

  MinorVersion="0"

  Duration="59730000">

  <StreamIndex

    Type="video"

    Chunks="1"

    QualityLevels="8"

    MaxWidth="640"

    MaxHeight="480"

    DisplayWidth="640"

    DisplayHeight="480"

    Url="QualityLevels({bitrate})/Fragments(video={start time})">

    <QualityLevel

      Index="0"

      Bitrate="1644000"

      FourCC="WVC1"

      MaxWidth="640"

      MaxHeight="480"

      CodecPrivateData="250000010FE3F213F0EF8A13F83BF1C0EFC80000010E5A0040" />

    <QualityLevel

      Index="1"

      Bitrate="1241000"

      FourCC="WVC1"

      MaxWidth="640"

      MaxHeight="480"

      CodecPrivateData="250000010FE3E613F0EF8A13F83BF1C0EFC80000010E5A0040" />

    <QualityLevel

      Index="2"

      Bitrate="937000"

      FourCC="WVC1"

      MaxWidth="640"

      MaxHeight="480"

      CodecPrivateData="250000010FE3DC13F0EF8A13F83BF1C0EFC80000010E5A0040" />

    <QualityLevel

      Index="3"

      Bitrate="708000"

      FourCC="WVC1"

      MaxWidth="428"

      MaxHeight="320"

      CodecPrivateData="250000010FE3D613F0EF8A13F83BF1C0EFC80000010E5A0040" />

    <QualityLevel

      Index="4"

      Bitrate="534000"

      FourCC="WVC1"

      MaxWidth="428"

      MaxHeight="320"

      CodecPrivateData="250000010FE3D013F0EF8A13F83BF1C0EFC80000010E5A0040" />

    <QualityLevel

      Index="5"

      Bitrate="403000"

      FourCC="WVC1"

      MaxWidth="428"

      MaxHeight="320"

      CodecPrivateData="250000010FE3CC13F0EF8A13F83BF1C0EFC80000010E5A0040" />

    <QualityLevel

      Index="6"

      Bitrate="305000"

      FourCC="WVC1"

      MaxWidth="364"

      MaxHeight="272"

      CodecPrivateData="250000010FE3C813F0EF8A13F83BF1C0EFC80000010E5A0040" />

    <QualityLevel

      Index="7"

      Bitrate="230000"

      FourCC="WVC1"

      MaxWidth="364"

      MaxHeight="272"

      CodecPrivateData="250000010FE3C613F0EF8A13F83BF1C0EFC80000010E5A0040" />

    <c

      n="0"

      d="58320001">

      <f

        i="0"

        s="438"

        q="7405" />

      <f

        i="1"

        s="327"

        q="5536" />

      <f

        i="2"

        s="237"

        q="4016" />

      <f

        i="3"

        s="195"

        q="3299" />

      <f

        i="4"

        s="137"

        q="2321" />

      <f

        i="5"

        s="101"

        q="1706" />

      <f

        i="6"

        s="75"

        q="1267" />

      <f

        i="7"

        s="63"

        q="1063" />

    </c>

  </StreamIndex>

  <StreamIndex

    Type="audio"

    Index="0"

    FourCC="WMAP"

    Chunks="3"

    QualityLevels="1"

    Url="QualityLevels({bitrate})/Fragments(audio={start time})">

    <QualityLevel

      Bitrate="64000"

      SamplingRate="44100"

      Channels="2"

      BitsPerSample="16"

      PacketSize="2973"

      AudioTag="354"

      CodecPrivateData="1000030000000000000000000000E00042C0" />

    <c

      n="0"

      d="39009523" />

    <c

      n="1"

      d="19504762" />

    <c

      n="2"

      d="1680045" />

  </StreamIndex>

</SmoothStreamingMedia>

Yukarıda gördüğünüz dosyaların yanında sadece farklı bitrate'ler ile encode edilmiş videolar bulunuyor. Olay aslında bu kadar basit. İstemci tarafına baktığımızda ise esas mesele bu yapıyı Silverlight nasıl oynatacak veya kendi bant genişliğini algılayarak nasıl seçim yapacak sorusu geliyor. İşte bu sorularla iglili cevapları içeren kodu sıfırdan yazmamız gerekmiyor. Expression Encoder ile beraber gelen tüm Silverlight şablonları hali hazırda Smooth Streaming desteğine sahip. Unutmayın ki bu şablonların hepsinin kaynak kodları da yine Expression Encoder'ın yüklü olduğu klasörde mevcut. Böylece oluşturulan yapıyı kopyalayarak kendi projelerinizde de rahatlıkla ilerleyebilirsiniz.

Sonuç olarak...

Hemen Smooth Streaming'i denemek isterseniz aşağıdaki adresten örnek bir videoyu izleyebilirsiniz. Hatta söz konusu sitede göreceğiniz Silverlight uygulamalasında kendi bant genişliğinizi üst kısımdan limitleyebilir ve alt solda da hangi kalitede videonun istemciye indirildiğini test edebilirsiniz.

Hepiniz kolay gelsin...

Saturday, August 22, 2009 6:50:42 PM (GTB Standard Time, UTC+02:00)  #    Comments [7]   Silverlight 3.0  | 
 Friday, August 21, 2009

Dün Expression Blend 3 içerisinde Behaviorlara göz attık. Bugün de gelin bir yazılımcı gözü ile nasıl Behavior yaratabiliriz inceleyelim.

Behavior'lar kendi içlerinde ayrı gruplarda incelenebilirler. Bazıları bir event üzerinden tetiklenmesi gerekirken bazıları ise doğal yoldan bağlandıkları nesnelere bir özellik ekleyebiliyorlar. Örneğin GoToStateAction aslında TriggerAction tipinde bir Behavior olarak karşımıza çıkarken FluidMoveBehavior ise tek başına Trigger içermeyen bir Behavior olarak tasarlanmıştır. Bu gibi farklı Behavior tiplerinin programlanması da kısmen tabi ki birbirinden farklı oluyor.

Bu yazımızda inceleyeceğimiz Behavior'lar Trigger içermeyen ve hedef kontrollerine özellik katma amacı güden Behavior'lar olacak.

Nasıl Behavior yazarız?

Behavior'lar System.Windows.Interactivity.dll içerisinde saklı Behavior<T> / Behavior(Of T) sınıfından türeyerek oluşturulular. O nedenle yarattığımız yeni bir Silverlight projesi içerisinde ilk olarak söz konusu DLL'i referans olarak eklemeliyiz. Projenize "solution explorer" içerisinde sağ tuş tıklayarak "add reference" komutunu verdiğiniz System.Windows.Interactivity karşınıza gelecektir. Hemen referans olarak ekleyerek ilerleyebilirsiniz.

Yeni Silverlight projemizde Behavior'ımızı yazmak için yeni bir VB/CS dosyası ekleyerek Behavior sınıfından türeyen yeni bir sınıf yaratıyoruz.

[VB]

Public Class OpacityBehavior

    Inherits Interactivity.Behavior(Of UIElement)

 

 

End Class

[C#]

    public class OpacityBehavior : System.Windows.Interactivity.Behavior<UIElement>

    {

    }

Behavior sınıfından türetirken generics yapısını kullanarak UIElement'i parametre olarak verdiğimizi görebilirsiniz. Bunun nedeni geliştirdiğimiz Behavior'ın tüm UIElement'lere uygulanabilecek olması. Behaviorımız şimdilik örnek olması amacıyla basit bir işlem yapacak ve kendisine verilen herhangi bir kontrolün fare ile üzerine gelinmesi halinde şeffaflığını değiştirecek. Böylece üzerine gelince şeffaf, çıkınca normal gözüken kontroller tasarlamak istersek doğrudan bu geliştirdiğimiz Behavior'ı kullanabileceğiz.

Şeffaflık değişimi için erişmemiz gereken property olan Opacity aslında UIElement'ten türetilmiş tüm kontrollerde bulunduğu için bizim de UIElement'leri hedeflememiz doğru olacaktır.

[VB]

    Private POpacity As Double

    <ComponentModel.Category("Behavior Özellikleri")> _

    Public Property Opacity() As Double

        Get

            Return POpacity

        End Get

        Set(ByVal value As Double)

            POpacity = value

        End Set

    End Property

[C#]

        [System.ComponentModel.Category("Behavior Özellikleri")]

        public double Opacity { get; set; }

Yukarıdaki basit bir şekilde Opacity adında bir Property tanımlıyoruz. Bu property bizim Behavior'ımızın bir propertysi olacak. Böylece kullanıcıların Behavior'ı kullanırken kendisine parametre olarak hedef kontrolün fare ile üzerine gelindiğinde ne kadar şeffaf olmasını istediklerini yazabilecekler. Bu Property Expression Blend içerisinde sağ kolonda gözükecek. Blend içerisinde Properties panelinde bu şekilde Property'lerinizi gruplayabilirsiniz. Property'mize verdiğimiz meta data bunu sağlıyor. Şimdi sıra geldi hedef kontrolü ve onun MouseEnter, MouseLeave eventlarını yakalamaya.

[VB]

    Protected Overrides Sub OnAttached()

        AddHandler AssociatedObject.MouseEnter, AddressOf MouseEnter

        AddHandler AssociatedObject.MouseLeave, AddressOf MouseLeave

        MyBase.OnAttached()

    End Sub

 

    Protected Overrides Sub OnDetaching()

        RemoveHandler AssociatedObject.MouseEnter, AddressOf MouseEnter

        RemoveHandler AssociatedObject.MouseLeave, AddressOf MouseLeave

        MyBase.OnDetaching()

    End Sub

[C#]

       protected override void OnAttached()

        {

            AssociatedObject.MouseEnter += new MouseEventHandler(AssociatedObject_MouseEnter);

            AssociatedObject.MouseLeave += new MouseEventHandler(AssociatedObject_MouseLeave);

            base.OnAttached();

        }

 

        protected override void OnDetaching()

        {

            AssociatedObject.MouseEnter -= new MouseEventHandler(AssociatedObject_MouseEnter);

            AssociatedObject.MouseLeave -= new MouseEventHandler(AssociatedObject_MouseLeave);

            base.OnDetaching();

        }

Yukarıdaki kod grubunda da gördüğünüz üzere BaseClass'tan gelen OnAttached ve OnDetaching metodlarını override ediyoruz. Bu metodlar aslında Behavior nesnemizin yaratıldığı anda çalışan OnAttached ve sahneden kaldırıldığı anda çalışan OnDetaching eventları şeklinde düşünülebilir. Biz de bu durumda OnAttached içerisinde hemen AssociatedObject'e ulaşarak istediğimiz eventları bağlıyoruz. AssociatedObject otomatik olarak bu Behavior'ın içerisine konduğu yani hedeflediği kontrolün bir referansını taşıyor. Aynı şekilde herhangi bir karışıklık olmasın diye OnDetaching'de de event listenerlarımızı kaldırıyoruz.

[VB]

    Public Sub MouseEnter(ByVal sender As Object, ByVal e As System.Windows.Input.MouseEventArgs)

        AssociatedObject.Opacity = POpacity

    End Sub

 

    Public Sub MouseLeave(ByVal sender As Object, ByVal e As System.Windows.Input.MouseEventArgs)

        AssociatedObject.Opacity = 1

    End Sub

[C#]

        void AssociatedObject_MouseLeave(object sender, MouseEventArgs e)

        {

            AssociatedObject.Opacity = 1;

        }

 

        void AssociatedObject_MouseEnter(object sender, MouseEventArgs e)

        {

            AssociatedObject.Opacity = Opacity;

        }

Eh artık sıra geldi MouseEnter ve MouseLeave durumlarında yapılacakları yazmaya. Gördüğünüz gibi daha önce yarattığımız parametremizden gelen değeri hemen duruma göre AssociatedObject'in Opacity özelliğine aktarıyoruz. Burada tabi ki hoş bir animasyon da kullanabilirdik.

Bu behavior'ı nasıl kullanacağız?

Herşeyi kaydettikten sonra projenizi Blend içerisinde açarsanız Asset Library içerisinde Behaviors tabında kendi Behavior'ınızı da görebilirsiniz. Hemen herhangi bir kontrole ataçlayarak sağ tarafta da ayarladığımız özellikleri inceleyebilirsiniz.

Blend içerisinde kendi Behavior'ımız.
Blend içerisinde kendi Behavior'ımız.

Aşağıdaki XAML koduna baktığımızda ise iki farklı XML NameSpace'i görebiliyoruz. Bunlardan biri doğrudan System.Windows.Interactivity'i hedeflerken local adındaki diğeri ise bizim kendi projemizin Assembly'sini hedefliyor. Bu NameSpace'ler üzerinden behavior'ımızı rahatlıkla kullanabiliyoruz.

[XAML]

<UserControl

   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:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

   xmlns:local="clr-namespace:SilverlightApplication1"

   x:Class="SilverlightApplication1.MainPage"

   d:DesignWidth="640" d:DesignHeight="480">

  <Grid x:Name="LayoutRoot">

      <Rectangle Fill="Red" Stroke="Black" Margin="154,177,273,163">

          <i:Interaction.Behaviors>

              <local:OpacityBehavior Opacity="0.2"/>

          </i:Interaction.Behaviors>

      </Rectangle>

  </Grid>

</UserControl>

Gördüğünüz gibi Behavior yazmak hiç de zor değil. Yazdığınız her Behavior tasarımcıların işini kolaylaştırmakla kalmıyor aynı anda yazılımcılar olarak sizin iş yükünüzü de azaltıyor.

Hepinize kolay gelsin ;)

Örneğe ait kaynak kodlar - 21082009_2.rar (47,29 KB)

Friday, August 21, 2009 1:41:36 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Silverlight 3.0  | 
 Thursday, August 20, 2009

Silverlight 3 ile beraber gelen en güzel özelliklerden biri de Behavior yapısı. Behavior'lar bize normalde programatik olarak yaptığımız işlevsellikleri XAML içerisinde tanımlama şansı tanıyan kontroller olarak öngörülebilirler. Daha fazla detaylarına inmeden SL 3.0 SDK ve Silverlight Toolkit ile beraber gelen Behavior'lara isterseniz tek tek göz atalım ve bakalım eskisine kıyasla işimizi ne kadar kolaylaştırıyorlar.

Behaviorlar nerede?

Behaviorlar'ı kullanmanın en kolay yolu Expression Blend'in içi. Sonuç olarak bu kontroller birer XAML kodu yaratacağına göre ve biz de bunları ezberleyip yazma eğiliminde en azından başlangıç aşamasında değilsek Blend doğru seçim olacaktır. Blend 3 içerisinde yeni bir SL projesi yarattıktan sonra hemen Assets paneline göz atıp Behaviors tabını görebilirsiniz.

Expression Blend içerisinde Behavior'lar.
Expression Blend içerisinde Behavior'lar.

Gördüğünüz üzere ilk bakışta toplam sekiz Behavior bizi bekliyor. Hemen en kolayı ile başlayarak bakalım Behavior'ları nasıl kullanıyoruz.

HyperlinkAction

Varsayalım ki elinizde bir kontrol var ve tıklandığında internette bir adrese yönlendirme yapmak istiyorsunuz. Bu durumda HyperlinkButton belki durumu kurtarabilir fakat elinizdeki her kontrolü tasarımı ile beraber birer HyperlinkButton'a çevirmek de ciddi işkence olabilir. Keşke herhangi bir kontrole tıklandığında bir URL'e yönlendirebilsek? Aslında bunu kod tarafından yapabiliriz. Örneğin elimizde bir Rectangle varsa onun MouseLeftButtonDown durumunu yakalayıp orada da kod ile kullanıcıyı başka bir web sayfasına yönlendirebiliriz.

Tüm bunları yapmak yerine basit bir şekilde HyperlinkAction Behavior'ını da kullanabiliriz. Hemen sahneye örnek bir Rectanlge aldıktan sonra Assets içerisinden de HyperlinkAction'ı alarak "Objects and Timeline" içerisinde Rectangle'ın üzerine bırakabilirsiniz.

Behavior'ımız sahnede.
Behavior'ımız sahnede.

Behavior'ı eklediğiniz anda yukarıdaki manzara ile karşılaşacaksınız. Artık Rectangle'ın için bir başka kontrol var yani bir Behavior var. Hemen bu Behavior nesnesini seçerseniz bu sefer de Properties panelinde Behavior'a ait özellikleri bulabilirsiniz.

Behavior'ımıza ait ayarlar.
Behavior'ımıza ait ayarlar.

Behavior'ımızın özelliklerini incelediğimizde aslında karşımızda bir EventTrigger olduğunu görüyoruz. Bu yapı daha önce WPF ile çalışanlara tanıdık gelecektir. SourceName özelliğinde Parent yazması Behavior'ımızın otomatik olarak içerisinde olduğu Rectangle'ı kaynak aldığını anlamına geliyor. EventName kısmında Rectangle'ın hangi durumunda linkin çalışmasını istiyorsak onu seçiyoruz. Varsayılan ayarlarda MouseLeftButtonDown geliyor. Son olarak tabi linkimizin adresini ve hedefini de girersek artık herşey tamamdır. Bir satır bile kod yazmadan sadece birkaç ayar ile işimizi bitirdik. Bu sistem özellikle tasarımcılar açısından çok değerli olsa da hızlı uygulama geliştirme noktasında herkesin işine yarayacaktır. Diğer yandan bu şekilde farklı Behavior'ları yazılımcıların da hazırlayabileceğini düşünürsek işlevselliklerin merkezi hale getirilmesinde de Behavior'lar ciddi rol oynayabilir.

MouseDragElementBehavior

Drag&Drop işlemleri alışkın olduğumuz işlevsellikler arasında. Bu işlevselliği Silverlight içerisinde sağlamak için de hemen kod tarafından ilerleyebiliriz. Oysa basit durumlarda kullanabileceğimiz bir Behavior olarak MouseDragElementBehavior da rahatlıkla kullanılabilir. Söz konusu behavior'ı herhangi bir kontrole bağlamanız onun sürüklenip bırakılabilir hale gelmesi için yeterli. MouseDragElementBehavior'ın ConstrainToParentBounds adında tek bir özelliği var. Eğer bu özelliğe True değerini verirseniz sürükelyip bırakmayı hedeflediğiniz nesne sadece kendi Parent nesnesi içerisinde sürüklenebilecektir. Aksi halde nesne tamamen global olarak sürüklenebilir.

PlaySoundAction

Silverlight içerisinde ses veya video dosyalarını oynatmak için MediaElement kullanıyoruz. Oysa bazı durumlarda basit bir uyarı sesi veya farklı bir efekt yaratmak isteyebiliriz. Bu gibi durumlarda özellikle sahnede bir MediaElement tutmak, duruma göre Source'unu değiştirmek ve Play metodu ile sesi oynatmak ciddi miktarda kod yazmanızı gerektirebilir. Oysa PlaySoundAction Behavior'ı ile doğrudan herhangi bir elementin bir durumunda ses oynatabilirsiniz.

PlaySoundAction ayarları.
PlaySoundAction ayarları.

PlaySoundAction Behavior'ı SourceName ile aldığı kendi Parent kontrolünün herhangi bir durumunda yine Behavior'a Source olarak verilmiş bir WMA veya MP3 dosyasını oynatabiliyor. Görüldüğü üzere işimizi ciddi şekilde kolaylaştırabilir.

RemoveElementAction

Adından da anlaşılacağı üzere RemoveElementAction sahnedeki herhangi bir kontrolü sahneden kaldırıp yok eden bir işlevselliğe sahip. Örneğin bir Rectangle'a tıklandığında başka bir kontrolün sahneden kalkmasını istiyorsanız RemoveElementAction doğru seçim olacaktır.

RemoveElementAction'ın ayarları.
RemoveElementAction'ın ayarları.

Klasik Behavior'larda olduğu gibi RemoveElementAction'ın da SourceName ve EventName adında iki property'si ile Behavior'ın hangi kontrolün hangi eventı çalıştığında çalıştırılacağına karar verebiliyoruz. Sonrasında da TargetName ile sahneden kaldırılacak kontrolü behavior'a teslim etmemiz gerekiyor. Bu noktada ekrandaki herhangi bir elementi seçebilirsiniz, Behavior'ınız rahatlıkla çalışacaktır.

ControlStoryboardAction

Hayatınızı ciddi anlamda kolaylaştırabilecek Behavior'lardan biri de ControlStoryBoardAction olacaktır. Tahmin edebileceğiniz üzere bu Behavior herhangi bir kontrolün bir durumunda gidip bir animasyonu çalıştırabilir, durdurabilir veya bu iki durum arasında toggle işlemi de sağlayabilir. Tüm bunları bir satır bile kod yazmadan artık doğrudan Blend arayüzünde ayarlayabilirsiniz.

ControlStoryboardAction iş başında.
ControlStoryboardAction iş başında.

Yukarıdaki ekran görüntüsünde de görebileceğiniz üzere rahatlıkla bir kontrolün MouseLeftButtonDown durumunda başka bir animasyonu çalıştırabiliyoruz.

ChangePropertyAction

Varsayalım ki herhangi bir kontrolün bir property'sine ait değeri değiştirmek istiyorsunuz. Bu çok basit bir şekilde nesnenin rengi olabileceği gibi belki çok daha farklı bir özelliği de olabilir. Bunun için arka tarafa geçip C# veya VB kodu yazabilirsiniz fakat tasarımcı gözü ile baktığımızda bu pek de sıcak bir seçenek değil. İşte bu gibi durumlarda lowlevel bir Behavior olarak ChangePropertyAction karşımıza çıkıyor ve herhangi bir kontrolün herhangi bir özelliğini istediğimiz zaman değiştirebiliyor.

ChangePropertyAction ile animasyon dahi oluşturabilirsiniz.
ChangePropertyAction ile animasyon dahi oluşturabilirsiniz.

ChangePropertyAction'ın TargetName özelliğine herhangi bir özelliğini değiştirmek istediğiniz hedef kontrolünü aktarıyorsunuz. Bizim örneğimizde Behavior'ın verildiği nesnenin ta kendisini değiştireceğimiz için TargetName değerini Parent olarak kaldı. Sonrasında hedef elementin hangi özelliğini değiştireceğimizi seçip yeni değeri veriyoruz. Eğer isterseniz ek olarak bir de Animation properties tabından animasyon ayarı yapabilirsiniz. Eğer seçtiğiniz hedef Property anime edilebilir bir Property ise bu kısımda yazdığınız Duration (Süre) ve Ease (İvme) ayarına göre animasyon oynatılacaktır. Bizim örneğimizde hedef nesnenin Fill özelliğini değiştirdiğimiz animasyon çalışmayacaktır çünkü Fill değil ancak Fill'in için SolidColorBrush'ının Color özelliği anime edilebilir. Oysa Opacity Property'sini seçseydik doğrudan hedef kontol üzerinden anima edebilirdik.

FluidMoveBehavior

Behavior'lar arasında en havalı efekte sahip Behavior ile karşı karşıyayız :) FliudMoveBehavior'ın amacı herhangi bir Panel kontrolü içerisindeki nesnelerin Panel kontrolü nedeniyle oluşan görsel değişikliklerinin birer animasyon ile gerçekleşmesini sağlamak. Güzel ve bir o kadar da anlaşılması zor bir cümle olduğunun farkındayım :) Şimdi diyelim ki bir WrapPanel'iniz var ve içerisinde birçok kontrolünüz bulunuyor. Bu kontroller WrapPanel'in durumuna göre yukarı aşağı toparlanarak farklı satırlara veya aynı satıra toplanırlar. İşte bu toplanma işleminde kontrollerin pozisyonları değişir fakat bu değişim bir animasyon ile gerçekleşmez. FliudMoveBehavior bu değişimin bir animasyon ile yapılmasını sağlar.

FliudMoveBehavior ve WrapPanel beraber.
FliudMoveBehavior ve WrapPanel beraber.

Yukarıdaki ekran görüntüsünde inceleyebileceğiniz örneğimizde bir Wrappanel içerisinde birçok düğme bulunuyor. Bu düğmeler duruma göre WrapPanel içerisinde değişik yerlerde bulunacaklar. İşte bu konum değişikleri de birer animasyonla yapılsın diye WrapPanel içine FluidMoveBehavior'ı atıp efektin WrapPanel'e değil de WrapPanel içindeki tüm kontrollere uygulanması gerektiğini de Applies To seçeneğine Children değerini vererek belirtiyoruz. İsterseniz oluşturulacak animasyonlar için süre ve easing de ayrıca Behavior üzerinden ayarlayabilirsiniz.

İtiraf etmek gerekirse tüm Behavior'lar arasında hızlı bir şekilde etkileyici görsel şölen yaratan en başarılı Behavior'lardan biri FluidMoveBehavior. O nedenle kesinlikle denemenizi tavsiye ederim.

GoToStateAction

Son Behavior'ımızın adı GoToStateAction. Hatırlarsanız VisualStateManager ile beraber neredeyse tüm kontrollerin farklı State'leri vardı. Bu State'ler kontrollerin farklı durumlarında tasarımcıların farklı tasarımlar ve hatta geçiş animasyonları yapmasını sağlıyor. Yazılımcılarla tasarımcıların beraber çalıştığı projelerde ayrıca özel UserControl'ler için de custom state tanımlamaları yapılabiliyor. İşte tüm bu işlemlerde bir control'ün state'ini değiştirmek için normalde yine kod tarafına geçerdir ama artık GoToStateAction ile beraber buna gerek kalmıyor ve Blend içerisinde de hangi kontrolün hangi durumunda herhangi bir kontrolün  farklı bir State'ine geçiş yapılabileceğine karar verebiliyoruz.

GoToStateAction'a ait ayarlar.
GoToStateAction'a ait ayarlar.

Behavior'ımızı herhangi bir kontrole ekleyip söz konusu kontrolün hangi event'ında çalışacağına da karar verdikten sonra TargetName hedef kontrolümüzü seçerek sonrasında da StateName kısmında da geçiş yapılmasını istediğimiz hedef kontrolün State'ini seçiyoruz. Ayrıca geçiş esnasında animasyon yapılmasını istiyorsanız UserTransitions özelliğini de seçili tutmayı unutmayın.

Sonuç olarak...

Behavior'lar ciddi anlamda Blend kullanımını arttırarak olabildiğince kod yazımını azaltabiliyor. Böylece hedef projeler çok daha hızlı bir şekilde sonlandırılabilir. Yazılımcılar olarak isterseniz kendi Behaviorlarınızı da yazabilirsiniz. O konuda da çok yakında ayrı bir makale ile karşınıza olacağım.

Hepinize kolay gelsin!

Thursday, August 20, 2009 12:37:47 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Silverlight 3.0  | 
 Sunday, August 16, 2009

DeepZoom uygulamalar Silverlight 2.0 yayınlandığından bu yana en ilgi çeken popülar uygulamalardan biri. DeepZoom ile beraber çok büyük fotoğraf arşivlerini rahatlıkla tarayıcı içerisinde gösterebilmekle kalmıyor aynı anda kullanıcılara çok farklı bir deneyim de yaşatabiliyoruz. Bugüne kadar DeepZoom uygulamaları geliştirirken kullandığımız DeepZoom Composer bundan bir süre önce beta olmaktan kurtulup ilk sürümü ile yayınlandı.

DeepZoom Composer download link:
http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=457b17b7-52bf-4bda-87a3-fa8a4673f8bf

DeepZoom Composer'ın son sürümünde standart DeepZoom uygulamaları ile ilgili çok güzel yenilikler geliyor ve bir satır bile kod yazmadan günü birlik kullanıma uygun güzel sonuçlar alınabiliyor. Bu yazımızda DeepZoom Composer'ın son sürümünü inceleyerek gelen yeniliklerden bahsedeceğiz.

Başlangıç için...

DeepZoom Composer'a açtığımızda hemen projemizi yaratıp ekrandaki "Add image" menüsünden yüksek çözünürlüklü resimlerimizi ekleyebiliyoruz. Import modundayken tüm resimlerimizi ekledikten sonra uygulamanın arayüzünü tasarlamak için Compose moduna geçebiliriz. Resimlerimizi farklı şekillerde ister iç içe, ister yan yana yerleştirebiliriz. Hayal gücü size kalmış. Şimdi gelin elimizdeki yeni proje ile en son yeniliklere göz atalım.

DeepZoom içinde linkler!

DeepZoom uygulamalarında binlerce resim olabilir. Bu gibi durumlarda bazen bu resimlerden birine tıklandığında veya ekranda belirli bir alana tıklandığında başka bir resme kullanıcıyı yönlendirmek isteyebilirsiniz. Belki de tamamen DeepZoom uygulaması dışında bir adrese yönlendireceksiniz kullanıcıları. Tüm bunları artık doğrudan DeepZoom Composer içerisinde ayarlayabiliyorsunuz.

Link olacak alanı belirlerken...
Link olacak alanı belirlerken...

Uygulamanız içerisinde link olarak tıklanacak bir alanı belirlemek için yukarıdaki resimde de görebileceğiniz ufak "Add area" düğmesini kullanacağız. Düğmeye tıkladıktan sonra DeepZoom uygulamanızın compose ekranında istediğiniz alanı bir kare içerisine alabilirsiniz.

Link olacak bölgeyi yarattık.
Link olacak bölgeyi yarattık.

Link olarak kullanılacak bölgeyi tanımladıktan sonra sıra geliyor linkin ne işe yarayacağını ayarlamaya. Daha önce de bahsettiğimiz gibi aslında iki farklı seçenek var. Bunlardan ilki linkin kullanıcıyı DeepZoom uygulaması içerisinde başka bir resme yönlendiriyor olması. Diğer seçenek ise doğrudan kullanıcıyı harici bir adrese yönlendirmek. İlk olarak gelin dahili linkleme işlemini nasıl yapabiliyoruz görelim.

Dahili linkleme işlemini yapıyoruz...
Dahili linkleme işlemini yapıyoruz...

Programı sol kısmında yukarıdaki aşağıya dikey bir menü göreceksiniz. Bu menünün üzerinde "Internatl Links" tabına bastıktan sonra "Layers" tabında yer alan ve daha önce yarattığımız alanlardan (Area) herhangi birini alıp "Internatl Links" tabına sürükleyerek atabiliyoruz. Bu işlemi yaptığımız gibi hemen karşımıza Direct Link penceresi geliyor ve bu alanın bir dahili link (Internal link) olarak kullanılırken DeepZoom uygulaması içerisinden hangi resmi hedef alacağını belirtmemizi istiyor. Bu ekrandan da bir resim seçmeniz ile artık linkleme işlemini bitirdiniz demektir. Bu şekilde rahatlıkla istediğiniz kadar link ayarlayabilir ve DeepZoom uygulamalarınız içerisinde ilginç navigasyon sistemleri oluşturabilirsiniz.

Harici link eklemenin yolu.
Harici link eklemenin yolu.

DeepZoom uygulamalarınız içerisinde dahili linkler gibi isterseniz harici sitelere linkler de yerleştirebilirsiniz. Bunun için ilk olarak "External Link" tabını açmış olmanız gerekiyor. Sonrasında ekranın sağ tarafındaki "Layers" tabından herhangi bir "Area"yı alarak "External Links" tabına sürükleyip bırakabilirsiniz. İşlemi tamamlamanızla beraber karşınıza "External Links Management" ekranı gelecektir. Hemen araç çubuğundan bir link ekleyebilir ve linkin adını, adresini girebilirsiniz. İşte bu kadar, artık DeepZoom uygulamasından dışarıya çıkacak bir link eklemiş oldunuz bile.

DeepZoom ile Slideshow!

DeepZoom uygulamaları resim göstermek ne kadar hoş olsa da bazı durumlarda resimlerin sırası ile ekrandan geçmesini isteriz. Bu gibi durumlarda ayrı Slideshowlar kullanmak zorunda kalsak da aslında DeepZoom uygulamaları kendi iç özelllikleri ile beraber SlideShow altyapıları içinde muhteşem bir sistem olarak öngörülebilir. Bu gibi bir çalışmayı yapmak için daha önceleri kod yazmak zorunda kalsak da artık DeepZoom Composer bize bu özelliği hazır olarak getiriyor.

Slideshow hazırlıyoruz.
Slideshow hazırlıyoruz.

DeepZoom uygulamanız içerisinde resimleri istediğiniz gibi yerleştirebilir, sıralayabilirsiniz. Bazı resimler ufak bazıları ise büyük hatta iç içe resimler bile olabilir. Tüm bunları algılayacak olan SlideShow sistemi otomatik olarak resimlerinizi tek tek gezerek ekrana sığdıracak şekilde resimlerinize gerektiğinde zoom da yapacaktır, aynı şekilde zoom out da yapabilir.

Uygulamanızın ana ekran tasarımını bitirdikten sonra "SlideShow" tabını açarak sağ taraftaki "Layers" bölümünden istediğiniz resimleri sırası ile "SlideShow" tabına sürükleyerek bırakabilirsiniz. Tab içerisindeki resimler yine tab içerisindeki sıraları ile DeepZoom uygulamasında gezilecektir.

Örnek DeepZoom uygulamasından bir kare.
Örnek DeepZoom uygulamasından bir kare.

DeepZoom uygulamalarında dahili menüler!

Menü sistemleri neredeyse her web sitesinde vardır. SlideShow ve harici linklerin dışında bazen bir DeepZoom uygulamasında çok fazla fotoğraf olduğunda ayrı bir menü ile fotoğrafların listelenebilmesi hatta belki de kategorilerine göre ayrılabilmelerini sağlamak hoş olabilir. İşte bu gibi bir işlevselliği rahatlıkla DeepZoom Composer içerisindeki menü sistemi ile geliştirebilirsiniz.

DeepZoom'umuz için menü hazırlıyoruz.
DeepZoom'umuz için menü hazırlıyoruz.

DeepZoom uygulamanıza bir menü eklemek için hemen "Menu" tabını açarak sağ taraftaki "Layers" tabından istediğiniz fotoğrafları "Menu" tabına sürükleyerek atabilirsiniz. Tüm fotoğraflarınızı taba aldıktan sonra istediğiniz bir fotoğrafı seçip dikey veya yatay olarak menü içerisindeki pozisyonunu değiştirebilirsiniz. Böylece rahatlıkla gruplamalar da yapabilirsiniz.

DeepZoom uygulamamızdaki menü!
DeepZoom uygulamamızdaki menü!

Gördüğünüz gibi artık DeepZoom uygulamamızın sol üstündeki oktan ayrı bir menüye ulaşabiliyoruz. Bu menüde daha önce DeepZoom Composer içerisinde hazırladığımız resimlerin bir listesi uygun şekilde listeleniyor. Menüden herhangi bir resme tıkladığınızda DeepZoom uygulaması otomatik olarak o resme zoom yapıyor.

DeepZoom Composer gün geçtikçe tek başına ayakta durabilen ayrı bir program halini alıyor. Özellikle bu son eklenen işlevsellikler ile beraber internetten indirilebilen ücretsiz bir program olarak web siteleri hazırlarken hızlı çözümler yaratmak adına işimizi kolaylaştıracağı kesin.

Hepinize kolay gelsin!

Sunday, August 16, 2009 6:25:44 PM (GTB Standard Time, UTC+02:00)  #    Comments [2]   Silverlight 3.0  | 
 Saturday, August 15, 2009

Silverlight uygulamalarınızda harici kontroller kullanmamak elde değil. Bugün ister Silverlight SDK ile beraber gelen kontroller olsun ister Silverlight Toolkit ile beraber gelenler hepsi de neredeyse hayati öneme sahip. Tüm bu senaryoda XAP dosyamızın büyüklüğünü olabildiğince ufak tutmak da tabi bir diğer hedefimiz. Bu çerçevede zaten hali hazırdaki çoğu kontrol de ayrı kütüphanalere saklanmış durumda. Örneğin Silverlight SDK ile beraber gelen ve Visual Studio içerisinde varsayılan yüklemelerle Toolbox'a yerleşen DataGrid aslında harici bir DLL içerisinde bulunuyor. Siz herhangi bir şekilde uygulamanıza DataGrid eklediğinizde akraplanda söz konusu DLL de projenize eklenmiş oluyor. Tabi bu durum gün geçtikçe XAP dosyanızın şişmesine neden olabiliyor çünkü malum, kullandığınız tüm DLL'lerin XAP'ın içerisinde yerleştirilmek durumunda.

Yazıma böyle uzun ve konumuzla alakası pek de belli olmayan bir yazıyla başlamamın emin olun bir nedeni var :) Konumuz olan "Assembly Caching" aslında da tam bir önceki parafragta bahsettiğimiz sorunları kısmen de olsa çözmeyi planlıyor. Sonuçta eğer DataGrid kullanacaksanız DLL'ini istemciye göndermek zorundayız, bunu engelleyemeyiz fakat nasıl gönderileceği ile ilgili belki bazı değişiklikler yapabiliriz...

Yeni  bir proje yeni bir hayat....

Şimdi gelin Visual Studio'da yeni bir Silverlight projesi yaratalım ve araç çubuğundan bir DataGrid tutup hemen sahneye atın. Bu işlemi yapmanızla beraber XAML tarafında DataGrid için gerekli namespace'lerin tanımlandığını ve bazı DLL'lerin otomatik olarak referans alındığını göreceksiniz. Hemen başka kod yazmadan projenizi Build ederek XAP dosyasının da uzantısını değiştirip ZIP yapıp içerisine bir göz atalım.

DataGrid ile normal bir Silverlight projesi.
DataGrid ile normal bir Silverlight projesi.

Yukarıdaki gördüğünüz üzere bir DataGrid için toplam 4 ayrı DLL XAP dosyasına eklenmiş durumda. Özellikle DataGrid'in kendisinin bulunduğu DLL 430KB. Tabi tüm bunlar XAP içerisinde ZIP'lendiği için daha ufalıyor fakat şu anda bile XAP dosyamızın büyüklüğü 206KB. Bu arada assembly_caching_VB.dll dosyası kafanızı karıştırmasın, benim yaratmış olduğum örnek projenin adı assembly_Caching_VB olduğu için aslında o dosya projemizin kendi kodlarını içeriyor.

Peki ne yapabiliriz?

Daha önce de dediğim gibi sonuçta bu DLL'lerin istemciye gitmesi şart. Yapabileceğimiz tek şey uygulamadan bağımsız olarak bu DLL'lerin istemci tarafında önbelleklenebilmesini sağlamak. Genelde bu şekilde uygulama geliştirirken uygulamanız sürekli değişebilir fakat kullandığınız hazır kütüphanaler genelde hep aynıdır. Yani bizim DataGrid olan uygulamamız değişse ve cachlenmese de aslında DataGrid'in kendisinin bulunduğu DLL rahatlıkla daha uzun vadeli olarak cachelenebilir.

Tabi bu gibi bir Cachelemenin tarayıcı tarafından yapılabilmesi için bu DLL'lerin ayrı paketler olarak istemciye gönderilmesi gerekiyor. Bu gibi bir senaryoyu kendi kodlarımızı yazarak oluşturabiliriz fakat Silverlight 3.0 ile beraber gelen Assembly Caching mekanizması zaten bunu yapıyor. O nedenle pek de kod yazmadan hemen işimizi halledebiliriz.

Silverlight projenize sağ tuş ile tıklayarak gelen menüden "Properties" komutunu verip karşınıza çıkan ekranda "Reduce XAP Size..." seçeneğini işaretleyebilirsiniz.

Assembly Caching'i aktif hale getiriyoruz.
Assembly Caching'i aktif hale getiriyoruz.

Hemen projemizi yeni build edip ASP.NET projemizdeki ClientBin klasörü içerisine bir göz atalım. Artık ClientBin içerisinde XAP dosyamızın yanı sıra ayrı ZIP dosyaları da bulunuyor. Bu dosyalar bir önceki adımda XAP dosyası içerisindeki DLL'lerin ZIP'lenmiş halleri. Anlayacağınız söz konusu DLL'ler artık XAP içerisinde değiller ve XAP dosyamız 5KB'a düşmüş durumda. DLL'ler harici ZIP'ler şeklinde paketlenerek XAP ile aynı konuma yerleştirilmiş.

Assembly'ler ayrı ZIP'lerde...
Assembly'ler ayrı ZIP'lerde...

Ne fark etti derseniz aslında konu çok basit. Yukarıdaki şekli ile DLL'ler ayrıldığında yazılımcıların bu DLL'leri ayrı ayrı update etme şansı oluyor. Sonuç olarak sunucu tarafındaki HTTP Header'a LastModifiedDate'e bakacak olan tarayıcı taraflı cachleme mekanizmaları uygulama değişmiş olsa da DLL'leri içerisinde ZIP'ler değişmemişse DLL'lere ait ZIP'leri indirmeyecek ve önbellektekini kullanacaktır. Bu durum da tabi ki performansta ciddi bir artış yaratır.

[AppManifest.xaml / Assembly Caching Öncesi]

<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" EntryPointAssembly="assembly_caching_VB" EntryPointType="assembly_caching_VB.App" RuntimeVersion="3.0.40624.0">

  <Deployment.Parts>

    <AssemblyPart x:Name="assembly_caching_VB" Source="assembly_caching_VB.dll" />

    <AssemblyPart x:Name="System.ComponentModel.DataAnnotations" Source="System.ComponentModel.DataAnnotations.dll" />

    <AssemblyPart x:Name="System.Windows.Controls.Data" Source="System.Windows.Controls.Data.dll" />

    <AssemblyPart x:Name="System.Windows.Controls.Data.Input" Source="System.Windows.Controls.Data.Input.dll" />

    <AssemblyPart x:Name="System.Windows.Data" Source="System.Windows.Data.dll" />

  </Deployment.Parts>

</Deployment>

[AppManifest.xaml / Assembly Caching Sonrası]

<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" EntryPointAssembly="assembly_caching_VB" EntryPointType="assembly_caching_VB.App" RuntimeVersion="3.0.40624.0">

  <Deployment.Parts>

    <AssemblyPart x:Name="assembly_caching_VB" Source="assembly_caching_VB.dll" />

  </Deployment.Parts>

  <Deployment.ExternalParts>

    <ExtensionPart Source="System.ComponentModel.DataAnnotations.zip" />

    <ExtensionPart Source="System.Windows.Controls.Data.zip" />

    <ExtensionPart Source="System.Windows.Controls.Data.Input.zip" />

    <ExtensionPart Source="System.Windows.Data.zip" />

  </Deployment.ExternalParts>

</Deployment>

Bildiğiniz üzere her XAP dosyası içerisinde Assembly'lerin adreslerini tutan birer de Manifest dosyası bulunuyor. Bu manifest dosyalarına dair birer örneği yukarıda inceleyebilirisniz. Assembly Caching aktif hale getirildikten sonra artık Assembly'lerin birer ExtensionPart olarak geçtiğini ve uygun ZIP dosyalarının adreslerinin de Source olarak verilmiş olduğunu görebiliyoruz.

Ya Kendi Kütüphanalerimiz?

Dikkat ettiyseniz yukarıdaki kütüphanalerin hepsi de Microsoft kütüphanaleriydi. Aynı sistemi kullanarak Silverlight projenize kendi yarattığınız bir kütüphaneyi referans alırsanız Assembly Caching aktif olsa da DLL'inizin yine XAP dosyası içerisine yerleştirildiğini göreceksiniz. Gelin bu durumu nasıl değiştirebileceğimize göz atalım.

İlk olarak ileriki aşamalarda Assembly'miz ile ilgili bir Public Key Token'a ihtiyacıımz olacağı için Assembly'mizin Strong Name'e ihtiyacı var. Bunun için Silverlight ile aynı Solution içerisine eklediğimizi varsaydığım örnek "Silverlight Class Library" projesine sağ tuş ile tıklayarak gelen menüden "Properties" komutunu verip "Signing" tabına geçmemiz gerekiyor.

Assembly'miz Strong Name ekliyoruz.
Assembly'miz Strong Name ekliyoruz.

Uygun bir "Key File Name" verip yaratacağınız key dosyasını seçerek bu işlemi tamamlayabilirsiniz. Daha önce de bahsettiğim üzere bize esas lazım olan Public Key Token olacak. Söz konusu key'i alabilmek için Class Library'i build ettikten sonra .NET Framework SDK ile beraber gelen Strong Name Utility'yi kullanmamız şart. Visual Studio ile beraber yüklenen "Visual Studio 2008 Commant Prompt"u başlat menüsünden bulup çalıştırdıktan sonra sn -Tp Assembly Adı şeklinde gerekli komutu çalıştırabilirsiniz.

Bize "Public Key Token" lazım!
Bize "Public Key Token" lazım!

Şimdi aldığımız bu Token ile aşağıdaki formatta bir XML dosyası yaratıp DLL'imiz ile aynı yere yani bin klasöründe Release altına koymalıyız.

[OrnekLibrary_VB.extmap.xml]

<?xml version="1.0"?>

<manifest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

          xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <assembly>

    <name>OrnekLibrary_VB</name>

    <version>1.0.0.0</version>

    <publickeytoken>266fe0f58a4900bb</publickeytoken>

    <relpath>OrnekLibrary_VB.dll</relpath>

    <extension downloadUri="OrnekLibrary_VB.zip" />

  </assembly>

</manifest>

Gördüğünüz üzere dosya içerisinde DLL'in adı, sürüm numarası, Assembly adı ve Token bulunuyor. Ayrıca extension tagına ait downloadUri özelliğinde de dosyanın adresini veriyoruz. Adres kısmında eğer yukarıdaki şekilde bir dosya adı verirseniz Visual Studio sizin adınızda DLL'i söz konusu dosya adı ile ZIP'ler ve ClientBin içerisinde yerleştirir. İsterseniz downloadUri'ye harici bir adres de verebilirsiniz. Hatta bu adres XAP'ın bulunduğu alan adı haricinde de olabilir. Böyle bir durumda Visual Studio ZIP üretmeyecektir. Unutmayın harici bir alan adında caching yapacaksanız kesinlikle o alan adında ZIP dosyaları için uygun ClientAccessPolicy.xml dosyalarının da oluşturulmuş olması gerekir, aksi halde cross-domain-request yapılamaz.

Dosyamız hazır olduğuna göre onu DLL ile aynı yere kaydedebilirsiniz. Kaydederken Assembly Adı ve sonrasında da extmap.xml yazmanız şart.

Artık herşeyimiz hazır olduğuna göre Silverlight projemizi build edebiliriz. ClientBin içerisinde DLL'inizi ayrıca ZIPlenmiş olarak göreceksiniz!

Neden kullanayım?

Assembly Caching kullanmak uygulamanızın açılışını hızlandırmaz. Eğer hedef makinede daha önce uygulama açılmamış ise yine tüm kütüphanaler indirileceği için birşey değişmeyecektir. Fakat ikinci ve sonraki açılışlarda tarayıcı harici kütüphanaleri cachlediği için uygulamanız değişse de çok daha hızlı açılacaktır. Siz reference DLL'leri değiştirmedikçe ve harici kütüphanalere dair dosyalar yenilenmedikçe cacheleme tarayıcı tarafından yapılacaktır.

Özellikle harici bir alan adında kütüphaneleri önbelleklemek belki de aynı kütüphanaleri farklı uygulamalarda kullananlar için uygun olabilir. Böylece merkezi bir yönetim de kısmen sağlanmış olacaktır. Tüm bunları yaparken de ek kod yazmak zorunda kalmamak güzel bir avantaj olsa gerek.

Hepinize kolay gelsin!

Dikkat: Assembly Caching Silverlight 3 Beta iken çok daha farklı çalışıyordu. Bu konuda internette bir araştırma yaptığınızda karşılaşacağınız kaynakların çoğu size Assembly Caching'in sadece MS kütüphanaleri için yapılabileceğini ve kütüphanalerin MS tarafından host edildiğini söylemeyebilir. Silverlight 3 Beta'yken durum böyleydi  fakat artık değil. MS kütüphanaleri dahil olmak üzere hostingi sizin yapmanız gerekiyor ayrıca yukarıda da gördüğünüz üzere kendi kütüphanalerinizde de bu özelliği kullanabiliyorsunuz.

Örneğe ait kaynak kodlar - 15082009_6.rar (986,18 KB)

Saturday, August 15, 2009 3:26:52 PM (GTB Standard Time, UTC+02:00)  #    Comments [1]   Silverlight 3.0  | 
 Friday, August 14, 2009

Silverlight 3.0 ile beraber tarayıcı içerisinde Multitouch desteği de geldi. Şu an için sadece Windows 7 ve Internet Explorer üzerinde sunulabilen bu deneyimi yaratmak için WPF tarafından biraz daha farklı tekniklerle ilerlemek gerekiyor. Bu yazımıda Silverlight 3.0 tarafında Multitouch API'larına göz attıktan sonra bir Manipulation örneği yapacağız.

Silverlight ve Multitouch

Silverlight içerisinde herhangi bir şekilde gerçekleşen Touch durumunu algılamak için kullanabileceğimiz tek bir event bulunuyor. Söz konusu event'ın adı Touch.FrameReported şeklinde. Bu eventa bağlanan bir event listener'ın argümanı ile beraber gelen bilgiler bizim için fazlası ile yeterli olacaktır.

[VB]

    Public Sub New()

        InitializeComponent()

        AddHandler Touch.FrameReported, AddressOf Touched

    End Sub

 

    Sub Touched(ByVal sender As Object, ByVal e As System.Windows.Input.TouchFrameEventArgs)

      

    End Sub

[C#]

        public MainPage()

        {

            InitializeComponent();

            Touch.FrameReported += new TouchFrameEventHandler(Touch_FrameReported);

        }

 

        void Touch_FrameReported(object sender, TouchFrameEventArgs e)

        {

 

        }

Event'ımızın TouchFrameEventArgs'ında birçok değerli veri bulunuyor. Örneğin her bir touch için birer Timestamp alabiliyoruz. Timestamp'i özünde eski OLEAutomationDate'lere benzetebilirsiniz. Size farklı touch işlemleri arasında süreyi rahatlıkla hesaplayabilmeniz için integer Timestamp'ler döndürüyor. Bunun haricince argüman tarafında önemli üç adet metod bulunuyor. Bunlardan ilki GetPrimaryTouchPoint metodu. Touch işlemi esnasından bir yada daha çok noktadan ekrana dokunulabileceği için ilk dokunma noktası Primary denerek GetPrimaryTouchPoint aracılığı ile bize iletiliyor. Ayrıca GetTouchPoints metodu da tüm dokunulan noktaların bir listesini getirir. TouchPoint tipinde gelen bu noktalara ait ek bilgileri de TouchPoint sınıfı üzerinden alabilirsiniz. Örneğin dokunulan noktanın pozisyonu, alanı, hatta TouchDevice üzerinden de unique ID'sini elde etmek mümkün. Son olarak argüman üzerindeki SuspendMousePromotionUntilTouchUp metodu de Touch işlemleri bitene yani kullanıcı tüm parmaklarını ekrandan çekene kadar fare kullanımını engelleyecektir.

Tüm bu hikaye içerisinde en önemli şeylerden biri Touch işleminin hangi aşamada olduğu. Toplam üç farklı aşama mevcut. Bunlardan ilgi kullanıcının ekrana değdiği ilk an (Down), bir sonraki kullanıcının parmağını ekranda gezdirdiği süre (Move), son olarak (Up) kullanıcının parmağını ekranda çektiği an şeklinde üç farklı aksyon bulunuyor. Bu her aksyon TouchPoint nesnelerinin Action değişkeninde bir enumaration olarak bizi bekliyor. Herhangi bir şekilde bize raporlanan TouchPoint'in pozisyonunu alabildiğimiz gibi o anda ilk dokunma mı, bırakma mı yoksa sürekli dokunma mı oluştuğunu takip edebiliyoruz. Unutmadan hatırlatmak fayda var, kullanıcı parmağını ekranda oynatmadan tutarsa da bu bir Move aksyonu olarak algılanıyor.

Şimdi gelin manipülasyon örneğimize geçelim ve tüm bunların birlikte nasıl kullanıldığına göz atalım. Manipülasyon örneğinde bildiğiniz üzere amacımız ekrana basit bir resim koyarak onun kullanıcı tarafından iki parmak kullanılarak boyutlandırılabilmesini, taşınabilmesini ve çevrilebilmesini sağlamaktır. WPF tarafından farklı olarak daha Silverlight için etrafta hazırlanmış bir ManipulationProcessor bulunmadığı için tüm işlemleri bizim yapmamız gerekecek. İlk aşamada gelin uygulamamızın ekranını hazırlayarak konuya girelim.

[XAML]

  <Grid x:Name="LayoutRoot">

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

            <Image.RenderTransform>

                <TransformGroup>

                    <ScaleTransform x:Name="ImageScale" />

                    <TranslateTransform x:Name="ImageTranslate" />

                    <RotateTransform x:Name="ImageRotate" />

                </TransformGroup>

            </Image.RenderTransform>

        </Image>

    </Grid>

Uygulama ekranımızda basit bir Image nesnesi bulunuyor. Bu Image nesnesinin durumda göre pozisyonunu, dönüş açısını ve boyutunu değiştireceğimiz için uygun Transform nesnelerini de içerisinde yerleştirerek gerekli isimlendirmeleri de yaptık. Böylece rahatlıkla kod tarafından bu işlemleri halledebiliriz. Şimdi sıra geldi Touch ile ilgili gerekli işlemleri arka tarafta yapmaya.

Hemen makalemizin başında da gördüğümüz üzere FrameReported eventına bir event-listener bağlıyoruz.

[VB]

    Public Sub New()

        InitializeComponent()

        AddHandler Touch.FrameReported, AddressOf Touched

    End Sub

[C#]

        public MainPage()

        {

            InitializeComponent();

            Touch.FrameReported += new TouchFrameEventHandler(Touch_FrameReported);

        }

Amacımız FrameReported içerisinde ilk olarak en az iki tane TouchPoint olurkenki durumları yakalamak. Kullanıcının resmi tutup manipulasyon yapabilmesi için en az iki parmağının ekrana dokunuyor olması gerek. Ayrıca Action olarak da parmaklarını ilk dokundurduğu anda değil parmaklarını sürüklerken işlem yapmamız şart. Tüm bu süreçleri kontrol ederken sürekli olarak parmakların bir önceki durumu ile şu anki durumu arasındaki farklara göre gerekli hesaplmaları yaparak resmimize yansıtacağız. Aslında tüm bu sürece gelişmiş bir drag&drop gözü ile bakabilirsiniz.

[VB]

    Dim _FirstTouch As Point = New Point(0, 0)

    Dim _SecondTouch As Point = New Point(0, 0)

Uygulamamızda ilk olarak yukarıdaki şekli ile iki tane Point değişkenini global olarak tanımlıyoruz. Bu değişkenler sürekli olarak kullanıcının parmaklarının bir önceki koordinatlarını saklayacak. Böylece biz de o anki koordinatlar ile bir önceki arasında farkları yakalayabileceğiz.

[VB]

    Sub Touched(ByVal sender As Object, ByVal e As System.Windows.Input.TouchFrameEventArgs)

        If Not e.GetPrimaryTouchPoint(Me) Is Nothing Then

            Dim IlkDokunus As TouchPoint

            Dim IkinciDokunus As TouchPoint

 

            IlkDokunus = e.GetPrimaryTouchPoint(Me)

            If IlkDokunus.Action = TouchAction.Down Then

                _FirstTouch = New Point(0, 0)

                _SecondTouch = New Point(0, 0)

            ElseIf IlkDokunus.Action = TouchAction.Move Then

                If e.GetTouchPoints(Me).Count > 1 Then

                    IkinciDokunus = e.GetTouchPoints(Me)(1)

                    ''BURADA MANIPULATION YAPILACAK

                End If

            ElseIf IlkDokunus.Action = TouchAction.Up Then

               _FirstTouch = New Point(0, 0)

               _SecondTouch = New Point(0, 0)

            End If

 

        End If

    End Sub

[C#]

void Touch_FrameReported(object sender, TouchFrameEventArgs e)

        {

            if ((e.GetPrimaryTouchPoint(this) != null))

            {

                TouchPoint IlkDokunus = default(TouchPoint);

                TouchPoint IkinciDokunus = default(TouchPoint);

 

                IlkDokunus = e.GetPrimaryTouchPoint(this);

                if (IlkDokunus.Action == TouchAction.Down)

                {

                    _FirstTouch = new Point(0, 0);

                    _SecondTouch = new Point(0, 0);

                }

                else if (IlkDokunus.Action == TouchAction.Move)

                {

                    if (e.GetTouchPoints(this).Count > 1)

                    {

                        IkinciDokunus = e.GetTouchPoints(this)[1];

                        //BURADA MANIPULATION YAPILACAK

                    }

                }

                else if (IlkDokunus.Action == TouchAction.Up)

                {

                    _FirstTouch = new Point(0, 0);

                    _SecondTouch = new Point(0, 0);

                }

            }

        }

Yukarıdaki ilk kodumuz karışık gibi gözükse de aslında basit birkaç koşul kontrolünden farklı değil. İlk olarak GetPrimaryTouchPoint ile hali hazırda ilk TouchPoint var mı yok mu kontrolünü gerçekleştiriyoruz. Eğer varsa ikinci amacımız söz konusu Touch işleminin Action'una göre işlem yapmak. Eğer ilk TouchPoint'imize ait Action Up veya Down ise yani kullanıcı parmağını ilk defa dokunduruyor veya çekiyorsa hemen global değişkenlerimizi sıfırlıyoruz. Böylece bir sonraki Touch ile sürükleme işleminde gerekli kontrolleri yaparak işlemleri sıfırdan başlatabiliriz. Fakat unutmayın ki daha bu sadece ilk TouchPoint yani kullanıcının ilk parmağı! Belki de hiç ikinci bir parmak değimedi ekrana. Böyle bir durumda manipülasyon yapamayacağımız için ikinci parmak var mı diye kontrol etmemiz şart.

Eğer ilk TouchPoint'in aksyonu Move ise bu sefer hemen GetTouchPoints ile toplam parmak sayısını alıyoruz. Eğer bu sayı birden yüksekse belli ki ekranda iki parmak var. Her iki parmağa ait TouchPoint'lerini ayrı birer değişkene aldıktan sonra sıra gelecek bu parmakların koordinatlarına göre hesaplamaları yapmaya.

[VB]

                    Dim FirstTouch As Point = IlkDokunus.Position

                    Dim SecondTouch As Point = IkinciDokunus.Position

 

                    If _FirstTouch.X <> 0 Then

                        ''ESAS OLAY BURADA :)

                    End If

 

                    _FirstTouch = FirstTouch

                    _SecondTouch = SecondTouch

[C#]

                        Point FirstTouch = IlkDokunus.Position;

                        Point SecondTouch = IkinciDokunus.Position;

 

                        if (_FirstTouch.X != 0)

                        {

                           //ESAS OLAY BURADA :)

                        }

 

                        _FirstTouch = FirstTouch;

                        _SecondTouch = SecondTouch;

Ele aldığımız TuchPoint'lerden Position alarak birer değişkene aktarıyoruz. Sonrasında tabi ki bu pozisyonları bir önceki pozisyonlar ile karşılaştıracağımız için aslında "bir önceki pozisyon" diye birşey var mı onu kontrol etmemiz gerekiyor. Eğer varsa gerekli işlemleri yapacağız yoksa eldeki pozisyonu kenara atacağız ki bir sonraki işlemde bu pozisyona göre değişiklikleri hesaplayabilelim.

[VB]

Dim ScaleDelta = (((((FirstTouch.X - SecondTouch.X) ^ 2) + ((FirstTouch.Y - SecondTouch.Y) ^ 2)) ^ (1 / 2)) / _

                             ((((_FirstTouch.X - _SecondTouch.X) ^ 2) + ((_FirstTouch.Y - _SecondTouch.Y) ^ 2)) ^ (1 / 2))) - 1

[C#]

 double ScaleDelta = (Math.Sqrt(Math.Pow(FirstTouch.X - SecondTouch.X, 2) + Math.Pow(FirstTouch.Y - SecondTouch.Y, 2)) /

                                Math.Sqrt(Math.Pow(_FirstTouch.X - _SecondTouch.X, 2) + Math.Pow(_FirstTouch.Y - _SecondTouch.Y, 2))) - 1;

İlk olarak boyutlandırma işlemi ile başlayalım. Resmimizin boyutunun ne kadar değişeceğini 1 üzerinden orantılayarak vermemiz gerekiyor ki ScaleTransform'un ScaleX ve ScaleY'sine aktarabilelim. Gelen ScaleDelta'yı sonrasında bu ScaleX ve ScaleY'ye ekleyeceğiz o nedenle bulduğumuz sonucu 1'den çıkartıyoruz ki normal boyuta göre farkı bulalım.

Resmin boyut değişikliği ile ilgili hesaplamayı yaparken izlediğimiz yol her iki parmağın bir önceki pozisyonlarına göre aralarındaki mesafeyi bulup sonra da şu an pozisyonlara göre mesafeleri arasında oranı bir üzerinden hesaplamak. Basit bir hipotenüs hesaplaması gözü ile bakarsak elimizdeki iki noktadan bir üçgen oluşturup hipotenüsü bulmamız mesafe için yeterli olacaktır. Üçgenin yatay kenarı ve dikey kenarının uzunlukları için noktaların X ve Y koordinatları arasındaki farkları kullanabiliyoruz.

[VB]

Dim PositionPoint = New Point(((FirstTouch.X + SecondTouch.X) / 2) - ((_FirstTouch.X + _SecondTouch.X) / 2), _

                                                 ((FirstTouch.Y + SecondTouch.Y) / 2) - ((_FirstTouch.Y + _SecondTouch.Y) / 2))

[C#]

 var PositionPoint = new Point(((FirstTouch.X + SecondTouch.X) / 2) - ((_FirstTouch.X + _SecondTouch.X) / 2),

                                ((FirstTouch.Y + SecondTouch.Y) / 2) - ((_FirstTouch.Y + _SecondTouch.Y) / 2));

Resmin pozisyonu ile ilgili değişikliği hesaplamak biraz daha kolay. İki parmak arasındaki doğrunun orta noktasını bularak bir önceki orta nokta ile şimdiki orta nokta arasındaki farkı almak pozisyon değişikliğini yakalamak için yeterli olacaktır.

[VB]

Dim AngleDelta = (Math.Atan2(FirstTouch.Y - SecondTouch.Y, FirstTouch.X - SecondTouch.X) * 180 / Math.PI) - _

                             (Math.Atan2(_FirstTouch.Y - _SecondTouch.Y, _FirstTouch.X - _SecondTouch.X) * 180 / Math.PI)

[C#]

 var AngleDelta = (Math.Atan2(FirstTouch.Y - SecondTouch.Y, FirstTouch.X - SecondTouch.X) * 180 / Math.PI) -

                                (Math.Atan2(_FirstTouch.Y - _SecondTouch.Y, _FirstTouch.X - _SecondTouch.X) * 180 / Math.PI);

İki keranın bildiğiniz bir üçgenin iç açılarından birini nasıl bulursunuz? :) Bazılarınızı yıllar önceki lise yıllarına döndürdüğümün farkındayım. Dikey mesafe (Y) ve yatay mesafeyi (X) verip bir noktanın x eksenine göre (0,0)'dan açısını radyan olarak veren Math.Atan2 metodunu kullanarak parmaklarımızla oluşturduğumuz çizginin orta noktasının (0,0)'a göre x ekseninden açısını alabiliyoruz. Tabi radyanı da bildiğimiz açıya çevirmek için 180'le çarpıp PI'ye bölüyoruz. Eski pozsiyonlara göre hesapladığımız açı ile şimdiki açı arasındaki fark da tam olarak bulmak istediğimiz şeydi.

[VB]

                        ImageScale.ScaleX += ScaleDelta

                        ImageScale.ScaleY += ScaleDelta

                        If ImageScale.ScaleX < 0 Then ImageScale.ScaleX = 0

                        If ImageScale.ScaleY < 0 Then ImageScale.ScaleY = 0

 

                        ImageTranslate.X += PositionPoint.X

                        ImageTranslate.Y += PositionPoint.Y

 

                        ImageRotate.Angle += AngleDelta

[C#]

                            ImageScale.ScaleX += ScaleDelta;

                            ImageScale.ScaleY += ScaleDelta;

                            if (ImageScale.ScaleX < 0) ImageScale.ScaleX = 0;

                            if (ImageScale.ScaleY < 0) ImageScale.ScaleY = 0;

 

                            ImageTranslate.X += PositionPoint.X;

                            ImageTranslate.Y += PositionPoint.Y;

 

                            ImageRotate.Angle += AngleDelta;

Sıra geldi tüm bu hesaplamalarla bulduğumuz değerleri Image nesnesminde Transform'lara aktarmaya. Sadece Scale için dikkat etmemiz gereken şey eksi değer vermemek. Aksi halde resim ters dönecektir.

Kodumuz bu kadar. Manipülasyon işlemimizi de tamamladık ve artık projemiz çalışmaya hazır.

Hepinize kolay gelsin.

Örneklere ait kaynak kodlar. - 14082009_1.rar (73,14 KB)

Friday, August 14, 2009 1:29:53 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   MultiTouch | Silverlight 3.0  | 
 Thursday, August 13, 2009

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.

Örneklere ait kaynak kodları - 13082009_1.rar (1,59 MB)

Thursday, August 13, 2009 5:47:05 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   MultiTouch | Windows 7 | WPF  | 
 Wednesday, August 12, 2009

Windows 7 Taskbarındaki en kuvvetli araçlardan biri JumpList'ler. Taskbardaki herhangi bir programın üzerine sağ tuş ile tıklarsanız karşınızda yeni bir menü gelecektir. Bu menü uygulama bazlı olarak değişebileceği gibi tamamen özelleştirilebiliyor da. Örneğin hızlıca Outlook'u inceleyecek olursan Windows 7 ile beraber Outlook'a ait JumpList menüsünde aslında hayatımızı gerçekten kolaylaştırabilecek kısayollar ile karşılaşabiliriz.

Outlook'a ait JumpList karşınızda...
Outlook'a ait JumpList karşınızda...

Gördüğünüz üzere JumpList üzerinden neredeyse program içerisindeki ana bölümlere ulaşmak veya en sık yapılan işlemlere hızlıca erişmek mümkün. İşte biz de programlarımıza bu özellikleri kazandırabiliyoruz. Bunun için her zamanki gibi WindowsAPICodePack ile beraber gelen Microsoft.WindowsAPICodePack.dll ve Microsoft.WindowsAPICodePack.Shell.dll'i uygulamalarımızda referans almamız gerekiyor. Gelin yeni bir WPF uygulaması yaratarak JumpList'e nasıl hükmedebileceğimize bir göz atalım.

[VB]

        Dim JumpL = JumpList.CreateJumpList()

 

        Dim JumpListGorev As New JumpListLink("C:\Windows\notepad.exe", "Not al!")

        JumpL.AddUserTasks(JumpListGorev)

        JumpL.AddUserTasks(New JumpListSeparator)

        JumpL.AddUserTasks(JumpListGorev)

        JumpL.Refresh()

Her uygulamada bir defaya mahsus olarak JumpList sınıfı üzerinden CreateJumpList metodunu çağırmak durumundayız .Tavsiyem bu metodu uygulamanızda bir Property haline getirerek duruma göre ilk çağrılışta nesnenin yaratılması. Artık JumpList'imiz elimizde olduğuna göre hemen JumpList'e farklı görevler ekleyebiliriz. Örneğimizdeki JumpListLink nesnesinin amacı uygulama dışındaki bir başka uygulamayı çalıştırmak. Basit bir şekilde Windows ile beraber gelen NotePad'i çağıracağız. JumpListLink yaratırken verdiğimiz ikinci parametre JumpList üzerinde bu komut ile eşleştirilecek olan metni tanımlıyor.

Son olarak bir önceki adımda yarattığımız JumpList nesnemize elimizdeki JumpListLink'i AddUserTasks komutu ile ekliyoruz. Örnek olması amacı ile yukarıdaki kod içerisinde aynı nesneyi iki kere JumpList'e ekledim. Arada da ayrıca bir JumpListSeparator kullandım. Böylece siz de JumpList'lerinizde eklediğiniz farklı komutları bu şekilde gruplayarak birbirinden ayırabilirsiniz.

Kendimize özel bir JumpList örneği.
Kendimize özel bir JumpList örneği.

JumpListLink yaratırken harici dosyalara link vermemizin arkasında aslında anlamlı nedenler var. JumpList'leri uygulamanızın bir parçası gibi gözükse de aslında uygulamanız yokken de Taskbar'da bulunabiliyorlar. Örneğin uygulamanızı kullanıcıların Taskbar'a pinlediğini düşünelim. Bu durumda uygulamanız kapalı olsa da ikonu taskbarda gözükecektir. Aynı şekilde uygulamanız kapalıyken ikonuna sağ tıklanırsa JumpList de gösterilecektir. Hatta uygulamanızın en son JumpList'i ayarladığı şekli ile gözükecektir!

Bizim örneğimizdeki uygulamayı taskbara pinlerseniz, uygulamayı kapatsanız da doğrudan Notepad'i açabilirsiniz ve bu durumdan bizim uygulamamızın haberi bile olmaz. Bu nedenle eğer kendi uygulamalarınıza parametre gönderecekseniz uygun argümanlar ile uygulamanızı hedeflemek durumundasınız.

Örnek kodumuzun son satırında da JumpList'e ait Refresh metodunu çağırıyoruz. Böylece gerekli ayarlar Windows tarafından kaydediliyor.

Son açılan dosyalar? Sık açılan dosyalar?

İsterseniz JumpList üzerinde uygulamanız tarafından sıkça açılan dosyaların veya en son açılan dosyaların gösterilmesini de sağlayabilirsiniz. Bunun için tabi ki uygulamanızın çalıştığı sistemde söz konusu dosya uzantılarının uygulamanız ile eşleştirilmiş olması gerekiyor. Merak etmeyin, programınızı söz konusu uzantıların varsayılan programı olarak ayarlamak zorunda değilsiniz, sadece eşleştirilmiş olması yeterli olacaktır.

Expression Web ile açılan son dosyalar...
Expression Web ile açılan son dosyalar...

Sonrasında aşağıdaki gibi bir kod ile bu işlevselliği ister "son açılan dosyalar" (Recent) ister "sıkça açılan dosyalar" (Frequent) modunda aktif hale getirebilirsiniz.

[VB]

        JumpL.KnownCategoryToDisplay = JumpListKnownCategoryType.Frequent

        JumpL.Refresh()

Eğer isterseniz siz de JumpList içerisinde bölümler yaratabilir ve bu bölümlere ayrı ayrı isimler verebilirsiniz. Sonrasında her bölüme istediğiniz kadar JumpListLink ekleme şansınız olacaktır.

[VB]

        Dim Kategori As New JumpListCustomCategory("Özel Kategori")

        Dim JumpListGorev As New JumpListLink("C:\Windows\notepad.exe", "Not al!")

        Kategori.AddJumpListItems(JumpListGorev)

        JumpL.AddCustomCategories(Kategori)

 

        JumpL.Refresh()

İlk satırda yarattığımız JumpListCustomCategory nesnesine her zamanki gibi AddJumpListItems ile yeni nesneler ekleyebiliriz. Tüm bunlar söz konusu kategori içerisinde listelenecektir. Tüm bu işlemlerden sonra JumpList üzerinden Refresh metodunu çağırmayı unutmayın.

Özel kategorisi ile birlikte JumpList'imizin son hali.
Özel kategorisi ile birlikte JumpList'imizin son hali.

Hepinize kolay gelsin.

Örnek uygulama kaynak kodları - 12082009_8.rar (566,92 KB)

Wednesday, August 12, 2009 5:02:42 PM (GTB Standard Time, UTC+02:00)  #    Comments [1]   Windows 7 | WPF  | 

Bir uygulama içerisinden birden çok pencerenin olduğu yılları hatırlarsınız dersem belki de "O yıllar geride mi kaldı?" diyebilirsiniz. MDI formları yıllarda uygulamalarımızın en önemli parçaları oldular. Kullanıcı deneyimi olarak olabildiğince onlardan kaçsak da farklı şekillerde de olsalar hep karşımıza çıkıyorlar. Sadece MDI formları değil aslında TAB içeren tüm mekanizmalara konumuza dahil edebiliriz. Örneğin Internet Explorer 8 içerisinde de birden çok tab açarak aslında apayrı işler yapabiliyoruz. Aynı program içerisinde çalışsak da aslında yaşanan deneyim son kullanıcı açısından ayrı bir program kullanmaktan çok da farklı olmuyor.

Internet Explorer içerisinden farklı tablar taskbarda...
Internet Explorer içerisinden farklı tablar taskbarda...

Şöyle bir düşünsek acaba bizim de hazırladığımız programlarda kullanıcıların böyle geçiş yapabilecekleri ekranlar var mıdır? Aynı uygulama içerisinden bir anda birden çok ekran üzerinde çalışan kullanıcılara taskbar üzerinden ulaşarak acaba bu ekranlara taskbardan da ayrı ayrı ulaşabileceklerini belirtebilsek güzel olmaz mıydı? Gelin bu durumda minik bir örnek ile bu işlevselliği programlarımıza nasıl katabileceğimize göz atalım.

Ön Hazırlıklar

Örnek WPF uygulamamızı yarattığımız gibi WindowsAPICodePack ile beraber gelen Microsoft.WindowsAPICodePack.dll ve Microsoft.WindowsAPICodePack.Shell.dll'i referans olarak projemize eklemeliyiz. Yarattığımız bu yeni WPF projesine ayrıca System.Drawing'i de referans alıp sonrasında XAML tarafına geçelim.

[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 x:Name="Resim" Source="Koala.jpg" />

        <Image x:Name="Resim2" Source="Desert.jpg" />

    </Grid>

</Window>

Yukarıdaki şekli ile uygulamamızın ana ekranında iki tane resim yer alacak. Bu resimler birbirlerinin üstündeler yani her zaman uygulama ekranında normal şartlarda sadece tek resim gözükebilir. Bu resimleri uygulamalarınız içerisinde farklı ekranlar olarak kabul edebilirsiniz. Amacımız kullanıcılar taskbar üzerinden bu iki resim arasında geçiş yapabilmelerini ve bu iki resmi sanki uygulamanın iki ayrı arayüzüymüş gibi görebilmelerini sağlamak.

[VB]

        If TaskbarManager.Instance.TabbedThumbnail.GetThumbnailPreview(Resim) Is Nothing Then

            Dim GenT = Resim.TransformToVisual(Application.Current.MainWindow)

            Dim Nokta As Point = GenT.Transform(New Point(0, 0))

            Dim Thumbnail As New TabbedThumbnail(Application.Current.MainWindow, Resim, New Vector(Nokta.X, Nokta.Y))

            Thumbnail.Tooltip = "Bu bir foto!"

            AddHandler Thumbnail.TabbedThumbnailActivated, AddressOf Thumbnail_TabbedThumbnailActivated

            TaskbarManager.Instance.TabbedThumbnail.AddThumbnailPreview(Thumbnail)

        End If

[C#]

            if (TaskbarManager.Instance.TabbedThumbnail.GetThumbnailPreview(Resim) == null) {

                var GenT = Resim.TransformToVisual(Application.Current.MainWindow);

                Point Nokta = GenT.Transform(new Point(0, 0));

                TabbedThumbnail Thumbnail = new TabbedThumbnail(Application.Current.MainWindow, Resim, new Vector(Nokta.X, Nokta.Y));

                Thumbnail.Tooltip = "Bu bir foto!";

                Thumbnail.TabbedThumbnailActivated += Thumbnail_TabbedThumbnailActivated;

                TaskbarManager.Instance.TabbedThumbnail.AddThumbnailPreview(Thumbnail);

            }

Kodumuz biraz karışık gibi gözükebilir fakat aslında epeyce basit. Gelin satır satır inceleyelim. İlk olarak IF içerisinde kontrol ettiğimiz noktadan bahsedelim. Bizim örneğimizde Resim nesnesi için Toolbar'da bir Thumbnail yaratacağız o nedenle daha önce yaratılıp yaratılmadığını kontrol etmekte fayda var. Kontrol işlemini GetThumbnailPreview metodu ile yapabiliyoruz. Eğer daha önce thumbnail yaratılmamış ise kodumuz çalışmaya devam edecek.

GenT ve Nokta değişkenlerimiz aracılığı ile aslında uygulama içerisinde Thumbnail olarak kullanacağımız elementin uygulama penceresine göre offsetini hesaplıyoruz. Bu bilgiyi bir Vector nesnesi olarak bir sonraki adımda TabbedThumbnail yaratırken kullanmak zorundayız. TabbedThumbnail nesnesi yaratmak aslında işlemin sonuna geldiğimizi de gösteriyor. Bu nesneyi yarattıktan sonra geriye bir tek onu taskbara eklemek kalıyor. TabbedThumbnail yaratırken parametre olarak uygulamamızın hangi penceresine eklediğimizi ve görsel nesnemizi (Resim) verirken son olarak da görsel nesnenin pencereye göre offsetini veriyoruz. TabbedThumbnail 'in isterseniz Tooltip ve Title özelliklerini de set ederek Taskbar'da gözükmelerini sağlayabilirsiniz.

Son adım yarattığımız TabbedThumbnail 'in kullanıcı tarafından seçildiğinde çalıştırılacak olan event-listener'ını eklemek. Bunun için TabbedThumbnail nesnesinin TabbedThumbnailActivated adında bir event'ı bulunuyor. Son olarak event bağlamasını da bitirdiğimize göre artık AddThumbnailPreview ile TabbedThumbnail 'imizi taskbara ekleyebiliriz.

Tek uygulama, taskbarda iki thumbnail!
Tek uygulama, taskbarda iki thumbnail!

Yukarıdaki şekli ile uygulamamızdaki iki farklı resim için de TabbedThumbnail yaratarak Taskbara'a ekledikten sonra tek yapmamız gereken her iki TabbedThumbnail'in de event listener'larında uygulama arayüzünde gerekli değişiklikleri yapmak. Aşağıda uygulamanın tam kodunu bulabilirsiniz.

Hepinize kolay gelsin.

[VB]

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

        If TaskbarManager.Instance.TabbedThumbnail.GetThumbnailPreview(Resim) Is Nothing Then

            Dim GenT = Resim.TransformToVisual(Application.Current.MainWindow)

            Dim Nokta As Point = GenT.Transform(New Point(0, 0))

            Dim Thumbnail As New TabbedThumbnail(Application.Current.MainWindow, Resim, New Vector(Nokta.X, Nokta.Y))

            Thumbnail.Tooltip = "Bu bir foto!"

            AddHandler Thumbnail.TabbedThumbnailActivated, AddressOf Thumbnail_TabbedThumbnailActivated

            TaskbarManager.Instance.TabbedThumbnail.AddThumbnailPreview(Thumbnail)

        End If

        If TaskbarManager.Instance.TabbedThumbnail.GetThumbnailPreview(Resim2) Is Nothing Then

            Dim GenT = Resim.TransformToVisual(Application.Current.MainWindow)

            GenT = Resim2.TransformToVisual(Application.Current.MainWindow)

            Dim Nokta As Point = GenT.Transform(New Point(0, 0))

            Dim Thumbnail As New TabbedThumbnail(Application.Current.MainWindow, Resim2, New Vector(Nokta.X, Nokta.Y))

            Thumbnail.Tooltip = "Bu başka bir foto!"

            AddHandler Thumbnail.TabbedThumbnailActivated, AddressOf Thumbnail2_TabbedThumbnailActivated

            TaskbarManager.Instance.TabbedThumbnail.AddThumbnailPreview(Thumbnail)

        End If

    End Sub

 

    Private Sub Thumbnail_TabbedThumbnailActivated(ByVal sender As Object, ByVal e As Microsoft.WindowsAPICodePack.Taskbar.TabbedThumbnailEventArgs)

        Canvas.SetZIndex(Resim2, 10)

        Canvas.SetZIndex(Resim, 11)

    End Sub

 

    Private Sub Thumbnail2_TabbedThumbnailActivated(ByVal sender As Object, ByVal e As Microsoft.WindowsAPICodePack.Taskbar.TabbedThumbnailEventArgs)

        Canvas.SetZIndex(Resim, 10)

        Canvas.SetZIndex(Resim2, 11)

    End Sub

[C#]

        private void Window1_Loaded(object sender, RoutedEventArgs e)

        {

            if (TaskbarManager.Instance.TabbedThumbnail.GetThumbnailPreview(Resim) == null) {

                var GenT = Resim.TransformToVisual(Application.Current.MainWindow);

                Point Nokta = GenT.Transform(new Point(0, 0));

                TabbedThumbnail Thumbnail = new TabbedThumbnail(Application.Current.MainWindow, Resim, new Vector(Nokta.X, Nokta.Y));

                Thumbnail.Tooltip = "Bu bir foto!";

                Thumbnail.TabbedThumbnailActivated += Thumbnail_TabbedThumbnailActivated;

                TaskbarManager.Instance.TabbedThumbnail.AddThumbnailPreview(Thumbnail);

            }

            if (TaskbarManager.Instance.TabbedThumbnail.GetThumbnailPreview(Resim2) == null) {

                var GenT = Resim.TransformToVisual(Application.Current.MainWindow);

                GenT = Resim2.TransformToVisual(Application.Current.MainWindow);

                Point Nokta = GenT.Transform(new Point(0, 0));

                TabbedThumbnail Thumbnail = new TabbedThumbnail(Application.Current.MainWindow, Resim2, new Vector(Nokta.X, Nokta.Y));

                Thumbnail.Tooltip = "Bu başka bir foto!";

                Thumbnail.TabbedThumbnailActivated += Thumbnail2_TabbedThumbnailActivated;

                TaskbarManager.Instance.TabbedThumbnail.AddThumbnailPreview(Thumbnail);

            }

        }

 

        private void Thumbnail_TabbedThumbnailActivated(object sender, Microsoft.WindowsAPICodePack.Taskbar.TabbedThumbnailEventArgs e)

        {

            Canvas.SetZIndex(Resim2, 10);

            Canvas.SetZIndex(Resim, 11);

        }

 

        private void Thumbnail2_TabbedThumbnailActivated(object sender, Microsoft.WindowsAPICodePack.Taskbar.TabbedThumbnailEventArgs e)

        {

            Canvas.SetZIndex(Resim, 10);

            Canvas.SetZIndex(Resim2, 11);

        }

Örneklere ait kaynak kodlar - 12082009_3.rar (3,23 MB)

Wednesday, August 12, 2009 5:23:51 AM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Windows 7  | 
 Tuesday, August 11, 2009

Son iki gündir Microsoft Yaz Okulu'nun İstanbul ayağında Silverlight anlatıyordum. İstanbul yaz okulu öğrencileri doğrudan Microsoft ofisinden olmanın tadını çıkartmıyorlar dersem yalan olur :) Ama itiraf ediyorum en azından aralarda Counter vs oynamıyorlardı. İki gün gibi kısa bir sürede Silverlight'tan girdik WPF'ten çıktık. Önümüzdeki hafta yaz okulunun proje haftasında tekrar onlarla beraber olacağım, bakalım ne fikirler çıkacak.

Microsoft Yaz Okulu, İstanbul
Microsoft Yaz Okulu, İstanbul

Yaz okulundaki öğrencilerime tavsiyem hafta sonunda benim SL seminerlerine bir göz atmaları. Unutmayın SeminerTV'de oyun programlama konusunda da bir seminer kaydı var, eğer projelerinizde SL ile oyun programlama gibi şeyler düşünüyorsanız bence kaçırmayın ;) Haftaya görüşmek üzere...

Tuesday, August 11, 2009 7:18:43 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Silverlight 3.0  | 
 Monday, August 10, 2009

Uygulamalarımız için Windows 7 ile beraber gelen Taskbar özelliklerinden biri de Taskbar içerisine uygulamaya özel Toolbar yerleştirebiliyor olmak. Kullanıcıların uygulamalarımızla ilgili en sık kullandıkları özelliklere hızlı bir şekilde ulaşılabilmesini sağlayan bu mini toolbarları yaratmak Taskbar API Wrapper'ları ile beraber çok kolay bir hal alıyor.

Taskbar Toolbar'ları ile ilgili canlı bir örnek görmek için hemen Windows7 içerisinde Media Player'a başvurabiliriz. Media Player içerisinde oynattığınız şarkılar veya videolar arasında hızlıca geçiş yapmanızı sağlayacak olan mini bir toolbar otomatik olarak Taskbar'da Preview panelinde gözüküyor.

Taskbar'da minik bir toolbar!
Taskbar'da minik bir toolbar!

Bu minik toolbarlar farklı işlemler için kullanılabilir. Özellikle hemen toolbarın üstünde de uygulamanın Preview kısmının bulunduğunu düşünürsek ilginç bir manzara oluşturmak da mümkün. Örneğin toolbardaki düğmelere göre uygulama kendi genelinde görsel değişiklikler yaparak kullanıcıyı bilgilendirirse otomatik olarak bu değişiklikler toolbar üzerindeki preview alanına da yansıyacağı için bu ufacık preview ekranından kullanıcıya ciddi bir işlevsellik aktarılabilir.

Peki biz nasıl yapacağız?

Windows7 içerisinde Taskbar Toolbars özelliğinden faydalanabilmemiz için ilk olarak WindowsAPICodePack ile beraber gelen Microsoft.WindowsAPICodePack.dll ve Microsoft.WindowsAPICodePack.Shell.dll'i referans olarak projemize eklemeliyiz. Yarattığımız bu yeni WPF projesine ayrıca System.Drawing'i de referans alırsak ileriki adımlarda sorun yaşamayız. Yapacağımız örnekte Toolbar içerisinde basit bir düğme ekleyerek görselliğini değiştirecek ve düğmeye basıldığında bir event-listener ile durumdan haberdar olacağız.

[VB]

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

        Dim ikon = New System.Drawing.Icon(Application.GetResourceStream(New Uri("dvd_rom.ico", UriKind.RelativeOrAbsolute)).Stream)

        Dim dugme As New ThumbnailToolbarButton(ikon, "DVD'den müzik çal!")

        AddHandler dugme.Click, AddressOf dugme_Click

        TaskbarManager.Instance.ThumbnailToolbars.AddButtons(New WindowInteropHelper(Application.Current.MainWindow).Handle, dugme)

    End Sub

 

    Private Sub dugme_Click(ByVal sender As Object, ByVal e As ThumbnailButtonClickedEventArgs)

        MessageBox.Show("OK")

    End Sub

[C#]

        void Window1_Loaded(object sender, RoutedEventArgs e)

        {

            var ikon = new System.Drawing.Icon(Application.GetResourceStream(new Uri("dvd_rom.ico", UriKind.RelativeOrAbsolute)).Stream);

            var dugme = new ThumbnailToolbarButton(ikon, "DVD'den müzik çal!");

            dugme.Click += new EventHandler<ThumbnailButtonClickedEventArgs>(dugme_Click);

            TaskbarManager.Instance.ThumbnailToolbars.AddButtons(new WindowInteropHelper(Application.Current.MainWindow).Handle, dugme);

        }

 

        void dugme_Click(object sender, ThumbnailButtonClickedEventArgs e)

        {

            MessageBox.Show("OK");

        }

Kodumuz içerisinde uygulamamız ekrana geldiği gibi projemi içerisinde bulunan dvd_rom.ico dosyasının bir ikon değişkenine yüklüyoruz. Sonrasında Toolbar'ımıza eklenmek üzere ThumbnailToolbarButton nesnesi yaratmamız gerekiyor. ThumbnailToolbarButton yaratırken söz konusu sınıfın constructor'ı bizden bir ikon ve bir de Tooltip için açıklama metni istiyor. Bir sonraki adımda yarattığımız bu özel düğmenin Click event'ını da ayrı bir event-listener'a bağlıyoruz.

Son olarak geriye sadece bu düğmeyi Toolbar'ımıza eklemek kalıyor. Bunun için ThumbnailToolbars sınıfı altındaki AddButtons metodunu kullanabiliriz. Metodumuz ilk olarak toolbarın hangi pencerenin Preview'una ekleneceğini anlayabilmek için bizden Handle istiyor, sonraki parametrelerde sıra ile yarattığımız tüm düğmeleri parametre olarak geçebiliyoruz. Biz örneğimizde sadece bir tek düğme kullandık fakat siz çok daha fazla ve farklı düğmeler kullanabilirsiniz.

Tek tıkla DVD'den müzik çalabilecek bir uygulama?
Tek tıkla DVD'den müzik çalabilecek bir uygulama?

Düğmeye basıldığında daha önce eklemiş olduğumuz dugme_Click event listener'ı çalışacak. Böylece istediğimiz işlemi rahatlıkla yapabileceğiz. Artık örneğinizi çalıştırıp test edebilirsiniz.

Hepinize kolay gelsin.

Örneklere ait kaynak kodlar - 10082009_3.rar (3,36 MB)

Monday, August 10, 2009 7:01:36 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Windows 7 | WPF  | 
 Sunday, August 09, 2009

Windows 7'nin RTM olduğu bugünlerde artık yeni hazırladığımız uygulamalarda da Windows7'nin özelliklerinden faydalanmanın zamanı geldi. Önümüzdeki günlerde sizlerle Windows7 tarafında farklı yenilikleri teknik detaylar ile paylaşacağım. Bugün bu ilk makalemizde hızlı bir giriş yapalım. Umarım yazılar faydalı olur.

Windows 7 ile beraber gelen farklı özelliklerin C# veya VB gibi managed diller ile kullanımı için ilk olarak gerekli olan managed wrapperları bilgisayarımıza indiremeliyiz. http://code.msdn.microsoft.com/WindowsAPICodePack adresinden indirebileceğiniz CodePack içerisinde yer alan wrapper'lar bizim rahatlıkla Windows7 API'larına C# veya VB ile ulaşabilmemizi sağlayacaktır. İndirdiğiniz paket içerisinde WindowsAPICodePack adında bir Visual Studio solution dosyası bulacaksınız. Söz konusu projeyi Visual Studio ile açıp kaynak kodlarını inceleyebilir ve derleyebilirsiniz. Derleme işlemi sonrasında Windows 7'nin farklı özellikleri için farklı wrapper DLL'leri kullanımımıza hazır olacak.

Taskbar'da Overlay Ikon Kullanımı

Windows 7 ile beraber gelen görsel anlamdaki en önemli özelliklerden biri alıştığımız Taskbar yapısının ciddi şekilde değişiyor olması. Yeni yapı ile beraber kullanıcılara taskbar üzerinden anlık olarak çok daha fazla bilgi aktarma şansımız oluyor. Örneğin eskiden sadece notification bölümünde gösterebildiğimiz veya özel uyarı mesajları çıkardığımız durumlara ait görsel değişiklikleri artık doğrudan Taskbar'da uygulama ikonu üzerine ek bir ikon ekleyerek kullanıcılara yansıtabiliyoruz. Bu etkileşimi kullanan uygulamalara bir örnek olarak Outlook'u ele alabiliriz. Outlook içerisinde herhangi bir mesaj geldiğinde artık taskbardaki outlook ikonunun yanında bir de mail ikonu görebiliyoruz. Böylece Outlook herhangi bir ek mesaj penceresi vs açmadan aslında kullanıcıyı rahatlıkla durumdan haberdar edebiliyor.

Taskbar içerisinde Overlay İkon gösterimi.
Taskbar içerisinde Overlay İkon gösterimi.

Biz de uygulamalarımızda bu tür ikonlar göstererek kullanıcıları farklı durumlardan haberdar edebiliriz. O nedenle gelin bu gibi ikonları Taskbar'da göstermenin yolunu beraberce inceleyelim.

İlk olarak hemen Taskbar ile ilgili wrapper DLL'lerimizi yeni yaratacağımız bir WPF projesine referans almamız gerekiyor. Taskbar özelliklerini kullanabilmeniz için Microsoft.WindowsAPICodePack.dll ve Microsoft.WindowsAPICodePack.Shell.dll'i referens almanız yeterli olacaktır. Ayrıca WPF tarafından System.Drawing sınıfını da referans almayı unutmayın. Sonraki adımda herhangi bir ikon (*.ico) dosyasını projenize sağ tuş ile tıklayarak "Add Existing Item" seçeneği ile ekleyin ve dosyanın Build Action'ını da "Resource" olarak ayarlayın. Böylece artık uygulamamız içerisinde bir ikon dosyamız var ve yeri geldiğinde bu dosyanın taskbarda gözükmesini sağlamamız gerekiyor.

Örnek olması amacı ile uygulama ekranın bir düğme yerleştirin ve düğmenin Click durumuna da aşağıdaki kodu yazalım.

[VB]

        Dim ikon = New System.Drawing.Icon(Application.GetResourceStream(New Uri("light.ico", UriKind.RelativeOrAbsolute)).Stream)

        TaskbarManager.Instance.SetOverlayIcon(ikon, "Uygulamanın bir fikri var!")

Kod içerisinde de görebileceğiniz üzere aslında işlem oldukça basit. İlk olarak projemize eklediğimiz ico dosyasını bir Resource olarak Application.GetResourceStream ile alıyoruz. Aldığımız bu stream'den bir ikon yarattıktan sonra wrapper sınıflarımız içerisinde SetOverlayIcon metodunu kullanmamız yeterli oluyor. Söz konusu metod toplamda iki parametre ile rahatlıkla çalışabilir durumda. İlk parametre malum ikonunumuz ikincisi ise screen reader uygulamaları tarafından ikon gösterildiğinde okunacak olan metin.

Uygulamamızın bir fikri var! Ampül yanıyor!
Uygulamamızın bir fikri var! Ampül yanıyor!

Yukarıdaki ekran görüntüsünde de inceleyebileceğiniz üzere istediğimiz zaman taskbar'da bu şekilde overlay ikonlar gösterebiliyoruz. Böylece kullanıcılar uygulamamız arka planda çalışırken bu ufak mesajlar ile uygulamamızın durumundan veya uygulamamıza yaptırdıkları herhangi bir işlemin durumundan haberdar olabilirler.

Taskbar'da Progress Gösterimi

Varsayalım ki bir programa uzun bir işlem yaptırıyoruz ve sürekli ne zaman biteceğinin merakı ile yanıp kavruluyoruz :) Aslında bu durum en sık internetten birşey indirirken başımıza gelmez mi? Download ettiğimiz o dosyanın ne kadarının bilgisayarımıza inmiş olduğunu kontrol etmek için sürekli gidip download penceresine bakarız. Oysa keşke bunun çok daha basit bir yolu olsaydı diyorsanız artık Taskbar üzerinde de Progress gösterebileceğimizin müjdesini veriyorum.

Şu anda hemen Windows7 üzerinde testini yapabileceğiniz uygulama Internet Explorer'ın ta kendisi. Eğer Internet Explorer ile bir download işlemi başlatırsanız Taskbar'da download işleminin ne kadarının bitmiş olduğunu gösteren bir progressBar göreceksiniz.

Internet Explorer ile download yaparken...
Internet Explorer ile download yaparken...

Ne kadar basit ve bir o kadar işlevsel değil mi? Yukarıda gördüğünüz görüntü sayesinde download esnasında sürekli Internet Explorer'a gidip durum kontrolü yapmaktan kurtuluyoruz. Peki biz kendi programlarımızda acaba bu işlevselliği nasıl sunarız?

Her zamanki gibi yeni bir WPF uygulaması yaratarak Microsoft.WindowsAPICodePack.dll ve Microsoft.WindowsAPICodePack.Shell.dll'i referans olarak projemize ekledikten sonra sahneye hemen deneme amaçlı bir düğme koyalım. Söz konusu düğmeye her basıldığında elimizdeki bir Progress değişkenini arttırdığımızı düşünelim.

[VB]

    Dim Progress As Integer = 0

    Private Sub btnTikla_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnTikla.Click

        Progress += 10

        TaskbarManager.Instance.SetProgressValue(Progress, 100)

    End Sub

Yukarıdaki kod örneğinizde tamamen deneme amaçlı olarak yarattığımız Progress değişkeni düğmeye her basıldığında ilerleyen bir süreci temsil ediyor. Söz konusu süreci Taskbar'a yansıtmak için SetProgressValue metodunu kullanıyoruz. Metodumuzun aldığı ilk parametresi göstermesi gereken Progress'in değeri, ikinci parametres ise maksimum verilebilecek Progress değerinin ta kendisi. Aslında işlem bu kadar basit fakat ek olarak isterseniz Progress ile ilgili ek bilgiler aktarmak adına Progress görselinin rengini de kısmen değiştirebilirsiniz. Örneğin belki de süreç kullanıcı tarafından veya bir hata nedeniyle durmuştur? Bu durumda kullanıcıyı yine Taskbar üzerinden uyarabilir miyiz acaba?

Taskbar Progress'de farklı State kullanımları.
Taskbar Progress'de farklı State kullanımları.

Taskbar'da Progress gösterirken Progress'in ayrıca nasıl gösterileceği ile ilgili ek bir ayar daha yapabilirsiniz. Bu ayarın adı da State. Böylece Progress'in bekleme durumunda olduğunu veya hata verdiğini veya belki de Progress'in belirsiz olduğunu ve ne zaman biteceğini bilmediğinizi kullanıcıya belirtebilirsiniz.

[VB]

            TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.Indeterminate)

Yukarıda gördüğünüz kod basit bir şekilde Taskbar'daki Progress'in belirsiz olduğunu belirterek Indeterminate State'e geçiş yaptırıyor. Böylece taskbardaki Progress sürekli aynı animasyon ile uygulama ikonunun arkasından geçecek ve uygulamanın bir işlem yaptığı fakat bitiş süresinin belli olmadığı kullanıcıya belirtilmiş olacakı. Bu şekilde farklı state'lerin görsellikteki değişikliklerini bir üstteki ekran görüntüsünde inceleyebilirsiniz.

Bir sonraki makalemizde Taskbar özellikleri ile devam edeceğiz. Hepinize kolay gelsin.

Makaleye ait örnek kodlar : 09082009_3.rar (1,05 MB)

Sunday, August 09, 2009 6:30:25 PM (GTB Standard Time, UTC+02:00)  #    Comments [2]   Windows 7 | WPF  | 
Copyright © 2010 Daron Yöndem. Tüm hakları saklıdır.