.net 3 etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
.net 3 etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

Çarşamba, Temmuz 08, 2009

Asp.Net MVC Framework - Master Pages Kavramı - I

Microsoft 'un web projelerini hazırlarken bizlere mimari tasarıları daha basit ve kullanışlı bir biçimde kullanmamızı sağlayan Asp.Net MVC Framework 'ü daha önceki yazılarımızda detaylı bir biçimde tanımaya çalıştık. Peki neler yapabiliyorduk. Son kullanıcıdan gelen istekler doğrultusunda başka bir sayfaya nasıl yönlendirebileceğimizi, Controller katmanı ile veri tabanı işlemlerini nasıl işleyip görsel sayfaya ileteceğimizi, model katmanı yardımı ile veri tabanı ile haberleşmelerin çok daha basit bir şekilde yapılabileceğini, veri tabanını aktif olarak kullandığımızda nasıl bir uygulama geliştirdiğimizi ve view sayfaları nasıl kolayca oluşturabileceğimizi detaylı olarak daha önceki yazılarımızda inceledik ve neler yapabileceğimizi gördük. Ayrıca uygulamalarımızda MVC tasarımını kullandığımızda daha önceden oldukça fazla uğraştığımız işlemleri ne kadar kolaylıkla yapabildiğimizi defalarca yapmış olduğumuz pratiklerde doğrulamış olduk. Bu yazımızda ise Asp.Net sayfalarında tasarım standardı açısından sıkça kullandığımız Master Pages kavramını MVC Framework 'te kullanım yapısını detaylı olarak inceliyor olacağız.

Asp.Net uygulamaların Master Page kullanmamızın sebebini kısaca hatırlayacak olursak. Benzer tarzda tasarım şablonu kullanacak olduğumuz sayfalarda tekrardan o tasarımsal öğeleri serverdan çağırıp sayfa üzerinde yüklenmemesini beklememek için oluşturulmuş başarılı bir yapıdır. Özellikle tasarım yapısı üzerinde herhangi bir değişiklik yok ise ve sadece içerikler değişecekse çok önemli bir tercih durumundadır. Aynı Asp.Net uygulamalarında olduğu gibi MVC uygulamalarında da Master Page kavramı önemli bir yer tutmaktadır. Varsayılan olan mavi bir tasarımı olan Master Page kullanılmaktadır. Peki biz bu tasarımı değil de kendi tasarımımızı Master Page 'e taşımak istersek neler yapmamız gerekir. Gelin hep birlikte bunu incelemeye çalışalım.

Her zaman olduğu gibi ilk olarak yapmamız gereken Visual Studio 2008 SP1 geliştirme ortamında bir Asp.Net MVC Framework Web uygulaması oluşturuyoruz. Proje dosyasının içerisinden ilk olarak varsayılan olarak oluşturulan Master Page 'in yerini bulalım.



MVC tasarımını dikkate alarak araştırmalarımızı yaptığımızda görsel öğelerin view katmanında olduğunu görürüz. Master Page her yerde kullanılacak bir içerik olduğu içinde view içerisinde Shared klasörünün içerisinde yer alır.

Biz uygulamamızda bu otomatik olarak oluşturulan Master Page 'i kullanmak yerine kendimize bir tane oluşturalım. Ama ilk olarak oluşturulmuş olan Site.Master 'ı siliyoruz. Sonrasında da Add- NewItem yolunu izleyerek web sitesine yeni bir Master Page ekliyoruz.



Master Page 'i sorunsuzca projeye ekledikten sonra nasıl bir içerik oluşturulacağının düşünülmesinin zamanı gelmiştir. Basit olması açısında içeriğe göre değiştirilebilir iki sütun ekleyelim. Bu değişen içerik eklemeyi Asp.Net 'ten de alışık olduğu gibi <asp:ContentPlaceHolder> yardımı ile yapacağız. İçeriği arttırdıktan sonra temel CSS ile Master Page 'in içerisine stil özellikleri de ekliyoruz ve bu yaptığımız işlemler sonrasında elimizdeki değişikler aşağıdaki gibi olacaktır.

View\Shared\Site.Master
<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
   <title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>

   <style type="text/css">
      html
      {
         background-color:Gray;
      }

      .column
      {
         float:left;
         width:300px;
         border:solid 1px black;
         margin-right:10px;
         padding:5px;
         background-color:white;
         min-height:500px;
      }

   </style>
   <asp:ContentPlaceHolder ID="head" runat="server">
   </asp:ContentPlaceHolder>
</head>
<body>
   <h1>Sitem</h1>

   <div class="column">
      <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
      </asp:ContentPlaceHolder>
   </div>
   <div class="column">
      <asp:ContentPlaceHolder ID="ContentPlaceHolder2" runat="server">
      </asp:ContentPlaceHolder>
   </div>
</body>
</html>

Eklemiş olduğumuz içerik kontrollerini body 'nin içerisine <div> taglarının arasına yerleştiriyoruz. Ayrıca div taglarının sütün gibi hareket etmesi için stil özelliğine daha önceden hazırlamış olduğumuz column 'u tanımlıyoruz. Yaptığımız bu işlemler sonucunda Master Page 'in görünümü aşağıdaki gibi olacaktır.



Basit fakat bizlerin hazırlamış olduğu bir Master Page 'e sahip olduk.

Master Page 'i Kullanan View Sayfa Oluşturmak

Asp.Net uygulamalarında Master Page 'ten yararlanarak web sayfaları oluşturabilmemiz mümkündü. Bunun bize en büyük yararı oluşturmuş olduğumuz sayfalarda Master Pages üzerinde belirtmiş olduğumuz içerik kontrol alanlarının düzenlenebilmesi ve diğer taraflar üzerinde herhangi bir değişiklik yapmadan hazırlıyor olmamızdır. MVC Framework 'ten yararlanarak hazırlamış olduğumuz web sayfalarında da Master Page Asp.Net uygulamalarında olduğu gibi kullanılmaktadır.

View klasörünün içerisinde yer alan Home klasörünün üzerinde sağ tıklama sonrasında karşımıza gelen menüden Add - NewItem seçeneğine tıklayarak ekrandan MVC View Content Page i seçerek Master Page 'i kullanabileceği bir web sayfa oluşturmaya başlıyoruz.


Sayfa tipimizi seçip ekle dediğimizde hazırlamış olduğumuz Master Page 'ler den istediğimizi seçmek için bir ekran çıkacaktır.


Tamam dedikten sonra artık Master Page 'i kullanan bir web sayfası uygulamamızda oluşturulmuş olacaktır. Oluşturulan sayfanın arka plan kodları aşağıdaki gibi oluşturulmuştur.

View\Home\Index.aspx
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="head" runat="server">
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
</asp:Content>
<asp:Content ID="Content4" ContentPlaceHolderID="ContentPlaceHolder2" runat="server">
</asp:Content>

Standart olarak projelere eklenmiş olan *.aspx sayfalardan farklı olarak HTML ve Body tagları yer almaktadır. Bu taglar Master Page de oluşturulduğu için yalnızca belirttiğimiz içerik kontrol alanları sayfada gözükmüştür ve sayfa içerisinde kullanıcılara göstermek istediğimiz içerikleri buralardan yayınlarız. Örnek olması açısında body taglarının içerisine eklemiş olduğumuz <asp:content> 'lerin içerisinde biraz değişiklik yaparak aldığımız sonuca hep birlikte çok atalım.

View\Home\Index.aspx
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="head" runat="server">
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
   İlk içerik kontrol alanı
</asp:Content>
<asp:Content ID="Content4" ContentPlaceHolderID="ContentPlaceHolder2" runat="server">
   İkinci içerik kontrol alanı
</asp:Content>

Yaptığımız düzenleme sonrasında ekran değişikliğimiz aşağıdaki gibi olacaktır.



Yapılmış olan değişikler sonrasında web sayfamızın içeriği istediğimiz gibi olmuştur.

Master Page yardımı ile oluşturulan view sayfanın içeriğini düzenlemek

İlk olarak düzenleme yapağımız yer sayfamızın başlığı olacaktır. Oluşturmuş olduğumuz web sayfası internet tarayıcısında açıldığında pencerenin en üst kısmında yer alan bölümü düzenleyelim. Asp.Net ten ve diğer bütün web sayfası kodlama yapılarından alışık olduğumuz üzere title bölümünde belirtmemiz yeterli olacaktır.

Sayfamızın markup kod tarafında en üstte yer alan <Page> </Page> bölümün aralığının içerisinde yer alan title özelliğini değiştirerek sayfa başlığını sorunsuzca oluşturmuş oluruz.


Yapmış olduğumuz işlemi hazırlanmış olan Master Pages ile ilişkilendirerek tekrardan yapalım. Bu durumda ilk olarak oluşturulan Master Pages içerisine koyduğumuz içerik kontrol alanlarını hatırlamamız gerekir ve bir tane head bölümünün içerisine <asp:contentplaceholder> ekledik. Alana başlık, meta ve diğer birçok kriteri ekleyebilmemiz mümkündür. Bu özellikten yararlanarak aspx sayfasının içerisinde aşağıdaki değişiklikleri uyguluyoruz.

View\Home\Index.aspx
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
   <title>Başlık buraya yazılacak</title>
   <meta name="description" content="İçindekiler buraya" />
   <meta name="keywords" content="ana arama kriterleri buraya" />

</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="head" runat="server">
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
   İlk içerik kontrol alanı
</asp:Content>
<asp:Content ID="Content4" ContentPlaceHolderID="ContentPlaceHolder2" runat="server">
   İkinci içerik kontrol alanı
</asp:Content>

Asp.Net uygulamaları ile uygulamadığımız tekniklerin birebir aynısıdır. Bu örneklerin bu kadar detaylı olarak gösterilmesinin sebebi Master Pages kavramının temel yapısının alışık olduğumuz Asp.Net Master Pages kavramından bir farkının olmadığını da göstermek amacıyladır. Yapmış olduğumuz işlemler sonrasında karşımıza gelen ekran görüntüsü aşağıdaki gibi olacaktır.



Yaptığımız işlemler sonucunda gayet başarılıyız. :)

Bu yazımızda Asp.Net MVC Framework ile hazırlanmış olan uygulamalarda Master Pages kavramını nasıl kullanabileceğimizi ve içerik düzenlemelerini nasıl kullanabileceğimizi detaylı bir biçimde incelemeye çalıştık. Bir sonraki MVC Framework yazımızda Master Pages kavramını incelemeye devam ederken veri tabanı işlemleri ile ilişkilerine de detaylı bir biçimde değineceğiz.

Umarım yararlı olabilmiştir.
info@turhaltemizer.com

Perşembe, Haziran 18, 2009

Silverlight 2.0 – SQL İşlemleri için WCF ve Linq To SQL Kullanım

Silverlight uygulamları RIA yapısında oldukları için direk sql işlemleri ile çalışmak yerine bunları bir servis olarak oluşturup kullanmamız önerilmektedir. Bu sebepten ötürü veri tabanı işlemlerimizi yapmak için ister Linq Data Model oluşturduktan sonra WCF servisine querynin yapılacağı metota bağlyoruz. Sonrasında Silverlight projesine Web service referance olarak ekledikten sonra gerekli işlemleri yaparak istediğimiz data kontrollerine ekleyebilinmesi mümkündür.

Sırası ile yapılması aşağıdaki adımları uygularsanız sizin projenizde sorunsuz bir biçimde çalışacaktır.

  • Silverlight projesi oluşturuyoruz.
  • Oluşturulan web projesine Linq To SQL Data Model ekliyoruz.
  • Oluşturulan web projesine WCF Service ekliyoruz ve aşağıdaki kodları interface ve sınıfa ekliyoruz.

