Makale Özeti

Bu makalemizde WPF ile kendi animasyon sınıfımızı nasıl oluşturacağımızı incelemeye çalışıyoruz.

Makale

Bu makalemizde Windows Presantation Foundation(WPF) ile bizlere sunulmakta olan animasyon sınıflarının aksine, kendi animasyon işlemlerimizi yapabilmemiz için yeni bir sınıf oluşturmak istediğimizde nasıl bir yol izleyeceğimizin üzerinde duracağız. Daha sonra ise bu sınıfı geliştireceğimiz bir projede nasıl kullanacağımızı görerek basit bir uygulama ile bilgilerimizi pekiştirerek makalemizi sonlandıracağız.

İsterseniz kısa olarak oluşturacak olduğumuz sınıfı ne amaçla kullanacağımızı incelemek ile işe başlayalım.



Yukarıdaki resimlerin bir ızgaraya(grid) dizilmiş olarak sıralandığını görmekteyiz.Bu yapıya Gridsel yapı denmektedir.İşte bizim oluşturacağımız sınıf bu gridsel yapının fare ile tıklandığının algılanması ve hareketi başlaması ile ilgili olacaktır.

Ekrandaki gridsel oluşumu yapabilmemiz için Grid kontrolünden GridLenght özelliğine satır ve sütün değerlerini atayarak oluşturabilmekteyiz.Öncelik ile GridLenght özelliğine opacity(saydamlık) ve Duration(animasyon akış süresi) değerlerini vermemiz gerekmektedir.

Kendimiz yeni bir animasyon sınıfı hazırlayabilmemiz için ilk olarak WPF tarafından oluşturulmuş olan animasyon sınıfının nasıl kullanıldığını anlamamız gerekmektedir.Bu mantığı kavramanın en mantıklı yolu ise küçük bir kod bloğu oluşturarak hangi özelliklerin nasıl kullanıldığını incelemek olacaktır.Bu işlem için basit bir animasyon işlemi yapabilen bir yapı tasarlayalım.

  <Button Name="button1">
   <Button.Triggers>
        <EventTrigger RoutedEvent="Button.Click">
             <BeginStoryboard>
                 <Storyboard>
                    <DoubleAnimation
                       Storyboard.TargetName="button2"
                       Storyboard.TargetProperty="Opacity"
                       From="1" To="0" Duration="0:0:2" />
                  </Storyboard>
             </BeginStoryboard>
        </EventTrigger>
   </Button.Triggers>
</Button>

Yukarıda incelemiş olduğumuz yapı belirli zaman aralığından saydamlık sağlamak için kullanılabilir.Şimdi kullandığımız özellikleri genel hatları ile açıklamaya çalışalım.

Opacity (Saydamlık): "0" ile "1" arasında aldığı değerler ile çalışmalarımızda istediğimiz oranda saydamlık kazandırmaktadır.

DoubleAnimation: Bu sınıf çalışmalarımızda kullanacak olduğumuz animasyonların bir grup şeklinde toplandığı ve hepsinin aynı anda kullanarak göze hoş gelen bir görünüm kazandırmamıza yardımcı olmaktadır.

System.Windows.Media.Animation: Animasyon işlemlerimizin yapılması için .net3.x' in bize sapladığı kütüphane.Animasyon işlemi yapmak istediğimizde kod tarafında otomatik olarak oluşturulmaktadır.

Duration: Animasyon işlemlerinin gerçekleşme hızını belirlemektedir.

From="" To="": Bu özellik yapmış olduğumuz animasyon işleminin ne kadar zaman ile çalışacağını ve hangi zamanda başlayıp hangi zamanda biteceğini belirtmektedir.

Sıradaki bakacağımız özellik ise grid' in satır ve sütun özellikleri olacaktır.Yazımızın başında Grid' in özelliklerinden biride satır ve sütunlar olduğunu, bizde oluşturacak olduğumuz bu uygulamada bu özelliklerden yararlanacağımızı belirtmiştik.Bu ColumnDefinition ve RowDefinition sınıfına dahil olan Height ve Widht özellikleri olacaktır. Bu özellikler GridLenght' e bağlıdır.

Animasyon işlemleri oluşturabileceğimiz bir diğer editörümüz ise Expression Studio ürünlerinden Expression Blend'tır.Bu editör yardımı ile de animasyon işlemleri gerçekleştirebilmemiz mümkündür.Bunu daha ayrıntılı olarak Expression Blend ile Animasyon Uygulamaları isimli makaleyi inceleyerek görebilirsiniz.

