Makale Özeti

Bu yazımızda Silverlight 2.0 Beta 1 ile beraber gelen kontrol şablonlama özelliklerine değinirken merkezi şablon yönetimi ve stil oluşturulmasını da inceleyeceğiz.

Makale

Silverlight 2.0 Beta 1 ile beraber gelen görsel özellikler içerisinde özellikle tasarımcıların en çok beğenecekleri ve bildiğimiz teknolojiler arasında CSS'e benzetebileceğimiz "Resource" yapısı çok önemli bir yere sahip. WPF'de hali hazırda var olan ve Silverlight tarafına da (ciddi farklılıklar ile) taşınan bu özellikler sayesinde Silverlight uygulamaları içerisinde kontrollerin programatik işlevselliklerinden bağımsız olarak görsel özellikleri ayarlanabildiği gibi merkezi bir yönetim de sağlanabiliyor. Gelin hızlı bir örnek ile konumuza giriş yapalım.

<UserControl x:Class="SilverlightApplication7.Page"

    xmlns="http://schemas.microsoft.com/client/2007"

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

    Width="400" Height="300">

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

      <Button HorizontalAlignment="Stretch" Margin="44,54,55,45" VerticalAlignment="Stretch" Content="Button"/>

    </Grid>

</UserControl>

İlk olarak yukarıdaki gibi yaratacağımız yeni bir Silverlight 2.0 uygulamasına basit bir Button yerleştirelim ve Button içerisinde yazının (Content) yerine farklı birşeyler koymayı deneyelim. Yukarıdaki kod içerisinde Content değeri doğrudan bit metne eşitlenmiş durumda. Oysa bu değerin içerisine başka bir Silverlight kontrolü yerleştirebiliriz.

<UserControl x:Class="SilverlightApplication7.Page"

    xmlns="http://schemas.microsoft.com/client/2007"

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

    Width="400" Height="300">

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

    <Button HorizontalAlignment="Stretch" Margin="44,54,55,45" VerticalAlignment="Stretch">

      <Button.Content>

        <Image Height="84" HorizontalAlignment="Center"  VerticalAlignment="Center" Width="139" Source="Dock.jpg"/>

      </Button.Content>

    </Button>

  </Grid>

</UserControl>

Yukarıdaki kod içerisinde Button nesnesinin Content özelliğini belirlemek için Button tagları arasında ayrıca bir Button.Content tagları açtık.

Button.Content özelliğini değiştirilen Button nesnesi sağda.
Button.Content özelliğini değiştirilen Button nesnesi sağda.

Bu noktada Button.Content içerisinde istediğiniz Silverlight kontrolünü yerleştirebilirsiniz fakat dikkat etmeniz gereken bir nokta var; maalesef bu taglar arasında sadece bir Silverlight kontrolü yerleştirilebilir. Aslında cümle olarak yanlış bir cümle oldu, daha doğru tabiri ile Button.Content içerisinde XAML kodunun her zaman tek bir RootElement'inin olması gerekiyor. Yani eğer bu bölgeye iki kontrol yerleştirmek istiyorsanız kontrollerinizi bir Container Element içerisinde gruplayarak yerleştirmek zorundasınız. Aşağıdaki örneğimizde Container Element olarak bir StackPanel kullanacağız.

<UserControl x:Class="SilverlightApplication7.Page"

    xmlns="http://schemas.microsoft.com/client/2007"

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

    Width="400" Height="300">

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

    <Button HorizontalAlignment="Stretch" Margin="44,54,55,45" VerticalAlignment="Stretch">

      <Button.Content>

        <StackPanel Orientation="Horizontal">

          <Image Height="84" HorizontalAlignment="Center"  VerticalAlignment="Center" Width="139" Source="Dock.jpg"/>

          <Image Height="84" HorizontalAlignment="Center"  VerticalAlignment="Center" Width="139" Source="Dock.jpg"/>

        </StackPanel>

      </Button.Content>

    </Button>

  </Grid>

</UserControl>

Gördüğünüz gibi Root Element olarak Button.Content içerisinde bir StackPanel kullandıktan sonra artık StackPanel içerisine istediğimiz kadar kontrol yerleştirebiliyoruz.

Button.Content içerisinde birden çok Silverlight kontrolü.
Button.Content içerisinde birden çok Silverlight kontrolü.

Bu yapı ile farklı kontrollerinin farklı görsel özelliklerini tanımlamak mümkün.

Bir kontrolün tüm görsel yapısını nasıl değiştiririz?