IService1.cs

using System.Collections.Generic;
using System.ServiceModel;

namespace SQLData.Web
{
    // NOTE: If you change the interface name "IService1" here, you must also update the reference to "IService1" in Web.config.
    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        List<Customer> GetCustomerGetByLastName(string lastName);
    }
}

Service1.cs

using System.Collections.Generic;
using System.Linq;

namespace SQLData.Web
{
    // NOTE: If you change the class name "Service1" here, you must also update the reference to "Service1" in Web.config.
    public class Service1 : IService1
    {

        #region IService1 Members

        public List<Customer> GetCustomerGetByLastName(string lastName)
        {
            var ctx = new DataClasses1DataContext();
            var matchingCustomer = from cust in ctx.Customers
                                   where cust.LastName.StartsWith(lastName)
                                   select cust;
            return matchingCustomer.ToList();
        }

        #endregion
    }
}

  • Yukarıdaki işlemleri gerçekledikten sonra webconfigte ws service content özelliğini basic olarak değiştiriyoruz.
  • Uygulamayı derliyoruz.
  • Sİlverlight uygulaması üzerine ilk olarak Silverlight.Data.Control assembly ‘ını projeye ekliyoruz. Bu işlem silverlight uygulamasında DataGrid kontrolünü kullanabilmenize olanak tanıyacaktır.
  • Silverlight uygulamasına WCF service ini referans olarak ekliyoruz.
  • Sonrasında Page.xaml ve page.xaml.cs ‘e aşağıdaki kod bloklarını ekliyoruz

Page.xaml

<UserControl x:Class="SQLData.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:my="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
    Width="700" Height="300">
    <Grid x:Name="LayoutRoot" Background="White" ShowGridLines="True">
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition />
            <RowDefinition Height="30" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>       
        <TextBlock Text="Lütfen Arayacağınız kişinin soyadını giriniz.." Grid.Column="0" Grid.Row="0" HorizontalAlignment="Left"/>
        <TextBox x:Name="txtLastName" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Right" Width="150" Text="Lütfen arayın..."/>
        <my:DataGrid x:Name="grd" AlternatingRowBackground="Beige" AutoGenerateColumns="True" Width="700" Height="200" CanUserResizeColumns="True" Grid.Row="1"/>
        <Button x:Name="btnClick" Grid.Row="2" HorizontalAlignment="Center" Content="Ara"/>
    </Grid>
</UserControl>

Page.xaml.cs

using System;
using System.Windows;
using System.Windows.Controls;

namespace SQLData
{
    public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();
            Loaded += new RoutedEventHandler(Page_Loaded);
        }

        void Page_Loaded(object sender, RoutedEventArgs e)
        {
            btnClick.Click += new RoutedEventHandler(btnClick_Click);
        }

        void btnClick_Click(object sender, RoutedEventArgs e)
        {
            ServiceReference1.Service1Client webService = new SQLData.ServiceReference1.Service1Client();
            webService.GetCustomerGetByLastNameCompleted += new EventHandler<SQLData.ServiceReference1.GetCustomerGetByLastNameCompletedEventArgs>(webService_GetCustomerGetByLastNameCompleted);
            webService.GetCustomerGetByLastNameAsync(txtLastName.Text);
        }

        void webService_GetCustomerGetByLastNameCompleted(object sender, SQLData.ServiceReference1.GetCustomerGetByLastNameCompletedEventArgs e)
        {
            grd.ItemsSource = e.Result;
        }
    }
}

Yukarıdaki işlemleri adım adım yaptıktan sonra projemizi çalıştırdığımız da sorunsuzca uygulamamız çalışacaktır. Yaptığımız işlem ise soyadını girdiğimiz kullanıcıları DataGrid kontrolünde listelemektir.

Not: Linq To Sql Data Model de SQL Server 2008 veri tabanı örneklerinden AdventureWorks_LT veri tabanından Customer tablosunu kullandık.

Herkese mutlu günler diliyorum.

Perşembe, Mayıs 21, 2009

Visual Studio 2010 Beta1 – Kurulum ve Genel Bakış

Gün içerisinde çalışmanın dışında bir de Visual Studio 2010 ‘u kurmakla zaman harcadım. Web installer versionu ile kurduğum VS ‘in kurulum ile ilgili ekran görüntülerini size yazının devamında sunuyor olacağım. Bir çok yenilikle bizlere sunulacak olan VS2010 ilk Beta1 sürümü ile karşımızda. F# ‘tan paralel programlamaya ide güncellemesine kadar nice yeni özelliklerle biz yazılımlacıları mutlu etmeye geliyor.

Kurulum aşaması;

yeni1

yeni2

yeni3

yeni4

yeni5

Yukarıdaki işlemler sonucunda uygumala kurulmuş oluyor. Kurulum sonrası bizi karşılayan ekrandaki değişiklik oldukça köklüdür.

yeni6

yeni7

Zaman içerisinde daha detaylı bilgi vereceğim.

Cuma, Nisan 24, 2009

Microsoft .Net Framework 3.0 & 3.5 Full Download

.Net ile yazılım geliştiricilerin özelliklede Framework 3.x ile geliştirenlerin başlıca sorunu kurulum dosyası hazırladıklarında .Net Framework ‘ü kurmak için internete bağlanmalarıdır. Çünkü kurulum dosyaları 2-3 MB arasıdır. Yavaş internet kullanıcılarında ve kurumsal şirketlerde projelerin çok ama çok geç kurulmasına sebep olmaktadır. Bu sebepten ötürü sizlere .Net 3.x Framework ‘lerin tam kurulabilir paketlerinin linklerini sizlere sunuyorum. Umarım yararlı olur.

Herkese iyi çalışmalar, kolay kurumlar diliyorum. :)

Salı, Mart 31, 2009

YTÜ Semineri ardından

Merhabalar,

Bu hafta sonu güzel bir etkinliği daha geride bıraktık. Yıldız Teknik Üniversitesinde gerçekleşen etkinlikte sql server ‘dan crm ‘e, WPF ‘den MultiPoint ‘e ve daha bir çok konuya değinildi. Seminer içeriğinde bende WPF ‘i anlattım. Genel olarak izleyicilerin görüşleri oldukça güzeldi. Seminer sonunda katılımcıların gözlerinin içinin güldüğünü görmek benim içinde oldukça iyi bir durumdur.

Seminerde anlattığım sunuya linkten erişebilirsiniz. Bir sonraki seminerde sizlerle görüşmek üzere, iyi çalışmalar…

Salı, Mart 24, 2009

YazGeliştir Seminerleri @İstanbul - Yıldız Teknik Üniversitesi

Merhabalar,

Benim konuşmacıları arasında olduğum Yazgelistir ekibi 28 Mart Cumartesi günü Yıldız Teknik Üniversitesinde olacağız. Benim anlatacağım konu Windows Presentation Foundation. Etkinlik ile ilgili duyuruyu yazının devamında okuyabilirsiniz. Katılımlarınızı büyük bir merak ve heyacanla bekliyoruz…

Duyuru;

Yazgeliştir, 2009 seminer turuna Yıldız Teknik Üniversitesi'nden devam ediyor. 28 Mart 2009 Cumartesi günü tüm gün sürecek olan, altı konuşmacının yedi ayrı konuyu anlatacağı seminerlerde sizde yerinizi alın!
Etkinlik Tarihi: 28 Mart 2009 Cumartesi

Etkinlik Programı:

09:30-10:15:    Mustafa Acungil - İş zekası nedir?
10:15-10:30:    ARA
10:30-11:15:    Mustafa Acungil - SQL Server 2008 İle Gelen Yeni Veri Tipleri
11:15-11:30:    ARA
11:30-12:15:    Volkan Korkmaz - Dynamics CRM 4.0
12:15-13:00:    YEMEK ARASI
13:00-13:45:    Turhal Temizer - Windows Presentation Foundation
13:45-14:00:    ARA
14:00-14:45:    Burak Batur - Microsoft Office SharePoint Server 2007
14:45-15:00:    ARA
15:00-15:45:    Daron Yöndem - MultiPoint Programlama
15:45-16:00:    ARA
16:00-16:45:    Emre Coşkun - Composite WPF

Etkinlik Yeri :
Yıldız Teknik Üniversitesi Beşiktaş Kampüsü, YTÜ C Blok Konferans Salonu

Perşembe, Mart 19, 2009

WPF AutoComplete Folder TextBox

Introduction

This article demos how to create a TextBox that can suggest items at runtime based on input, in this case, disk drive folders.

Background

There is a number of AutoComplete TextBox implementation around but some don't support data binding, others don't support runtime items polling and so on. After lot of research and Googling, I decided to write my own instead of continue looking for one.

My Design process

My first design is based on ComboBox, I copy the default template and remove the drop down button and develop from that, it doesnt work because combobox have it's own autocomplete mechanism which will change the selection of textbox when Items is changed, it's not designed for Items that change at realtime.

So the second design is based on TextBox, I create the following style :

<Style x:Key="autoCompleteTextBox" TargetType="{x:Type TextBox}"><Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type TextBoxBase}"> <Grid x:Name="root"> <ScrollViewer Margin="0" x:Name="PART_ContentHost"/> <Popup x:Name="PART_Popup" AllowsTransparency="true"

Placement="Bottom" IsOpen="False" PopupAnimation="{DynamicResource

{x:Static SystemParameters.ComboBoxPopupAnimationKey}}"> <ListBox x:Name="PART_ItemList" SnapsToDevicePixels

="{TemplateBinding SnapsToDevicePixels}" VerticalContentAlignment="Stretch" HorizontalContentAlignment

="Stretch" KeyboardNavigation.DirectionalNavigation="Contained" /> </Popup> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>

and then create a custom control and hook the style to it :

<TextBox x:Class="QuickZip.Controls.SelectFolderTextBox" Style="{DynamicResource autoCompleteTextBox}" >...
</TextBox>

PART_ContentHost is actually a control that TextBoxView, it is required for TextBox template
(with that name), or the control won't function, the another two part (PART_Popup and PART_ItemList)
is defined so I can use them in the custom control :

public partial class SelectFolderTextBox : TextBox

{

Popup Popup { get { return this.Template.FindName("PART_Popup", this) as Popup; } }

ListBox ItemList { get { return this.Template.FindName("PART_ItemList", this) as ListBox; } }

ScrollViewer Host { get { return this.Template.FindName("PART_ContentHost", this) as ScrollViewer; } }

UIElement TextBoxView { get { foreach (object o in LogicalTreeHelper.GetChildren(Host)) return o as UIElement; return null; } }

}

AutoCompleteTextBox

If text is changed, the suggestion item list is updated as well :

protected override void OnTextChanged(TextChangedEventArgs e)

{

if (_loaded)

{

try

{

if (lastPath != Path.GetDirectoryName(this.Text))

{

lastPath = Path.GetDirectoryName(this.Text);

string[] paths = Lookup(this.Text);

ItemList.Items.Clear();

foreach (string path in paths)

if (!(String.Equals(path, this.Text, StringComparison.CurrentCultureIgnoreCase)))

ItemList.Items.Add(path);

}

Popup.IsOpen = true;

//I added a Filter so Directory polling is only called once per directory, thus improve speed

ItemList.Items.Filter = p =>

{

string path = p as string;

return path.StartsWith(this.Text, StringComparison.CurrentCultureIgnoreCase) &&

!(String.Equals(path, this.Text, StringComparison.CurrentCultureIgnoreCase));

};

}

catch

{

}

}

}