Grid ve animasyon işlemleri ile ilgili özellikleri inceledikten sonra artık sınıfımızı oluşturmaya başlayabiliriz.Bu sınıfı oluştururken ilk yapmamız gereken AnimationTimeline sınıfını başka bir değere atamak olacaktır.

 namespace GridAnimationDemo
   {
         internal class GridLengthAnimation : AnimationTimeline
   {


AnimationTimeline' nın abstrack' ından bulunan TargetPropertyType'ını belirlememiz gerekmektedir.Bu da get ve set ile sağlanmaktadır.Bu değerler ile oluşturduğumuz olan sınıfın yeniden çağırılarak kullanılmasını sağlamış oluyoruz.

public override Type TargetPropertyType
   {
      get
         {
            return typeof(GridLength);
         }
   }


Yukarıda ki fonksiyona benzer şekilde From ve To için get ve set' lerini oluşturabiliriz.

From ve To fonksiyonlarını oluştururken dikkatimi çeken bir nokta oldu.Oluşturduğumuz kodlar oldukça uzun oluyor ve performansları yeteri kadar iyi olmuyordu.Bunun için biraz araştırma ile WPF guru' da işimize çok yarayacak bir yapı buldum.Bu yapıyı MSDN' de daha geniş çaplı bir araştırma ile incelediğim zaman gördüm ki oluşturacağımız projelerde oluşturduğumuz sınıfları daha etkili ve mükemmel bir performans ile kullanmamız için dependency property isimli bir özellik olduğunu fark ettik.Bu özellik genellikle component model ve Workflow' da kullanılıyor. Bu bizim gerçekten tam anlamıyla aradığımız özellikti.Dependency Property ile kullanacağımız değerleri belirtmemiz yeterli olacaktır.Şimdi bu kullanımı örnek ile bu özelliği kullanalım;

static GridLengthAnimation()
{
    FromProperty = DependencyProperty.Register("From", typeof(GridLength),
    typeof(GridLengthAnimation));

    ToProperty = DependencyProperty.Register("To", typeof(GridLength),
       typeof(GridLengthAnimation));
}
    public static readonly DependencyProperty FromProperty;
    public GridLength From
    {
        get
        {
            return (GridLength)GetValue(GridLengthAnimation.FromProperty);
        }
        set
        {
            SetValue(GridLengthAnimation.FromProperty, value);
        }
    }
    public static readonly DependencyProperty ToProperty;
    public GridLength To
    {
        get
        {
            return (GridLength)GetValue(GridLengthAnimation.ToProperty);
        }
        set
        {
            SetValue(GridLengthAnimation.ToProperty, value);
        }
}



Şimdi ise sınıfımıza yükleyeceğimiz değer GetCurrentValue özelliği olacaktır.Bu özelliğin alacağı değerler animasyon işleminin ne kadar bir zamanda gerçekleştirilebileceğini belirlememize olanak tanıyacaktır.

  public override object GetCurrentValue(object defaultOriginValue, object defaultDestinationValue, AnimationClock animationClock)
   {
      double fromVal = ((GridLength)GetValue(GridlenghtAnimation.FromProperty)).Value;
      double toVal = ((GridLength)GetValue(GridlenghtAnimation.ToProperty)).Value;

if (fromVal > toVal)
    {
         return new GridLength((1-animationClock.CurrentProgress.Value)*(fromVal - toVal)+toVal,GridUnitType.Star);
    }
else
    {
         return new GridLength((animationClock.CurrentProgress.Value) * (toVal - fromVal), GridUnitType.Star);
    }
}



Bu işlemimiz sonucunda animasyon zamanını ayarlayacak olan objemizde hazırlamış oluyor.Bu objeyi hazırlarken GridUnitType metodunu kullandık.Bu özellik kendisine ait alt özelliklerden biri olan Star ile birlikte kullanılarak animasyonun saniyesel olarak kullanımını sağlamıştır.

Artık gridsel animasyon işlemi için düşündüğümüz GridAnimated sınıfımızı oluşturmuş oluyoruz.Fakat bizim bu sınıfı WPF ile tasarıma dönüştürebilmemiz için XAML 'de kullanabilmemiz gerekmektedir.Şimdi ise oluşturduğumuz bir sınıfın XAML 'de nasıl kullanılacağını incelemeye çalışacağız.

İlk olarak XAML 'e sınıfımızı tanıtmadan önce kod tarafına tanıtırsak XAML 'e yükleyecek olduğumuz yük daha da azalacak,bu sayede işlemlerimizi çok daha hızlı bir biçimde bitirmiş olacağız.

  namespace GridAnimation
{

public partial class Window1 :System.Windows.Window
{
         public Window1()
    {
         InitializeComponent();
    }
    bool bTekliResimModu = false;

void resim_MouseDown(object sender, MouseEventArgs e)
    {
         Image resim = sender as Image;
         if (resim!=null)
    {
    int satir = Grid.GetColumn(resim);
    int sutun = Grid.GetRow(resim);

    for (int satirIcerigi = 0; satirIcerigi < anaGrid.RowDefinitions.Count; satirIcerigi++)
    {
    if (satirIcerigi!= satir)
         {
              GridlenghtAnimation gla = new GridlenghtAnimation();
              gla.From = new GridLength(bTekliResimModu ? 0 : 1, GridUnitType.Star);
              gla.To = new GridLength(bTekliResimModu ? 1 : 0, GridUnitType.Star); ;
              gla.Duration = new TimeSpan(0, 0, 2);
              anaGrid.RowDefinitions[satirIcerigi].BeginAnimation(RowDefinition.HeightProperty, gla);
            }

}

for (int sutunicerigi = 0; sutunicerigi < anaGrid.ColumnDefinitions.Count; sutunicerigi++)
    {
         if (sutunicerigi!=sutun)
         {
               GridlenghtAnimation gla = new GridlenghtAnimation();
               gla.From = new GridLength(bTekliResimModu ? 0 : 1, GridUnitType.Star);
               gla.To = new GridLength(bTekliResimModu ? 1 : 0, GridUnitType.Star);
               gla.Duration = new TimeSpan(0, 0, 2);
               anaGrid.ColumnDefinitions[sutunicerigi].BeginAnimation(ColumnDefinition.WidthProperty, gla);
         }

      }
    }
     bTekliResimModu = !bTekliResimModu;

               }
          }
}


Yukarıda yaptığımız işlemleri daha ayrıntılı bir biçimde inceleyemeye çalışalım.İlk olarak yaptığımız, oluşturmuş olduğumuz gridsel ekranın satır ve sütunlarına resimleri atamak olmuştur.Daha sonra bizim belirlemiş olduğumuz resimlerin sayısı for döngüsü ile algılanıp, bu resimlerin belirli bir animasyon hızı ile hareketi sağlanmıştır.Bu işlem hem satır hem de sütun işlemleri için yapılmıştır.Bu resimlerin animasyon işlemlerinin hızlarını belirtmemizin dışında aynı zamanda bizim tarafımızdan oluşturulmuş olan GridAnimated sınıfına ait olan GridLenghtAnimation metodunu da kullanmış olduk.Yaptığımız bu işlem bizlere WPF 'in grafik kütüphanesi tarafından sunulan hazır animasyon işlemlerinin dışında kendi animasyon sınıfımız ile oluşturduğumuz işlemleri kullanmamıza olanak tanıyacaktır.

Yapmak istediğimiz animasyon sınıfının yukarıdaki for döngüleri yardımıyla gerçek işlemine kavuştuğunu gözden kaçırmamız gerekmektedir.Çok ufak bir özellik hatası sonucunda projemizin derlenmesinde herhangi bir hata ile karşılaşmamıza rağmen, projemiz çalışırken runTimeError denilen çalışma zamanı hatası ile karşılaşma durumumuz yüksektir.Bu sebepten dolayı satırın boyu, sütunun genişliği ve bunun gibi diğer özelliklerini kullanırken çok dikkatli olmamız gerekmektedir.

Artık XAML 'in arka plan kod'unu oluşturduğumuza göre işin görsel tarafı olacak olan XAML 'i oluşturmaya başlayabiliriz.XAML 'in içeriğinde projemizde kullanacak olduğumuz resimlerin hangi sütün ve hangi satırda bulunacağı, kullanacağımız resmin adresi gibi bilgileri tutacağız.

Diğer WPF makalelerini incelediyseniz XAML 'in içerisinde bir takım işlemler uyguluyorduk.Kendi animasyon sınıfımızı oluşturmamız sonucunda bu işlemlerin tamamından projemizi kurtarmış oluyoruz.

Şimdi ise ilk olarak kendi animasyon sınıfımızı kullanmadan oluşturulmuş bir XAML kod örneğini, daha sonra ise bizim oluşturmuş olduğumuz XAML 'i inceleyelim.

  <Grid>
   <Grid.ColumnDefinitions>
   <ColumnDefinition Name="Sutun0" Width="*"/>
   <ColumnDefinition Name="Sutun1" Width="*"/>
   </Grid.ColumnDefinitions>

       <Button Name="button1">

<Button.Triggers>
   <EventTrigger RoutedEvent="Button.Click">
      <BeginStoryboard>
         <Storyboard>
            <proj:GridLengthAnimation
          Storyboard.TargetName="Col1"
          Storyboard.TargetProperty="Width"
          From="*" To="2*" Duration="0:0:2" />
         </Storyboard>
       </BeginStoryboard>
    </EventTrigger>
</Button.Triggers>
</Button>

<Button Name="button2" Grid.Column="1"></Button>
</Grid>


Yukarıdaki XAML'i incelerseniz belirli sınıflar ve bu sınıfların özellikleri yardımı ile animasyon işlemlerini oluşturuyorduk.Bizim yaptığımızda ise aşağıdaki gibi bir XAML oluşmaktadır.Dikkat ederseniz .net 3.x ile gelen özel sınıflardan hiç biri kullanılmamıştır.

  <Window x:Class="GridAnimation.Window1"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:proj="clr-namespace:GridAnimation"
            Title="Büyütmek için Tıklayın...Tekrar küçültmek için tıklayın..." Height="700" Width="700">
            <Grid Background="Silver" x:Name="anaGrid">
            <Grid.Resources>
                        <Style TargetType="{x:Type Image}">
                                    <Setter Property="Stretch" Value="Fill"/>
                                    <Setter Property="Margin" Value="2"/>
                        </Style>
            </Grid.Resources>

            <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" Name="sutun0"/>
                        <ColumnDefinition Width="*" Name="sutun1"/>
            </Grid.ColumnDefinitions>

            <Grid.RowDefinitions>
                        <RowDefinition Height="*" Name="satir0"/>
                        <RowDefinition Height="*" Name="satir1"/>
                        <RowDefinition Height="*" Name="satir2"/>
            </Grid.RowDefinitions>

            <Image MouseDown="resim_MouseDown" Source="Images/birthday.jpg" Grid.Row="0" Grid.Column="0" />
            
<Image MouseDown="resim_MouseDown" Source="Images/deve.jpg" Grid.Column="1" />
            
<Image MouseDown="resim_MouseDown" Source="Images/logo.jpg" Grid.Row="1" Grid.Column="0" />
            
<Image MouseDown="resim_MouseDown" Source="Images/saksili.jpg" Grid.Row="1" Grid.Column="1" />
            
<Image MouseDown="resim_MouseDown" Source="Images/saksisiz.jpg" Grid.Row="2" Grid.Column="0" />
            
<Image MouseDown="resim_MouseDown" Source="Images/tsunami.jpg" Grid.Row="2" Grid.Column="1" />

            </Grid>
</Window>


Yukarıdaki XAML ile bir önceki XAML'i incelediğimiz zaman oluşturduğumuz kod parçacığının biraz daha azaldığını ve hiç bir özel sınıf ve fonksiyon kullanmadığımızı görmüş oluyoruz.Bu zaten kuvvetli olan WPF animasyon kütüphanesini biraz daha etkili kullanmamıza olanak tanımaktadır.Ayrıca bu işlemler yardımı ile XAML'imizde animasyon işlemlerini yapmamış ve kod tarafında işimizi bitirip XAML'de yalnızca hangi resmin grid üzerinde ne tarafta duracağını belirtiyoruz.Oluşturduğumuz bu sınıf ile nesne tabanlı programlamayı WPF ile de kullanabileceğimiz görmüş oluyoruz.

Artık bütün işlemlerimizi bitirdiğimize göre projemizin çalışması esnasındaki bir kaç ekran görüntüsüne göz atabiliriz.


Büyüme işlemi başlarken...


Büyüme işlemi sonlanmaya yaklaşırken....


Büyüme işlemi sonucunda oluşan görüntü.

Yukarıdaki ekran görüntülerinden de görülebileceği gibi seçtiğimiz resmin üzerine tıkladığımız zaman boyutu otomatik olarak formumuzun tümüne yayılmakta, tekrar tıklandığımızda ise eski boyutuna geri dönmektedir.Bu resim sayısını istediğimiz şekilde arttırabilmemiz mümkündür.Ayrıca bu sınıfı bir protatif olarak düşünüp bunu daha da özelleştirerek çok daha farklı animasyon işlemleri elde edebilmeniz mümkündür.

Bir makalemizin daha sonuna gelmiş bulunuyoruz.Bu makalemizde kendi animasyon sınıfımızı oluşturarak formumuzu oluşturmak ile görevli olan XAML'in üzerinde olan animasyon işlemleri yükünü azaltmış bulunuyoruz.Ayrıca bu işlem yeni bir animasyon işlemi gerçekleştirmek istersek nasıl bir yol izleyeceğimizide öğrenmiş oluyoruz.

Umarım yararlı olmuştur.

Kullanmış olduğumuz programlama dilleri tarafından bizlere sunulan imkanların dışına çıkarak kendinize özel yapılar, sınıflar oluşturmanız dileğiyle.

İyi çalışmalar...

Turhal TEMİZER

Kaynaklar
MSDN Library for Visual Studio 2008 Beta2
Josh Smith WPF Blogs
Makalemizdeki örneğin kaynak kodları...