Bu noktaya kadar elimizdeki bir düğmenin içerisinde farklı Silverlight kontrolleri yerleştirdik. Oysa söz konusu düğmenin tüm görsel yapısını değiştirmek de isteyebilirdiniz. Örneğin bir Resim nesneninin bir Button olarak tanımlanması mümkün müdür?

<UserControl x:Class="SilverlightApplication7.Page"

    xmlns="http://schemas.microsoft.com/client/2007"

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

    Width="400" Height="300">

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

    <Button HorizontalAlignment="Stretch" Margin="44,54,55,45" VerticalAlignment="Stretch">

      <Button.Template>

        <ControlTemplate>

          <Image Height="84" HorizontalAlignment="Center"  VerticalAlignment="Center" Width="139" Source="Dock.jpg"/>

        </ControlTemplate>

      </Button.Template>

    </Button>

  </Grid>

</UserControl>

Yukarıdaki kodumuz içerisinde bir Button nesnesinin Template özelliğini başka bir ControlTemplate atayarak değiştiyoruz. Tanımladığımız ControlTemplate içerisinde sadece bir adet Image nesnesi var. Böylece artık düğmemiz sadece bir Image nesnesinden oluşacak oysa programcımız için bu nesne hala bir Button.

Bir Button nesnesine benzemiyor değil mi? Ama öyle.
Bir Button nesnesine benzemiyor değil mi? Ama öyle.

Peki tüm bunların anlamı nedir?

Yukarıdaki iki yapıyı Button.Content ve Button.Template beraber kullanmanız halinde istediğimiz gibi düğmeler oluşturabilirsiniz. Gelin şimdi güzel bir dikdörtgen çizelim ve bunu yeni yarattığımız bir Button nesnesinin Template özelliğine aktaralım.

<UserControl x:Class="SilverlightApplication7.Page"

    xmlns="http://schemas.microsoft.com/client/2007"

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

    Width="400" Height="300">

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

    <Button HorizontalAlignment="Stretch" Margin="44,54,55,45" VerticalAlignment="Stretch">

      <Button.Template>

        <ControlTemplate>

          <Rectangle Stroke="#FF000000" RadiusY="16" RadiusX="16" Margin="{TemplateBinding Margin}">

            <Rectangle.Fill>

              <RadialGradientBrush>

                <GradientStop Color="#FFFF0000" Offset="1"/>

                <GradientStop Color="#FFFFFFFF" Offset="0"/>

              </RadialGradientBrush>

            </Rectangle.Fill>

          </Rectangle>

        </ControlTemplate>

      </Button.Template>

    </Button>

  </Grid>

</UserControl>

Yukarıdaki kod içerisinde en önemli nokta Button'umuzun ControlTemplate'i içerisindeki Rectangle nesnesinin Margin özelliklerinin TemplateBinding ile içerisinde olduğu kontrolün Margin özelliğine bağlanmış olması. Böylece Button nesnesi büyüdükçe otomatik olarak ControlTemplate içerisinde tanımlanmış Rectangle da büyüyecek. Her zaman olduğu gibi eğer ControlTemplate içerisinden birden çok Silverlight kontrolü kullanarak görsel özellikler tanımlamak isterseniz bir Container Element kullanmanız gerekecektir. Bizim örneğimizde tek nesne bulunduğu için gerek olmadı.

Yeni Silverlight Button nesnemiz karşınızda!
Yeni Silverlight Button nesnemiz karşınızda!

Gördüğünüz gibi düğmemizin görsel özellikleri tamamlandı. Peki ya bu düğmenin içerisine yazılacak olan yazı nerede? Şu anda kontrolün bu hali ile Button.Content özelliğine farklı değerler verdiğiniz görsel anlamda herhangi bir değişiklik görmeyeceksiniz. Çünkü kontrolün ControlTemplate tanımı içerisinde herhangi bir şekilde Content içeriğinin konacağı bir yer ayarlanmış değil. Yani Button.Content içerisine konacak nesnelerin veya yazının ControlTemplate içerisinde nereye konacağını tanımlamamız gerekiyor.

<UserControl x:Class="SilverlightApplication7.Page"

    xmlns="http://schemas.microsoft.com/client/2007"

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

    Width="400" Height="300">

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

    <Button HorizontalAlignment="Stretch" Margin="44,54,55,45" VerticalAlignment="Stretch">

      <Button.Content>

        <TextBlock VerticalAlignment="Center" Text="TextBlock" TextWrapping="Wrap"/>

      </Button.Content>

      <Button.Template>

        <ControlTemplate TargetType="Button">

          <Grid>

            <Rectangle Stroke="#FF000000" RadiusY="16" RadiusX="16" Margin="{TemplateBinding Margin}">

              <Rectangle.Fill>

                <RadialGradientBrush>

                  <GradientStop Color="#FFFF0000" Offset="1"/>

                  <GradientStop Color="#FFFFFFFF" Offset="0"/>

                </RadialGradientBrush>

              </Rectangle.Fill>

            </Rectangle>

            <ContentPresenter Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center"/>

          </Grid>

        </ControlTemplate>

      </Button.Template>

    </Button>

  </Grid>