A number of handlers is then defined :

public override void OnApplyTemplate()

{

base.OnApplyTemplate();

_loaded = true;

this.KeyDown += new KeyEventHandler(AutoCompleteTextBox_KeyDown);

this.PreviewKeyDown += new KeyEventHandler(AutoCompleteTextBox_PreviewKeyDown);

ItemList.PreviewMouseDown += new MouseButtonEventHandler(ItemList_PreviewMouseDown);

ItemList.KeyDown += new KeyEventHandler(ItemList_KeyDown);

}

AutoCompleteTextBox_PreviewKeyDown :

if user press down button, the textbox will move focus to the Listbox, so the user can choose an item from it, this is placed in PreviewKeyDown instead of KeyDown because TextBox's mechanism will consume the event before it reach KeyDown if the button is down button.

void AutoCompleteTextBox_PreviewKeyDown(object sender, KeyEventArgs e)

{

if (e.Key == Key.Down && ItemList.Items.Count > 0 && !(e.OriginalSource is ListBoxItem))

{

ItemList.Focus();

ItemList.SelectedIndex = 0;

ListBoxItem lbi = ItemList.ItemContainerGenerator.ContainerFromIndex(ItemList.SelectedIndex) as ListBoxItem;

lbi.Focus();

e.Handled = true;

}

}

AutoCompleteTextBox_KeyDown

if user press <enter> button, the textbox will close the popup and update the binding.

void AutoCompleteTextBox_KeyDown(object sender, KeyEventArgs e)

{

if (e.Key == Key.Enter)

{

Popup.IsOpen = false;

updateSource();

}

}

ItemList_PreviewMouseDown and ItemList_PreviewMouseDown

if user press <enter> button (or select by mouse), the text textbox will be updated with ListBox.SelectedValue, and then update the binding.

void ItemList_KeyDown(object sender, KeyEventArgs e)

{

if (e.OriginalSource is ListBoxItem)

{

ListBoxItem tb = e.OriginalSource as ListBoxItem;

Text = (tb.Content as string);

if (e.Key == Key.Enter)

{

Popup.IsOpen = false;

updateSource();

}

}

}

void ItemList_PreviewMouseDown(object sender, MouseButtonEventArgs e)

{

if (e.LeftButton == MouseButtonState.Pressed)

{

{

TextBlock tb = e.OriginalSource as TextBlock;

if (tb != null)

{

Text = tb.Text;

updateSource();

Popup.IsOpen = false;

e.Handled = true;

}

}

}

updateSource is required because I bound text's UpdateSourceTrigger as Explicit, if updateSource is not called it wont update the text :

void updateSource()

{

if (this.GetBindingExpression(TextBox.TextProperty) != null)

this.GetBindingExpression(TextBox.TextProperty).UpdateSource();

}

The component is working now, but if you want to add validation as well, read below :

To support validation, a Validation Rule is written :

If the path is not found or an exception raised when looking up, it will return ValidationResult false, the error will be

accessed by using the attached properties Validation.Errors and Validation.HasError.

public class DirectoryExistsRule : ValidationRule

{

public static DirectoryExistsRule Instance = new DirectoryExistsRule();

public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)

{

try

{

if (!(value is string))

return new ValidationResult(false, "InvalidPath");

if (!Directory.Exists((string)value))

return new ValidationResult(false, "Path Not Found");

}

catch (Exception ex)

{

return new ValidationResult(false, "Invalid Path");

}

return new ValidationResult(true, null);

}

}

and change the binding : (to use the created Validation rule, noted that UpdateSourceTrigger is Explicit. )

<local:SelectFolderTextBox x:Name="stb" DockPanel.Dock="Bottom" Margin="4,0,0,0">

<local:SelectFolderTextBox.Text>

<Binding Path="Text" UpdateSourceTrigger="Explicit" >

<Binding.ValidationRules>

<t:DirectoryExistsRule />

</Binding.ValidationRules>

</Binding>

</local:SelectFolderTextBox.Text>

</local:SelectFolderTextBox>

Now the textbox show a red border if directory not exists. As a red border isnt clear enough, we can change the behavior :

to disable the default red border:

<Style x:Key="autoCompleteTextBox" TargetType="{x:Type TextBox}">

<...>

<Setter Property="Validation.ErrorTemplate">

<Setter.Value>

<ControlTemplate >

<AdornedElementPlaceholder />

<!-- The TextBox Element -->

</ControlTemplate>

</Setter.Value>

</Setter>

</Style>

then change the control template, which will show the dockWarning when Validation.HasError :

<ControlTemplate TargetType="{x:Type TextBoxBase}">

<Border Name="Border" CornerRadius="2" Background="{StaticResource WindowBackgroundBrush}"

BorderBrush="{StaticResource SolidBorderBrush}" BorderThickness="1" Padding="1" >

<Grid x:Name="root">

<...>

<DockPanel x:Name="dockWarning" Visibility="Collapsed" LastChildFill="False" >

<Border DockPanel.Dock="Right" BorderBrush="Red" Background="Red" BorderThickness="2" CornerRadius="2,2,0,0">

<TextBlock x:Name="txtWarning" DockPanel.Dock="Right" Text="{TemplateBinding ToolTip}" VerticalAlignment="Top"

Background="Red" Foreground="White" FontSize="10" />

<Border.RenderTransform>

<TranslateTransform X="2" Y="{Binding ElementName=dockWarning, Path=ActualHeight,

Converter={x:Static t:InvertSignConverter.Instance}}"/>

<!--TranslateTransform move the border to upper right corner, outside the TextBox -->

<!--InvertSignConverter is a IValueConverter that change + to -, - to + -->

</Border.RenderTransform>

</Border>

</DockPanel>

</Grid>

</Border>

<ControlTemplate.Triggers>

<MultiTrigger>

<MultiTrigger.Conditions>

<Condition Property="Validation.HasError" Value="true" />

<Condition SourceName="PART_Popup" Property="IsOpen" Value="False" />

</MultiTrigger.Conditions>

<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>

<Setter TargetName="dockWarning" Property="Visibility" Value="Visible" />

<Setter TargetName="Border" Property="BorderThickness" Value="2" />

<Setter TargetName="Border" Property="Padding" Value="0" />

<Setter TargetName="Border" Property="BorderBrush" Value="Red" />

</MultiTrigger>

</ControlTemplate.Triggers>

</ControlTemplate>

Pazar, Nisan 20, 2008

WPF – Uygulamalara Video Kontrolleri Eklemek

2005 yılında Windows Presentation Foundation ile ilgili söylentiler ortaya atıldığında, Adobe Flash ile yapabildiğimiz bütün işlemleri ve daha fazlasını masaüstü ve web uygulamalarında yapabileceğimizden söz edilmişti. Daha sonra .Net Framework 3.0 ‘ın ilk sürümleri olarak .Net FX bizlere sunulduğundan bu söylentilerin gerçekleşebileceği düşünceleri zihnimizde canlanmaya başlamış ve özellikle masaüstü uygulamalarda devrim niteliği taşıyabileceğini düşünmeye başlamıştık. Belirli zaman geçtikten sonra WPF ‘i yalnızca masaüstü uygulamalar için kullanılacağına ve web uygulamalarının isminin de Microsoft Silverlight olacağını gözlemlemiş olduk. WPF ile de web uygulamaları yayınlamak hala mümkün olmakta ve bu tür uygulamalara da XBAP uygulamalar denilmiştir. Web tarafında Silverlight ‘ın estirdiği fırtınayı masaüstü uygulamalarında da WPF gerçekleştirmeye başlamıştı.

WPF ile gelen ve dünya da en çok kullanılan ürünler ile Windows Vista, Office System 2007 ve Windows Live Messenger 8.0 ve sonrası olmuştur. Karşımıza gelen bu ürünlerin ortak özellikleri tasarımlarının bir önceki sürümlerine oranla inanılmaz değişikler gösterebilmiş olmasıdır. Özelliklere kısaca örnek vermek gerekirse, Windows Vista ile izleyeceğimiz bir videoyu herhangi bir oynatıcı açmadan izleyebiliyor olmamızdır. Bu ve bunun gibi birçok işlemin ana özelliği uygulamalarımızda yer alan kontrolleri dinamik bir biçimde kullanabiliyor olmamızdır.



Windows Vista ile nasıl video kontrollerini kullanıldığını yukarıdaki ekran görüntüsünden bakabiliriz. Son kullanıcılar bu tür kontrolleri kullandıkça bizlerden geliştirmemizi istedikleri uygulamalarda bu tür kullanım kolaylıklarının olmasını istemektedirler. Bizlerde bu tür istekleri uygulamamızın performansını da düşünerek gerçekleştirmeye çalışırız.

Windows kontrollerinin daha iyileştirilmiş ve yeni nesil sistemler ile uyumlu olabilmesi açısından WPF ile geliştirmek bizler için her daim ayrıcalıklı bir durum olacaktır. Ayrıcalıklı durumdan kastımız, uygulamalarımızda ne kadar en son teknoloji kullanırsak son kullanıcılar tarafında o kadar coşku uyandırırız. Müşteri memnuniyetine dayalı bir dünyada yaşadığımız içinde bu bize çok daha fazlası ile katma değer olarak geri dönecektir.

Kısaca neden yeni kontrolleri kullanmamız gerektiğini ve bu kontrollerden video kontrolünün Windows Vista işletim sistemi ile nasıl gözüktüğüne göz attıktan sonra bu makalemizde değinecek olduğumuz WPF uygulamalarına Media kontrollerinin eklenmesi konusunu incelemeye başlayabiliriz.

Win32 uygulamalarımızı dijital Media kontrollerinin eklemenin iki yolu vardır. Bunlardan birincisi animasyon işlemlerini daha canlı göstermek ve video kontrollerini uygulamalarımızda kullanmak için başvurduğumuz DirectShow Api leridir. Bir diğeri ise Windows Media Player ActiveX kontrolüdür. Bu iki kontrolü de uygulamamıza gömülü bir biçimde uygulamalarımızda kullanabiliyoruz.

“Peki, bu iki kontrol varken neden WPF ‘te kullanılmakta olan kontrollerin bizlere sunulduğu” sorusunun cevabını araştırmaya çalışalım. DirectShow ile geliştiricilere Media uygulamaları için inanılmaz fazlalıkla imkânlar sağlanmaktadır. Tabii bu imkânları kullanabilmemiz için bu api hakkında geniş bir bilgi yelpazesine sahip olmamız gerekiyordu. Uygulamalarımızda yalnızca tek bir video göstermek içinde geliştiricilere bu api hakkında geniş bilgi düzeyine sahip olmalarını istemek geliştiriciler için oldukça sıkıcı bir durum oluyordu.

WPF ‘de ise DirectShow api lerinde yer alan kontrollerinin benzerlerini .Net Framework 3.0 ile birlikte gelen özellikler ile rahatlıkla gerçekleştirebilmemiz mümkündür. WPF ile gelen kontrolleri de Win32 uygulamalarında sorunsuzca kullanabiliriz.

Media oynatmak içinde Windows Media Player ActiveX kontrolüne ait özelliklere hâkim olmamız gerekmektedir. Yine geliştiricilere ekstra bir şeyler öğrenmeye zorlamak yerine WPF ile .Net Framework yapısına eklenen MediaElement kontrolü yardımı ile bu işlemleri de sorunsuzca gerçekleştirebilmemiz mümkündür. Kullandığımız bu kontrol yardımı ile videolarımızı 3D yüzeylerdeymiş gibi de kullanabilmemiz mümkündür.

Ses ve Media kontrollerini geliştiriciler kullanmak istediğinde WPF ile sunulan Media özellikleri olduğundan bahsetmiştik. Bu Media özelliklerini kullanmak istediğimizde arka planda Media Player işlemini başlatarak işlemlerimizi gerçekleştirebilmemize olanak sağlanmaktadır. WPF uygulaması açıldıktan sonra Media uygulaması olan sayfada yer alan Media dosyası yüklendikten sonra Media Player ‘ın açabileceği bir format olup olmadığını kontrol ediyor. Eğer bu formatlar dışında bir dosya açmak istiyorsak uygulama bizden codec yüklememizi istemektedir.

Media uygulamaları olan uygulamalarımızdaki dosyalar gösterilirken arka planda dosyalarımızın yüklenmesine devam edilmektedir. Bu yüklenme işlemi biz video ‘yu duraklatsak bile yüklemeye devam edecektir. Video ‘nın yüklenmesini engellemek için yapmamız gereken işlem tamamen durdurmak olacaktır. Ayrıca, ses dosyalarını çaldırabilmemiz için System.Media.SoundPlayer sınıfını da kullanabilmemiz mümkündür.

MediaElement Kontrolünün Kullanılması

Windows Presentation Foundation ile Media dosyalarını göstermenin oldukça kolay olduğundan ve bu tür işlemleri yapabilmek içinde MediaElement kontrolünün gerekli olduğundan üst paragraflarda bahsetmiştik. Bu kontrolü XAML kod tarafında ekleyebileceğimiz gibi C# veya VB.Net kod tarafında MediaElement kontrolünü aktifleştirerek kullanabilmemiz mümkündür. MediaElement kontrolünü uygulamalarımızda kullanırken ya bağımsız olarak kontrollerimizden aldığımı değeler yönetiriz ya da sistem saatinden alarak işlemlerimizi yapmamıza olanak tanıyacaktır.

Bağımsız durumda Media elementi nasıl kullanacağımıza göz atalım;

XAML
<MediaElement Source="Forza.wmv" Width="320" Height="240" LoadedBehavior="Play" />

XAML kod bloğunu uygulamamıza yazıp derlediğimiz zaman uygulamamızın içerisinde videomuzun görüntülendiğine tanık olacağız.

Not: Eğer videonuzun yolunu doğru bir şekilde belirttiğiniz halde gözükmediyse muhakkak projenizin oluştuğu klasörün altında yer alan Debug dizininin altında izlettireceğiniz videonun yer almasına dikkat ediniz.

MediaElement kontrolünün performans açısından çok daha fazla olanaklarının olduğundan ve diğer Api lere oranla işlemciyi daha az yoracağından bahsetmiştik. Şimdi bu söylemlerimizi doğrular nitelikteki ekran görüntülerini veriyoruz. Bu ekran görüntüsünde MediaElement yardımı ile oluşturduğumuz videonun yanında görev yöneticisi yer almaktadır.



MediaElement kontrolümüzü kullanarak çok basit anlamda bir uygulama geliştirdik ve bu uygulamamızı görüntüledik. Şimdi yapacağımız örnekte ise başlat, duraklat, bitir sessiz butonlarının olduğu bir örnek olacaktır. Bu örneğimizde kontrolleri XAML kod tarafında hazırlarken kontrollere verilecek olan görevleri C# kod tarafında oluşturuyor olacağız. Ayrıca kontrollerimizin kullanıcılar tarafından kullanıldığından videoyu kontrol edebilmesi için LoadBehavior özelliğine Manual ‘i atayacağız.

XAML
<Window x:Class="WpfMedia.Window1"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   Title="MediaElement Makale" Height="800" Width="600">
   <Grid>
      <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
         <MediaElement Name="MediaMakale" Source="Forza.wmv"
            LoadedBehavior="Manual" Width="320" Height="240" />
         <StackPanel Orientation="Horizontal" Margin="0,10,0,0">
            <Button Content="Oynat" Margin="0,0,10,0"
               Padding="5" Click="mediaOynat" />
            <Button Content="Duraklat" Margin="0,0,10,0"
               Padding="5" Click="mediaDuraklat" />
            <Button x:Name="sessizButton" Content="Sessiz"
               Padding="5" Click="Sessiz" />
         </StackPanel>
      </StackPanel>
   </Grid>
</Window>

C#
susing System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace WpfMedia
{
   public partial class Window1 : System.Windows.Window
   {
      public Window1()
      {
         InitializeComponent();
         mediaMakale.Volume = 100;
         mediaMakale.Play();
      }

      void mediaOynat(Object sender, EventArgs e)
      {
         mediaMakale.Play();
      }

      void mediaDuraklat(Object sender, EventArgs e)
      {
         mediaMakale.Pause();
      }

      void mediaMute(Object sender, EventArgs e)
      {
         if (mediaMakale.Volume == 100)
         {
            mediaMakale.Volume = 0;
            sessizButton.Content = "Sesli";
         }
         else
         {
            mediaMakale.Volume = 100;
            sessizButton.Content = "Sessiz";
         }
      }
   }
}



MediaElement, zaman durumuna bağlı olarak Windows Presentation Foundation ‘ta yer alan animasyon mimarisine bağlı olarak ta kullanılabiliyor. Zaman durumunu aktifleştirdiğimizde MediaElement ‘in özelliklerinden olan MediaTimeline ve MediaClock özelliklerine başlangıç ve ilerleme değerleri aktararak kullanabiliyoruz. Uygulamalarımızda başlangıç zamanı ve hangi zaman aralıkları ile ilerleyeceği ile ilgili özellikler Windows Presentation Foundation ‘da animasyon işlemlerini yaparken kullandığımız StoryBoard üyesine benzemektedir. Bu üyeyi de kullanırken belirli bir başlangıç zamanı, ilerleme zamanı ve sonlanma zamanı belirtebiliyorduk. Şimdi bu tür zamana bağlı kontrolleri kullanmak istediğimizde uygulamamızda ne tür bir kod bloğu kullanabileceğimize değinmeye çalışalım.

XAML
<MediaElement Name="mediaMakale" Width="320" Height="240" />
   <Grid.Triggers>
      <EventTrigger RoutedEvent="Grid.Loaded">
         <EventTrigger.Actions>
            <BeginStoryboard>
               <Storyboard Storyboard.TargetName="mediaMakale">
                  <MediaTimeline Source="Forza.wmv"
                     BeginTime="00:00:00" Duration="00:05:00" />
               </Storyboard>
            </BeginStoryboard>
         </EventTrigger.Actions>
      </EventTrigger>
   </Grid.Triggers>

Uygulamalarımızda MediaTimeline özelliğini kullanmak yerine MediaElement kontrolünü kullanmak ileride gerçekleşebilecek sorunları ortadan kaldırmak açısından çok daha yararlı olacaktır. Çünkü MediaTimeline ile uygulamalarımızı çalıştırmak istediğimizde arkada MediaElement kontrolü oluşturulmak istenecek ve istek sonrasından belirli zaman aralıklarında önbelleğe alacaktır. Sürekli çağırılan video dosyaları uygulamalarımızı web tabanlı yaptığımız durumlarda performans kaybına uğrayabilmekte ya da Windows tabanlı olarak geliştirdiğimiz uygulamalarda çok büyük dosyalı uygulamaları göstermek istediğimizde hatalar verebilecektir. Fakat aklımıza başka bir soru takılabilir. “O da çok büyük boyutlu bir videoyu birden önbelleğe aldırarak oynat dediğimizde performans kaybı yaşamayacak mıyız?” olacaktır. İşte bu sorunu engellememiz için MediaElement kontrolü içerisinde video dosyalarımızı oynatıyoruz. MediaElement kontrolünün Media dosyalarını çalıştırma mantığı minimum sistem zorlaması ile maksimum performans alabilmektedir. Diğer durumlarda biz yönetmeye çalışırsak, WPF öncesi durumlarda olduğu gibi oldukça zprlanabilir ve işlemlerimizi yaparken büyük sorunlar yaşabiliriz.

MediaPlayer Sınıfının Kullanılması

MediaElement kontrolünün özelliklerinde çok fonksiyonlu özellikler yani, yeniden oynatma, çalma listesi gibi özellikler yer almamaktadır. MediaElement üzerinde yer alan kontrollerimize daha çok fonksiyonellik kazandırmak istediğimizde MediaPlayer sınıfını kullanabiliriz. MediaElement ile video ve benzeri Media dosyaları oynatmak istediğimizde arka planda Media Player işleminin başlatıldığından ve bu işlemler üzerinden videomuzu oynattığından bahsetmiştik. İşte bu sebepten ötürü uygulamalarımızda MediaPlayer sınıfını kullanmak performans kaybı yerine son kullanıcıya sunacağımız birçok özelliği kolaylıkla sunabilme olanağı tanımaktadır.

MediaPlayer sınıfını kullanmamız için uygulamamızda System.Windows.Media isim alanının kullanılıyor olmasına dikkat etmemiz gerekmektedir. Şimdi ise C# ile Media Player sınıfını nasıl kullanabileceğimize ilişkin bir örnek vereceğiz.

C#
MediaPlayer mp = new MediaPlayer();
   Try {
      mp.Open(new Uri("mediaMakale.wmv"));
      }
     catch(Exception ex) {
    MessageBox.Show(ex.Message);
}

Media Player sınıfı ile kullanabildiğimiz özellikler ve üyeler MediaElement kontrolünün içerisinde kullanılabilmektedir. MediaElement ve Media Player uyumu ile gerçekleştirdiğimiz uygulamalarda yalnızca HD videoların gösterilmesinde sorunlar çıkmaktadır. Bu sorunlarda sistem özelliklerimizden olduğu düşünülmektedir. Çünkü ekran kartı iyi bir sistem ile uygulamamızı çalıştırdığımızda herhangi bir sorun ile karşılaşmazken, daha düşük özellikleri sistemlerde denediğimizde hatalar verebilmektedir. Media Player sınıfı videolarımızı çalıştırırken VideoDrawing sınıfı yardımı ile bu işlemleri yapmaktadır. 3D yüzeylerde de video izlemek istediğimizde Media Player sınıfı yine VideoDrawing sınıfı yardımı ile kolaylıkla video dosyalarını gösterebilmektedir.

Media Player sınıfında MediaElement kontrolünde olduğu gibi direk yolu gösterebilmek yerine uri özelliği ile yolunu uygulamamızın içerisindeki yolunu belirtiyoruz.

WPF Kontrollerinde Gömülü Videoların Kullanımı

MediaElement kontrolü System.Windows.UIElement sınıfından miraslanarak oluşturulmaktadır. Miraslamadan ötürü bir kontrolü başka bir kontrolün içerisinde de kullanabilmemize olanak tanınmıştır. Bizde bu özelliği test edebilmek için Button kontrolünün içerisinden MediaElement kontrolünü çalıştırmayı deneyeceğiz. Bunun için XAML kod tarafına aşağıdaki kodları ekliyoruz.

XAML
<Button Name="videoButton" Click="videoButton_Click"
   Width="320" Height="240">
   <MediaElement Name="forzaVid" Source="Forza.wmv"
       LoadedBehavior="Play" Width="320" Height="240" />
</Button>