</UserControl>

Yukarıdaki kod içerisinde tanımladığımız Button'ın ControlTemplate'i içerisinde bir ContentPresenter nesnesi bulunuyor. Bu ContentPresenter'ın Content özelliği ise TemplateBinding ile ana Button nesnesinin Content'ına bağlanmış durumda. Böylece Button'un Button.Content'ı içerisindeki tüm Silverlight kontrolleri otomatik olarak ControlTemplate içerisindeki ContentPresenter içerisine yerleştirilmiş olacaktır. Bu noktada dikkat etmemiz gereken önemli bir ayar var, ControlTemplate'ın TargetType'ının özel olarak ayarlanmış olması gerekiyor, aksi halde söz konusu ControlTemplate bir Button içerisinde bulunmasına rağmen doğru olarak çalışmıyor. Umarım Beta sürümü sonrasında sürümlerde bu yapı düzeltilir, şu anki çalışma yapısı pek mantıklı değil.

Button'umuzun son hali.
Button'umuzun son hali.

Peki neden doğrudan herşeyi ControlTemplate içerisine koymuyoruz?

Çok mantıklı bir soru. Çünkü bu noktaya kadar yaptığımız tüm görsel ayarların üzerinden bir merkezi yönetim sistemi kurmak istiyoruz. Bir önceki adımda Button nesnemizin Template özelliğini ayarlamıştık. Şimdi sıra geldi söz konusu Template'ı birden çok kontrol tarafından kullanılabilir hale getirmeye.

<UserControl x:Class="SilverlightApplication7.Page"

    xmlns="http://schemas.microsoft.com/client/2007"

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

    Width="400" Height="300">

  <UserControl.Resources>

    <Style x:Key="Dugme" TargetType="Button">

      <Setter Property="Template">

        <Setter.Value>

          <ControlTemplate TargetType="Button">

            <Grid>

              <Rectangle Stroke="#FF000000" RadiusY="16" RadiusX="16" Margin="{TemplateBinding Margin}">

                <Rectangle.Fill>

                  <RadialGradientBrush>

                    <GradientStop Color="#FFFF0000" Offset="1"/>

                    <GradientStop Color="#FFFFFFFF" Offset="0"/>

                  </RadialGradientBrush>

                </Rectangle.Fill>

              </Rectangle>

              <ContentPresenter Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center"/>

            </Grid>

          </ControlTemplate>

        </Setter.Value>

      </Setter>

    </Style>

  </UserControl.Resources>

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

    <Button Style="{StaticResource Dugme}" HorizontalAlignment="Stretch" Margin="44,54,55,45" VerticalAlignment="Stretch">

      <Button.Content>

        <TextBlock VerticalAlignment="Center" Text="TextBlock" TextWrapping="Wrap"/>

      </Button.Content>

    </Button>

  </Grid>

</UserControl>

Template tanımımızı Button'un içerisinden keserek doğrudan UserControl.Resources altına alıyoruz. Böylece artık tüm Silverlight XAML sayfası içerisinde kullanabileceğiz. Tabi bunu yaparken bir Style tanımlıyoruz ve TargetType özelliğini de Button olarak düzenliyoruz. Style içerisinde bir Setter yerleştirerek hedef kontrolün Template özelliğini değiştirmek istediğimizi de belirttikten sonra Setter.Value içerisine daha önce hazırladığımız Template yapısını yerleştiriyoruz.

Son olarak adını Dugme koyduğumuz bu Style'ı kullanmak istediğimiz tüm Button kontrollerinin Style özelliğini StaticResource olarak stilimize bağlayarak kullanabiliyoruz. Böylece tüm düğmelerimiz aynı ControlTemplate'i kullanacaklar ve hepsi de kendi içerisindeki Content'ı hedef Style içerisindeki ContentPresenter'a yerleştirerek gösterecekler. Herhangi bir görsel değişiklik gerektiğinde ise merkezi olarak Style'ımızı değiştirerek ilerleyebileceğiz.

İsterseniz bu Stlye'ınızı Silverlight uygulamasının App.xaml dosyası içerisine koyarak Silverlight uygulamanızdaki tüm XAML dosyaları içerisinde kullanılabilir hale de getirebilirsiniz.

Hepinize kolay gelsin.