Bu uygulamamızı derlediğimiz anda video oynamaya başlamaktadır. Bunun sebebi LoadedBehavior özelliğine Play değerini atmamızdan kaynaklanmaktadır. Eğer ki biz uygulama başlar başlamaz değil de Butona tıklandığı zaman videoyu başlatmak istersek Button_onClick özelliğine MediaElement ‘e vermiş olduğumuz isim ile forzaVid.Play(); kodunu eklememiz yeterli olacaktır. Bu değişiklik sonrasında uygulamamız butona tıklandığı anda, buton içerisinde video gösteren bir hal almış olacaktır. Bu tür ince detaylar son kullanıcılara sunulacak olan uygulama kullanım zevkini maksimuma çıkaracağı kaçınılmaz bir gerçektir.

Islak Zemin Yansıması Teknikleri

Uygulamalarımızda suda ya da camda karşılaştığımız yansıma işlemlerini yapmaya çalışırken fazlaca zaman kaybına uğrardık. WPF ile bu işlemleri yapmak istediğimizde ise VisualBrush üyesine MediaElement ‘i bağlamamız yeterli olacaktır. Daha sonrasında ise, VisualBrush içerisinden yapacağımız ince detaylar ile yansıma işlemini gerçekleştirebiliriz.

Yansıma işlemlerini nasıl yaptığımız konusunda kafanızda herhangi bir soru işareti oluşursa daha önceki WPF makalelerimizi okuyabilirsiniz.

Şimdi ise ıslak zemin yansıma efektine benzeyen uygulamamızı yapmamıza yarayacak olan kodlarımızı verelim.

XAML
<Window x:Class="WpfMedia.Window3"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   Title="WPF Makale" Height="340" Width="350">
      <Grid x:Name="Grid">
         <StackPanel VerticalAlignment="Center">
            <MediaElement Name="Vid" Source="Forza.wmv"
               LoadedBehavior="Play" Width="320" Height="240" />
            <Rectangle Width="320" Height="240">
               <Rectangle.Fill>
                  <VisualBrush Visual="{Binding ElementName=Vid}" />
               </Rectangle.Fill>
               <Rectangle.OpacityMask>
                  <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                     <GradientStop Color="#AA000000" Offset="1" />
                     <GradientStop Color="#00000000" Offset="0" />
                  </LinearGradientBrush>
               </Rectangle.OpacityMask>
            <Rectangle.RenderTransform>
         <TransformGroup>
         <ScaleTransform ScaleY="-1" />
         <TranslateTransform Y="242" />
      </TransformGroup>
   </Rectangle.RenderTransform>
</Rectangle>
</StackPanel>
</Grid>
</Window>



Bu uygulamamızı da basitçe yapmış oluyoruz.

Uyumlu Ses Dosyalarının WPF Uygulamalarına Eklenmesi

WPF uygulamalarımıza MediaElement kontrolü yardımı ile birçok video ekledik ve eklediğimiz bu videoları form kontrollerinin içerisine bile ekleyebildik. Fakat dijital dünya sadece görüntüden ibaret değildir. Birde bu dünyada sesler yer almaktadır. Şimdi de ses dosyalarını nasıl WPF uygulamalarımıza ekleyebileceğimize değineceğiz.

Ses dosyalarımızı uygulamalarımızda kullanabilmemiz için System.Windows.Media isim alanı altında tanımlı olan sound Player sınıfını kullanacağız. Bu sınıfta Media Player ile çalabildiğimiz ses dosyalarımızı WPF uygulamalarımız da çalabilmemiz mümkündür. Ayrıca Media Player için varsayılan ses formatı olan WAV ‘ı da sorunsuzca çalıştırabilmektedir.

SoundPlayer sınıfını kullanırken bu sınıfın metotlarından olan SoundPlayer.Play(); metodu yardımı ile de ses dosyalarını yürütebilmemiz mümkündür. Tabii yürütebilme özelliği ile birlikte duraklatma ve sonlandırma metotları da yer almaktadır.

Şimdi uygulamalarımızda SoundPlayer özelliğini kullanabileceğimiz örnek için gerekli olan kod parçacığına göz atalım.

XAML
<EventTrigger RoutedEvent="Button.MouseEnter" SourceName="Button">
   <EventTrigger.Actions>
      <SoundPlayerAction Source="ses/lake.wav"/>
   </EventTrigger.Actions>
</EventTrigger>

Uygulamamızı derlediğimizde ses dosyamız sorunsuz bir biçimde çalacaktır.

Bu yazımızda nelere değindiğimizi toparlamak gerekirse, ilk olarak WPF ile geliştirilmiş ve günümüzde en çok kullanılan uygulamalara değindik ve bu uygulamalardan Windows Vista ‘da MediaElement kontrolünü nerede kullanıldığına göz attık. Sonrasında ise MediaElement kontrolü kullanılarak yapılmış farklı uygulamaları incelemeye çalıştık.

Sonuç olarak en temel anlamda MediaElement kontrolünü kullanarak video dosyalarını nasıl kullanabileceğimize incelemeye çalıştık.

Umarım yararlı olmuştur.

Yazımızda değinmiş olduğumuz uygulamanın kaynak kodlarına linkten erişebilirsiniz.

turhal.temizer@csharpnedir.com

Pazartesi, Nisan 14, 2008

WPF - 2D, 3D ve Yüzey (Topoloji) Uygulamaları

Windows Presentation Foundation ile yapılmış profesyonel projeleri incelediğimizde genellikle 3D işlemlerinin uygulandığını görüyoruz. Geliştiriciler uygulamalarında 3D ‘yi tercih etmelerinin sebeplerinin başında WPF ‘in grafik kartlarını başarılı bir şekilde kullanabilmesi gelmektedir. Bu başarıyı sağlayan en büyük etmen ise Windows Vista işletim sistemli bilgisayarlarda grafik kartını çok daha performanslı bir şekilde kullanabilmesidir. Vista işletim sisteminde animasyonların bir çoğunun WPF ‘te kullanılan sınıflar ile yapıldığını düşünürsek neden 3D işlemlere daha fazla yer verildiğini anlayabiliriz.

Günümüzde hazırlanan ve hazırlanmakta olan uygulamaları göz önüne alarak bizlerde uygulamalarımızda 3D işlemleri uygulamamız, yeni teknolojileri yakalamak açısından yararlı olacaktır.

Bu ve diğer bir çok sebebi düşünerek bu makalemizde Windows Presentation Foundation ile 3D uygulamalarını nasıl geliştireceğimizi ayrıntılı bir biçimde incelemeye çalışacağız.

Daha önceleri ayrıntılı bir 3D uygulaması geliştirmek istiyorsak yönetilebilir 3D programlama yapısını, vektörsel 2D programlama yapısını bilmemiz gerekiyordu. Tabii bu bilgileri bilmememiz durumunda uzunca bir süre bunları öğrenmek ile zaman kaybettikten sonra uygulamalarımızı geliştirmeye başlayabiliyorduk. Fakat çok daha ileri düzey uygulama geliştirmek istediğimizde harcayacağımız zaman aralığı oldukça artacak ve belki de hazırlayacak olduğumuz uygulama sonlandığında çalıştığımız sistemler güncelliğini kaybedecektir.

Fakat .net 3.0 yapısı ile bizlere sağlanan kolaylıkları kullanarak biraz önce bahsettiğimiz o zorlu 3D uygulamalarını rahatlıkla yapabilmemize olanak tanınmaktadır. Biz bu makalemizde 3D işlemlerimizi bir tutorial gibi en temelden ele alarak eğitsel bir biçimde inceleyeceğiz.

3D Grafik Teorisi

WPF ile 3D programlamaya başlarken nasıl yapacağı konusunda herhangi bir fikri olmayanlar için açıklamaya başlarsak, 3D modelleme ve çizim işlemlerini yaparken Mesh (ağ topluluğu) içerisine noktalarımızı yerleştirmeli, üçgen (triangle) ve doğrusal (normal) çizimler ile de istediğimiz şekli çizmeliyiz. Bu karmaşık gelen işlemleri çok az 3D modelleme bilgisi ile WPF ‘de çabucak işlemlerimizi yapabilmemiz mümkündür. Bu işlemleri yapmaya başlarken biraz önce saydığımız üç özelliği incelemek çok daha yararlı olacaktır.

Mesh Nedir?

Mesh, yüzeyi yeniden göstermek olarak kısaca tanımlayabiliriz. Çizimlerimizi yaparken köşe noktalarımızı belirler, en yüksek ve en alçak yükseltilerini belirleyerek basitçe çizim yapabiliriz.

Peki, “biz daha önceden mesh ‘i kullanmak isteseydik kullanamaz mıydık?” diye bir soru kafanıza takılırsa bunun cevabı evet olurdu. .net Framework 2.0 dan beri mobile uygulamalarda DirectX kütüphanesinde Direct3D nesnesini kullanabilmemiz mümkündür. Eğer ki mobile uygulamalarınızda mesh sınıfını kullanmayı düşünüyorsanız bu sınıf .net 3.5 ile de yapıda yer almıştır.

Mesh ‘i WPF üzerinden incelemeye devam edelim. Yüzey yüksekliğini minimum olarak belirlersek bizim karşımıza yalnızca düz bir yüzey çıkacaktır. Bu yüzeyde en basit anlamda bir üçgene benzer fakat yükseklikleri ve farklı koordinat noktalarını belirttiğimiz zamanda karşımıza küp görünümlü bir ekran görüntüsü alabiliriz. Biraz daha farklı düşünürsek, tek bir yükseklik değil de birden fazla düşünmemiz durumunda ise tümsekli bir yeryüzü yapısı elde edebilmemiz mümkündür.

Mesh ile çizim işlemlerimiz aşağıdaki üçlü sayesinde oluşturulabilmektedir.
Mesh pozisyonu
Triangle Indices
Üçgen standartları

Mesh Pozisyonu

Mesh pozisyonu, çalışacağımız yüzeyde belirlediğimiz noktalar sayesinde hangi alanda çalışacağımızı belirlememize yarar.

Triangle Indices

Mesh ile çalışırken temel olarak 3 nokta üzerinde çalışıp üçgen ‘e benzer çizim işlemleri yapabilmemiz mümkündür. Fakat biz bu üç noktadan daha fazla çalışmak istersek farklı köşegen noktaları belirlememiz gerekecektir.

WPF ile çalışırken ise bizden mesh ‘imizin koordinat noktalarını girmemiz istenir. Bizde bu noktaları diziye benzer bir şekilde uygulamamıza ekleriz. Örneğin, beş tane koordinat noktası eklemek istediğimizde {n0,n1,n2,n3,n4} biçiminde ekleyebiliriz. Burada n0..5 ile belirlediğimiz koordinatlar uygulamada point2d veya point3d koordinat noktalarına karşılık gelecek olan noktalardır. Eğer biz bu girdiğimiz koordinat noktaları ile bir üçgen çizmek istiyorsak bu noktalardan üçünü kullanmamız yeterli olacaktır veya koordinat noktalarına girdiğimiz değerler diziye girdiğimiz sırayla değil, bu durumda ise bu koordinat noktalarını istediğimiz şekilde değiştirebilmemiz mümkündür. {n2,n1,n4,n0,n3} Daha sonra biz bu noktaları uygulamamızda çağırırken 4. nokta diye belirteceğiz. Biz kod içerisinde 4. nokta dediğimizde o n0 ‘ı algılayacaktır. Aynı şekilde diğer noktaları da bu şekilde çağırabilmemiz mümkündür.

Biraz önce bahsettiğimiz koordinat noktalarının uygulamada çağırılma işlemi esnasında bizler bu noktaların içerisine girdiğimiz değerleri dışarıdan da alarak şekillerimizin X, Y, Z koordinatlarında şekillenmesine olanak tanıyabiliriz.

Ayrıca doğru çizimlerini yaparken unutmamamız gereken bir önemli noktada bu doğruları uygulamada belirtirken saat yönünün tersinde belirtmemiz gerektiğidir. Yani bunu kısaca örneklemek gerekirse, sağ elini kullananlar kâğıt üzerinde çizim yaparken ilk olarak en sağdaki noktayı çizer ve daha sonra sola doğru çizim yaparlar. İşte WPF ‘de çizim işlemlerini yaparken bu mantıkla çalışmaktadır. Yani bizler WPF ile çizim yaparken ilk olarak en sağ noktasını belirtip daha sonra en sol noktaya kadar olan noktaları belirterek işlemlerimizi yapabilmemiz mümkündür.

Peki, neden sağ el ile çizim örneğini verdik. Hemen isterseniz elinize bir kâğıt ve kalem alıp deneyin (Örneğimiz sağ elini kullananlar içindir). Bir sağdan sola, birde sağdan sola doğru çizim yapmayı deneyin. Dikkat edeceksiniz ki sağdan sola doğru yaptığınız çizim diğer koordinatlarda yaptığınız çizim işlemlerine göre çok daha düzgün olacaktır. Dünyada ki insanların büyük bir çoğunluğu da sağ ellerini kullandıkları için bilinçaltında kabul edilmiş çizim mantığının dışına çıkılmaması için böyle bir düşünce içerisine girilmiş.

Üçgen Standartları

Hepimizin ilkokuldan bildiği bir bilgi üçgenler üç noktadan oluşur. Bu üç noktaları bilgisayar ortamında çizmeye çalıştığımızda üç noktaya karşılık 6 koordinat noktaları verir ve çizimlerimizi yapardık. Daha önceden bilgisayar grafikleri ile uğraşamamış olan arkadaşlar için neden altı koordinat noktası girilmesi gerektiğinden kısaca bahsedeyim. Bilindiği üzere uygulamamızın formunun üzerinden işaretlenen bir noktanın ayrıntılarını incelediğimizde X ve Y noktalarından oluştuğunu ve bu bilgilerin tutulduğu gözlenir. Bizlerde üçgen oluşturmak istediğimizde A noktası için (X1 ve Y1), B noktası için (X2 ve Y2) ve son olarak ta C noktası içinde (X3 ve Y3) koordinatlarının bilgilerini tutar. Son olarak ta bu üç noktayı üçgen oluşturma kurallarına aykırı olmadığı takdirde birleştirdiğimiz zaman bir üçgen oluşacaktır.

Çizim işlemlerimizi yaparken birden fazla yöntem izleyebiliriz. Bunlar ABxAC, BCxBA veya CBxCA noktalarını ele alarak çizim işlemlerimizi yapabiliriz.




Üç basit doğru ile bir üçgen çizebildik. Bu noktaları arttırıp, açı değerlerini aralarında 90 derece olacak şekilde ayarlayıp koordinat noktalarını belirmemiz durumunda ise küp çizebilmemiz mümkündür.

Buraya kadar çizimin nasıl yapılacağına değinmeye çalıştık. Şimdi bu edindiğimiz bilgilerin daha kalıcı olabilmesi için örnek uygulamalar yapacağız.

Uygulama Geliştirmeye Başlarken

Yapacağımız örneğin daha anlaşılır olabilmesi için ilk başta bir kullanıcı arayüzü hazırlayacağız. Hem de uygulamamızı kullanacak olan son kullanıcılar için daha hoş bir görünüm sunulacaktır. Hazırladığımız kullanıcı arayüzünde 3D nesnelerin gözükebilmesi için viewport3D kontrolü kullanılmıştır.

<Grid>
<DockPanel
Width="Auto"
VerticalAlignment="Stretch"
Height="Auto"
HorizontalAlignment="Stretch"
Grid.ColumnSpan="1"
Grid.Column="0"
Grid.Row="0"
Margin="0,0,0,0"
Grid.RowSpan="1">
<StackPanel>
<StackPanel.Background>
<LinearGradientBrush>
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="DarkGoldenrod" Offset=".3"/>
<GradientStop Color="DarkGoldenrod" Offset=".7"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</StackPanel.Background>
<StackPanel Margin="10">
<Button
Name="buton"
Click="butonClick">Ucgen</Button>
</StackPanel>
</StackPanel>
<Viewport3D Name="anaGoruntuleme" ClipToBounds="True">
<Viewport3D.Camera>
<PerspectiveCamera
FarPlaneDistance="100"
LookDirection="-11,-10,-9"
UpDirection="0,1,0"
NearPlaneDistance="1"
Position="11,10,9"
FieldOfView="70" />
</Viewport3D.Camera>
<ModelVisual3D>
<ModelVisual3D.Content>
<DirectionalLight
Color="White"
Direction="-2,-3,-1" />
</ModelVisual3D.Content>
</ModelVisual3D>
</Viewport3D>
</DockPanel>
</Grid>

3D alanımızı basit bir biçimde Viewport3D kontrollerini kullanarak oluşturuyoruz. Viewport3D kontrollerini kullanmamızın sebebi üç boyut işlemlerini çok daha performanslı gerçekleştirebilmesinden ötürüdür. Viewport3D kontrolünün özelliklerini MSDN ‘de incelerseniz size not olarak “eğer 3D işlemlerini kullanacaksanız Viewport3D kontrolünün içerisinden kullanmanız uygulamanızın performansı açısından daha yararlı olacaktır” ile karşılaşacağız. Normalde form kontrolleri 2D işlemlerini çok performanslı gerçekleştirirken 3D işlemlerinde zaman kaybına uğradığı bilinmektedir. Bu zaman kaybının önlemek içinde Viewport3D kontrolünün kullanılması daha mantıklıdır.

Ayrıca daha sonradan kullanıcılara görüntümüzü farklı açılar üzerinden göstermeyi amaçladığımız için PerspectiveCamera özelliğini ekliyoruz. PerspectiveCamera Viewport3D kontrollünün alt özelliklerinden biridir. Varsayılan olarak kamera açısı {0, 0, 0} olarak bizlere sunulmaktadır.

DirectionalLight, yine Viewport3D kontrollerinden birisidir. Bu özellik uygulamamızdaki nesnemize belirli bir açı ile ışık geliyormuş hissi verir. Sanki gerçek hayatta nesnelerin üzerine güneş ışığının çarpması sonrasında belirli bir parlaklık alması gibi algılayabiliriz.

Hazırlamış olduğumuz kullanıcı kontrolü aşağıdaki gibi oluşmuştur.



Daha önceki WPF makalelerimizi incelediyseniz uygulamalarımızı genellikle XAML kod tarafında hazırlardık. Bu sefer ise C# kod tarafında da kod yazacağız. Bu sayede uygulamamızda yönetilebilir kodda kullanılmış olacak.

Kod tarafına geçtiğimizde isteklerimize karşılık verilebilmesi için butonun tıklanması olayına dayanarak işlemlerimizi yapacağız. Kullanıcı arayüzümüzü tasarlarken bir de ekrana buton eklemiştik. Bu eklediğimizi butonun name özelliğine istediğimiz ismi verebilmemiz mümkündür. Ben bu yazımız için buton veriyorum. Butona ismi verdikten sonra tıklanması sonrasında olayların gerçekleştirilebilmesi için kod tarafında butonu belirtmemiz gerekmektedir. Bu işlem esnasında ister XAML ile oluşturduğumuz butonun üstüne çift tıklar otomatik oluşturturuz ya da kendimiz nesne özellikleri ve yönlendirmesini belirterek biz oluştururuz.

İşlemi gerçekleştirdiğimizde karşımıza aşağıdaki bir kod oluşmalıdır.

private void butonClick(object sender, RoutedEventArgs e)
{
}

Basit Bir Çizim Modeli Oluşturulması

Viewport3D kontrollünün içerisinde kullanabileceğimiz tipleri ve 3D sınıfları kısaca tanımlayalım.

GeometryModel3D: .net3.0 ile gelmiştir. Mesh (MeshGeometry3D) ile 3 boyutlu çizim nesneler oluşturabilmemize olanak tanır.
MeshGeometry3D: .net3.0 ile gelmiştir. Bünyesinde nesnemizin pozisyonlarını, koordinatlarını ve normal çizgileri tutar.
Point3D: .net3.0 ile gelmiştir. Çizeceğimiz nesnelerin koordinat noktalarını tutar.
Vector3D: .net3.0 ile gelmiştir. Normal çizgileri oluşturur.
DiffuseMetarial: .net3.0 ile gelmiştir. Oluşturduğumuz modele renginin ve üzerinde tasarım var ise o özelliklerini vermemize olanak tanır.
DirectionalLight: .net3.0 ile gelmiştir. Nesnemizi aydınlatabilmemize olanak tanır.
Ayrıca bu özellikleri kullanabilmemize olanak tanıyan isim alanı System.Windows.Media.Media3D ‘dır. Bu sınıfa ait özellikleri yeri geldikçe inceleyeceğiz.

Uygulamamıza Kod Ekliyoruz

İlk olarak biraz önce bahsettiğimiz tipleri, özellikleri ve daha fazlasını kullanabilmemiz için Media3D sınıfını uygulamamıza ekliyoruz.

using System.Windows.Media.Media3D;

Şimdi ise üçgen çizebilmemiz için gerekli olan kodları eklemeyelim.

İlk olarak yeni bir MeshGeometry3D oluşturalım;
MeshGeometry3D ucgenYuzey = new MeshGeometry3D();

Sıradaki adımımız üçgenimizi oluşturabilmek için üç nokta eklememiz gerekmektedir.

Point3D nokta0 = new Point3D(0, 0, 0);
Point3D nokta1 = new Point3D(5, 0, 0);
Point3D nokta2 = new Point3D(0, 0, 5);

Şimdi eklediğimiz bu üç noktayı üçgen oluşturabilmesi için ucgenYuzey ’e ekliyoruz.

ucgenYuzey.Positions.Add(nokta0);
ucgenYuzey.Positions.Add(nokta1);
ucgenYuzey.Positions.Add(nokta2);

Şimdi üçgenimizin çizilmesi için Triangle Indices ‘e noktaları tanımlıyoruz. Bu işlemleri yaparken de çizimleri sağ elimizle yaptığımızı anlatan örneği aklımıza getirmek yararlı olacaktır.

ucgenYuzey.TriangleIndices.Add(0);
ucgenYuzey.TriangleIndices.Add(2);
ucgenYuzey.TriangleIndices.Add(1);

Doğrularımızın çizilebilmesi için noktalarımızı Vector3D ‘ye aktarıyoruz ve doğru olarak çizmesini belirtiyoruz.

Vector3D normal = new Vector3D(0,1,0);
ucgenYuzey.Normals.Add(normal);
ucgenYuzey.Normals.Add(normal);
ucgenYuzey.Normals.Add(normal);

Son olarak üçgenimizin yüzeyini ve oluşturulacak olan modele ait bilgileri nereden alacağını belirtmemiz gerekmektedir.

Material madde = new DiffuseMaterial(new SolidColorBrush(Colors.DarkGoldenrod));
GeometryModel3D ucgenModel = new GeometryModel3D(ucgenYuzey, madde);
ModelVisual3D model = new ModelVisual3D();
model.Content = ucgenModel;
this.anaGoruntuleme.Children.Add(model);

Uygulamamızda üçgen çizilmesi için gerekli olan kodları bitirmiş oluyoruz. Bu işlemlerimizin sonucunda buton ‘unun tıklanması olayında oluşan kodlar toplu halde aşağıdaki gibi olmuştur.

private void butonClick(object sender, RoutedEventArgs e)
{
MeshGeometry3D ucgenYuzey = new MeshGeometry3D();
Point3D nokta0 = new Point3D(0, 0, 0);
Point3D nokta1 = new Point3D(5, 0, 0);
Point3D nokta2 = new Point3D(0, 0, 5);
ucgenYuzey.Positions.Add(nokta0);
ucgenYuzey.Positions.Add(nokta1);
ucgenYuzey.Positions.Add(nokta2);
ucgenYuzey.TriangleIndices.Add(0);
ucgenYuzey.TriangleIndices.Add(2);
ucgenYuzey.TriangleIndices.Add(1);
Vector3D normal = new Vector3D(0,1,0);
ucgenYuzey.Normals.Add(normal);
ucgenYuzey.Normals.Add(normal);
ucgenYuzey.Normals.Add(normal);
Material madde = new DiffuseMaterial(new SolidColorBrush(Colors.DarkGoldenrod));
GeometryModel3D ucgenModel = new GeometryModel3D(ucgenYuzey, madde);
ModelVisual3D model = new ModelVisual3D();
model.Content = ucgenModel;
this.anaGoruntuleme.Children.Add(model);
}

Bu işlemlerimiz sonucunda oluşan ekran görüntüsü aşağıdaki gibi oluşmuştur.


Evet, bu şekilde üçgeni çizebilmiş oluyoruz. Şimdi ise 3 boyutlu Mesh ‘i nasıl çizeceğimize değinmeye çalışacağız.

Küp Oluşturmak

Küpü nasıl oluşturacağımızı hatırlamaya çalışalım. Küpün 12 açısı bulunmaktadır (6 yüzeyi ve iki tane kesim noktası bulunmaktadır).
Kilit noktalardan yola çıkarak saatin tersi yönünde (sağ elle çizim alışkanlığına göre) çizim yapacağımız noktaları belirleriz.

Bu iki kısa bilgiyi aklımızda tutarak uygulamamızı yapmaya devam edebiliriz.

İlk olarak yapmamız gereken küpün oluşturulması için formumuza bir buton eklenmesi gerekmektedir. Bunun için XAML kod tarafına aşağıdaki kodu ekleriz.

<Button Name="kupButon" Click="kupButonClick">Kup</Button>

Şimdi kod tarafına geçmek için butonun tıklanması olayını ekliyoruz.

private void kupButonClick(object sender, RoutedEventArgs e)
{
}

Küpümüzü çizerken üç boyutlu modelleme işlemlerimizi daha kolay yapabilmemiz için Model3DGroup sınıfını kullanacağız. Model3DGroup koleksiyonuna dahil olan GeometryModel3D nesnesi işimizi oldukça kolaylaştıracaktır. Bu yapacağımız örnek basit olarak 3D modellemeyi bize kavratacaktır.

3D Modelleme işlemlerimizi yaparken normal çizim işlemlerine ek olarak bizde iki metot ekleyeceğiz. Ekleyeceğimiz bu metotlar UcgenModeliOlustur() ve NormalHesapla() olacaktır. Şimdi bu iki metodumuzun kodlarına göz atalım.

private Model3DGroup UcgenModeliOlustur(Point3D n0, Point3D n1, Point3D n2)
{
MeshGeometry3D mesh = new MeshGeometry3D();
mesh.Positions.Add(n0);
mesh.Positions.Add(n1);
mesh.Positions.Add(n2);
mesh.TriangleIndices.Add(0);
mesh.TriangleIndices.Add(1);
mesh.TriangleIndices.Add(2);
Vector3D normal = NormalHesapla(n0, n1, n2);
mesh.Normals.Add(normal);
mesh.Normals.Add(normal);
mesh.Normals.Add(normal);
Material madde = new DiffuseMaterial(
new SolidColorBrush(Colors.DarkGoldenrod));
GeometryModel3D model = new GeometryModel3D(
mesh, madde);
Model3DGroup grup = new Model3DGroup();
grop.Children.Add(model);
return grup;
}
private Vector3D NormalHesapla(Point3D n0, Point3D n1, Point3D n2)
{
Vector3D v0 = new Vector3D(
n1.X - n0.X, n1.Y - n0.Y, n1.Z - n0.Z);
Vector3D v1 = new Vector3D(
n2.X - n1.X, n2.Y - n1.Y, n2.Z - n1.Z);
return Vector3D.CrossProduct(v0, v1);
}

UcgenModeliOlustur() metodu Model3DGroup mesh ‘i kullanarak oldukça şık ve sevimli bir çizim yapmamıza olanak tanır. NormalHesapla() metodu ise UcgenModeliOlustur() metodu ile aldığımız noktalara ilişkin koordinat bilgilerinden yararlanarak bu noktaları vektöre dönüştürür. Bu iki metodun yaptığı işlemler sonrasında ise oldukça şık bir küp oluşturulmuş olur.

Tabii küpü çizebilmemiz için butonun tıklanması olayında koordinat bilgileri girmemiz gerekmektedir. Bu girdiğimiz bilgilerde UcgenModeliOlustur() metoduna oradan da NormalHesapla() metodu ile çizilmesini sağlayacaktır.


Şimdi küpümüzün butona tıklanması küpü çizebilmesi için koordinatlarına ilişkin bilgilerini kod tarafında girelim.

private void kupButonClick(object sender, RoutedEventArgs e)
{
Model3DGroup kup = new Model3DGroup();
Point3D n0 = new Point3D(0, 0, 0);
Point3D n1 = new Point3D(5, 0, 0);
Point3D n2 = new Point3D(5, 0, 5);
Point3D n3 = new Point3D(0, 0, 5);
Point3D n4 = new Point3D(0, 5, 0);
Point3D n5 = new Point3D(5, 5, 0);
Point3D n6 = new Point3D(5, 5, 5);
Point3D n7 = new Point3D(0, 5, 5);
//ön yüz
kup.Children.Add(UcgenModeliOlustur(n3, n2, n6));
kup.Children.Add(UcgenModeliOlustur(n3, n6, n7));
//sağ yüz
kup.Children.Add(UcgenModeliOlustur(n2, n1, n5));
kup.Children.Add(UcgenModeliOlustur(n2, n5, n6));
//arka yüz
kup.Children.Add(UcgenModeliOlustur(n1, n0, n4));
kup.Children.Add(UcgenModeliOlustur(n1, n4, n5));
//sol yüz
kup.Children.Add(UcgenModeliOlustur(n0, n3, n7));
kup.Children.Add(UcgenModeliOlustur(n0, n7, n4));
//üst yüz
kup.Children.Add(UcgenModeliOlustur(n7, n6, n5));
kup.Children.Add(UcgenModeliOlustur(n7, n5, n4));
//alt yüz
kup.Children.Add(UcgenModeliOlustur(n2, n3, n0));
kup.Children.Add(UcgenModeliOlustur(n2, n0, n1));
ModelVisual3D model = new ModelVisual3D();
model.Content = kup;
this.anaGoruntuleme.Children.Add(model);
}

Bu kodlarımızı uygulamamıza ekledikten sonra derlediğimizde karşımıza aşağıdaki gibi güzel bir görüntü çıkacaktır.



Viewport ‘u Temizleme

Uygulamamızda oluşturduğumuz modelleri birbirleri üzerine tekrardan oluşturmak istediğimizde dikkatimizi çeken bir sorun çıkmıştır. Üçgeni çizdirdikten sonra küpü çizdirdiğimizde bir öncekinin üstünü kapatarak küpü çizerken, küpten üçgene geçmek istediğimiz zaman çizememektedir. Bu durumun ana nedeni küpün kapladığı alanın üçgenden daha büyük olması ve çizimleri yaptırırken bir önceki sildirmeden yapmamızdan kaynaklanmaktadır. Bu kötü durumu ortadan kaldırmak için görüntü alanı olarak kullandığımız Viewport ‘u temizlememiz gerekecektir. Bu temizleme işlemi için aşağıdaki kodları metot olarak oluşturuyoruz.

private void ViewportTemizle()
{
ModelVisual3D m;
for (int i = anaGoruntuleme.Children.Count - 1; i >= 0; i--)
{
m = (ModelVisual3D)anaGoruntuleme.Children[i];
if (m.Content is DirectionalLight == false)
anaGoruntuleme.Children.Remove(m);
}
}

ViewportTemizle() metodunu başarılı bir biçimde kullanabilmemiz için küp ve üçgen çizdirmek için kullandığımız butonların tıklanması olaylarının ilk satırlarına ViewportTemizle(); metodunu çağırarak temizleme işlemini gerçekleştirebiliriz.

Kamera Kontrolleri

Biz küpü ve üçgeni basit bir şekilde oluşturduk. Fakat son kullanıcılar bizlerden bu görüntüleri farklı açılarında da görmek istiyoruz şeklinde isteklerde bulunabilirler. Bizde bu durumu yerine getirebilmek için yapmamız gereken işlemler koordinatlara ait bilgileri kullanıcıdan girmesini istemek olacaktır. Bu işlemi yerine getirebilmek için kullanıcı arayüzüne girdi girilebilmesi için TextBlock ve TextBox eklememiz gerekecektir.

<StackPanel Margin="10">
<Button Name="buton" Click="butonClick">Ucgen</Button>
<Button Name="kupButon" Click="kupButonClick">Kup</Button>
<Button Name="Kapat" Click="kapatButon">Kapat</Button>
<TextBlock Text="Kamera X pozisyonu:"/>
<TextBox Name="kameraPozisyonXTextBox" MaxLength="5"
HorizontalAlignment="Left" Text="9" Width="100" />
<TextBlock Text="Kamera Y Pozisyonu:"/>
<TextBox Name="kameraPozisyonYTextBox" MaxLength="5"
HorizontalAlignment="Left" Text="8" Width="100" />
<TextBlock Text="Kamera Z Pozisyon:"/>
<TextBox Name="kameraPozisyonZTextBox" MaxLength="5"
HorizontalAlignment="Left" Text="10" Width="100" />
<Separator/>
<TextBlock Text="Bakılan X Yönü:"/>
<TextBox Name="bakilanXTextBox" MaxLength="5"
HorizontalAlignment="Left" Text="-9" Width="100" />
<TextBlock Text="Bakılan Y Yönü:"/>
<TextBox Name="bakilanYTextBox" MaxLength="5"
HorizontalAlignment="Left" Text="-8" Width="100" />
<TextBlock Text="Bakılan Z Yönü:"/>
<TextBox Name="bakilanZTextBox" MaxLength="5"
HorizontalAlignment="Left" Text="-10" Width="100" />
<Separator/>
</StackPanel>

XAML kod tarafımız yukarıdaki gibi olmuştur. Şimdi yapmamız gereken işlem ise TextBox lardan alınan bilgiler ile 3D modelimizin kamera açılarının değişmesidir. Bunu yapabilmek için KameraAiınan() isimli bir metot oluşturuyoruz.

private void KameraAlinan()
{
PerspectiveCamera kamera = (PerspectiveCamera)anaGoruntuleme.Camera;
Point3D pozisyon = new Point3D(
Convert.ToDouble(kameraPozisyonXTextBox.Text),
Convert.ToDouble(kameraPozisyonYTextBox.Text),
Convert.ToDouble(kameraPozisyonZTextBox.Text)
);
Vector3D lookDirection = new Vector3D(
Convert.ToDouble(bakilanXTextBox.Text),
Convert.ToDouble(bakilanYTextBox.Text),
Convert.ToDouble(bakilanZTextBox.Text)
);
kamera.Position = pozisyon;
kamera.LookDirection = lookDirection;
}

Uygulamalarımızda da kamera kontrollerinin işleyebilmesi için KameraAlinan() metodunu butonların tıklanması olayına eklememiz yeterli olacaktır. Metodu çağırma işlemini tamamladıktan sonra uygulamamızı çalıştırdığımızda aşağıdaki gibi bir görüntü elde ederiz.

ScreenSpaceLines3D Sınıfı

Bahsettiğimiz bu sınıf .net3.0 yapısının içerisinde bulunmamaktadır. Sınıfı kullanabilmemiz için yapmamız gereken CodePlex 3D Tools ‘te geliştiriciler tarafından geliştirilmekte olan 3DTools uygulaması indirerek uygulamamıza referans olarak eklememiz yeterli olacaktır.



Bu dll ‘i referans olarak uygulamamıza ekleriz.

using _3DTools;

Artık ScreenSpaceLines3D sınıfını kullanabiliriz.

Modelde Köşe Noktalarına ait Doğrularının Eklenmesi

Üç boyutlu modellerimizin daha şık gözükebilmesi için köşe noktalarından devam eden çizgiler göstermek oldukça iyi olacaktır. Bu işlemleri gerçekleştirebilmemiz için ScreenSpaceLines3D sınıfını kullanacağımızdan bahsetmiştik. XAML ve C# kod tarafında bu işlemleri nasıl yapabileceğimize göz atalım.

<Separator/>
<CheckBox Name="normalCheckBox">Doğruları Göster</CheckBox>
<TextBlock Text="Doğru Boyutu:"/>
<TextBox Name="normalBoyutTextBox" Text="1"/>

XAML tarafından kontrolleri ekledikten sonra bu kontroller eşliğinden işlem yapacak olan C# kodlarını da uygulamamıza ekleyelim.

private Model3DGroup NormalYapilandirma(Point3D n0, Point3D n1, Point3D n2, Vector3D normal)
{
Model3DGroup DogruGrup = new Model3DGroup();
Point3D n;
ScreenSpaceLines3D normal0Tel = new ScreenSpaceLines3D();
ScreenSpaceLines3D normal1Tel = new ScreenSpaceLines3D();
ScreenSpaceLines3D normal2Tel = new ScreenSpaceLines3D();
Color c = Colors.Blue;
int genislik = 1;
normal0Tel.Thickness = genislik;
normal0Tel.Color = c;
normal1Tel.Thickness = genislik;
normal1Tel.Color = c;
normal2Tel.Thickness = genislik;
normal2Tel.Color = c;
double num = 1;
double mult = .01;
double denom = mult * Convert.ToDouble(normalBoyutTextBox.Text);
double faktor = num / denom;
n = Vector3D.Add(Vector3D.Divide(normal, faktor), n0);
normal0Tel.Points.Add(n0);
normal0Tel.Points.Add(n);
n = Vector3D.Add(Vector3D.Divide(normal, faktor), n1);
normal1Tel.Points.Add(n1);
normal1Tel.Points.Add(n);
n = Vector3D.Add(Vector3D.Divide(normal, faktor), n2);
normal2Tel.Points.Add(n2);
normal2Tel.Points.Add(n);
this.anaGoruntuleme.Children.Add(normal0Tel);
this.anaGoruntuleme.Children.Add(normal1Tel);
this.anaGoruntuleme.Children.Add(normal2Tel);
return DogruGrup;
}

Kod tarafına eklememiz gereken tek bir kod parçası daha kaldı. O da, CheckBox ‘ta seçilmiş ise doğruların gözükmesidir. Bu işlemi için basit bir if koşulu eklememiz yeterli olacaktır.

if (normalCheckBox.IsChecked == true)
grup.Children.Add(NormalYapilandirma(n0, n1, n2, normal));

UcgenModeliOlustur() metodunun en alt kısmına bu if koşulunu eklediğimiz takdirde aşağıdaki gibi bir uygulamaya sahip olmuş oluruz.



Yeryüzü Oluşturma

3D teorisini anlatırken oluşturulan modellerin maksimum ve minimum noktalarının olduğunu ve bu noktaların rastgele verilmesi durumunda tümsekli eğrilere sahip bir bölgesel yüzey elde edebileceğimizden bahsetmiştik. Şimdi bu yüzeyi nasıl oluşturabileceğimize değineceğiz.

Kullanıcı arayüzümüze yüzey yapısının oluşturmalarını oluşturabilmesi için bir buton ekliyoruz.

<Button Name="yuzeyYapisiButon" Click="yuzeyYapisiButonClick">Yüzey Yapısı</Button>

Modellemeyi yaparken X ve Z koordinatları ile yüzeyi Y koordinatları ile ise yükseklikleri belirtiyoruz. Bu yapacağımız 10x10 ‘luk bir yüzey olacaktır. Yüzeyimizin çok daha gerçekçi olabilmesi için koordinat noktalarına ait değerleri rastgele oluştururuz. Bu işlemi yapmak için aşağıdaki koddan yararlanabilmek mümkündür.

private Point3D[] RastgeleYuzeyNoktalari()
{
//10x10 'luk yüzey oluşturuluyor..
Point3D[] noktalar = new Point3D[100];
Random r = new Random();
double y;
double denom = 1000;
int sayac = 0;
for (int z = 0; z < 10; z++)
{
for (int x = 0; x < 10; x++)
{
System.Threading.Thread.Sleep(1);
y = Convert.ToDouble(r.Next(1, 999)) / denom;
noktalar[sayac] = new Point3D(x, y, z);
sayac += 1;
}
}
return noktalar;
}

Rastgele nokta ürettikten sonra yuzeyYapisi butonunun tıklanması olayına aşağıdaki kodları ekliyoruz.

private void yuzeyYapisiButtonClick(object sender, RoutedEventArgs e)
{
ViewportTemizle();
KameraAlinan();
Model3DGroup yuzeyYapisi = new Model3DGroup();
Point3D[] noktalar = RastgeleYuzeyNoktalari();
for (int z = 0; z <= 80; z = z + 10)
{
for (int x = 0; x < 9; x++)
{
yuzeyYapisi.Children.Add(
UcgenModeliOlustur(
noktalar[x + z],
noktalar[x + z + 10],
noktalar[x + z + 1])
);
yuzeyYapisi.Children.Add(
UcgenModeliOlustur(
noktalar[x + z + 1],
noktalar[x + z + 10],
noktalar[x + z + 11])
);
}
}
ModelVisual3D model = new ModelVisual3D();
model.Content = yuzeyYapisi;
this.anaGoruntuleme.Children.Add(model);
}

Basit olarak çizimlerimizi oluşturmuş oluyoruz. Uygulamamızda Yüzey oluştur butonuna tıkladığımız hazırladığımız kod parçaları bize zig-zag olarak çizilmiş doğrular verecektedir. Bu doğruları da 3D nokta olarak tuttuğumuzdan ötürü bizlere tümsekli bir yeryüzüne benzeyen görüntü sunacaktır.



Uzantı doğrularını göstererek;


Tel görüntü Ekleme

Yüzey ile ilgili uygulamaları incelediğimizde bizlere uzantı noktaları ile birlikte tümsekler üzerinde daha belirleyici olması için tellere benzer çizimlerin yapıldığını görürüz. Şimdi bizde yüzey uygulamamıza bu telleri ekleyeceğiz.

Bu işlemi daha önceden doğruları gösterme işinde yaptığımız gibi CheckBox ile yaparsak sanırım çok daha kullanışlı olacaktır. Kullanıcı arayüzümüze XAML tarafında bu kontrolü ekliyoruz.

<Separator/>
<CheckBox Name="telGoruntuCheckBox">Telleri Goster</CheckBox>

Kontrolleri ekledikten sonra seçildiği zaman işlem görebilmesi için UcgenModeliOlustur() metoduna aşağıdaki koşulu ekliyoruz.

if (telGoruntuCheckBox.IsChecked == true)
{
ScreenSpaceLines3D telGoruntu = new ScreenSpaceLines3D();
telGoruntu.Points.Add(n0);
telGoruntu.Points.Add(n1);
telGoruntu.Points.Add(n2);
telGoruntu.Points.Add(n0);
telGoruntu.Color = Colors.LightBlue;
telGoruntu.Thickness = 3;
this.anaGoruntuleme.Children.Add(telGoruntu);
}

Metot ile bağlantılar bütün butonlarda sağlanmış olduğu için CheckBox ‘ı seçili duruma getirdiğimizde teller gözükecektir. Fakat ekran görüntüsünü göstermeden önce sizlere son olarak UcgenModeliOlustur() metodunun en son halini verelim. Çünkü içerisinde çok fazla oynama yaptık. Karıştırmış olabilme ihtimalinde, nerede hata yaptığınızı da bulabilmemiz mümkün olacaktır.

private Model3DGroup UcgenModeliOlustur(Point3D n0, Point3D n1, Point3D n2)
{
MeshGeometry3D mesh = new MeshGeometry3D();
mesh.Positions.Add(n0);
mesh.Positions.Add(n1);
mesh.Positions.Add(n2);
mesh.TriangleIndices.Add(0);
mesh.TriangleIndices.Add(1);
mesh.TriangleIndices.Add(2);
Vector3D normal = NormalHesapla(n0, n1, n2);
mesh.Normals.Add(normal);
mesh.Normals.Add(normal);
mesh.Normals.Add(normal);
Material madde = new DiffuseMaterial(
new SolidColorBrush(Colors.DarkGoldenrod));
GeometryModel3D model = new GeometryModel3D(
mesh, madde);
Model3DGroup grup = new Model3DGroup();
grup.Children.Add(model);

if (normalCheckBox.IsChecked == true)
grup.Children.Add(NormalYapilandirma(n0, n1, n2, normal));

if (telGoruntuCheckBox.IsChecked == true)
{
ScreenSpaceLines3D telGoruntu = new ScreenSpaceLines3D();
telGoruntu.Points.Add(n0);
telGoruntu.Points.Add(n1);
telGoruntu.Points.Add(n2);
telGoruntu.Points.Add(n0);
telGoruntu.Color = Colors.LightBlue;
telGoruntu.Thickness = 3;
this.anaGoruntuleme.Children.Add(telGoruntu);
}

return grup;
}

Son olarak telli olarak yüzeyimiz aşağıdaki gibidir.



Burada yazımızın sonuna gelmiş bulunuyoruz. 3D teorisinden başlayarak tümsekli yüzey(topoloji) oluşturarak sonlandırmış oluyoruz. 3D modellemeyi incelerken daha anlaşılır olması açısından en basit anlamda anlatmaya çalıştık. Bu anlattıklarımızı rahatlıkla anlayabildiyseniz artık çok daha karmaşık 3D modellemeleri rahatlıkla yapabilmeniz mümkün olacaktır.

Umarım yararlı olabilmiştir.

Uygulamanın kaynak dosyasına linkten ulaşabilirsiniz.

Kaynaklar
CodePlex 3DTools App.
Microsoft Developer Network
Turhal Temizer, Karadeniz Teknik Üniversitesi, Bilgisayar ve İstatistik Bilimleri Bölümü, Bitirme Çalışması

Turhal Temizer
turhal.temizer@csharpnedir.com
http://turhal.blogspot.com/