Makale Özeti

Bu makalemde sizlere XNA uygulamanızda WPF UserControl’leri ve Windows Kontrollerini nasıl kullanabileceğinizi göstereceğim.

Makale

Bu makalemde sizlere XNA uygulamanızda WPF UserControl’leri ve Windows Kontrollerini nasıl kullanabileceğinizi göstereceğim.

Belki daha önce çoğu yerde duymuşsunuzdur; XNA içerisinde WPF i bırakın Windows Kontrollerini dahi kullanamazsınız.

Bu makale bu yanlış görüşü ortadan kaldırmaya yönelik olacaktır.

Ancak şunun altını çizmemde fayda vardır ki bu makalede öğreneceğiniz yöntem sadece Windows uygulamaları/oyunları geliştiriyorsanız geçerli olacaktır.

Bileşenlerimiz:

Kullanacağımız Windows Kontrolleri

  • TextBox
  • ElementHost

İhtiyacımız olan Araçlar

XNA GS 3.1 ve Visual Studio 2008(SP1)
.NET Framework 3.5(SP1)

Projemizi oluşturalım:

image1.gif

Projemizi oluşturduktan sonra Add Reference diyerek “System.Windows.Forms” u projemize dahil edelim

image2.gif

ve Game1.cs dosyamızda bunu tanımlayalım:

image3.gif

Not:
 Ekstradan "System.Drawing" ad uzayını da dahil etmemiz gerekiyor. 


TextBox Kontrolü:

Windows Formlarında klavyeden giriş yapabileceğimiz kontroldür.
 
Aşağıda görüldüğü üzere bir tanımlama yaparak projemize dahil edebiliyoruz:

TextBox text1 = new TextBox();

Yeni bir TextBox nesnesi oluşturduk.

Initialize()  fonksiyonumuzu aşağıda gördüğünüz üzere güncelleyiniz. 

   protected override void Initialize()
        {           
 
           
 base.Initialize();
           
 text1.Location = new System.Drawing.Point(40, 40);
            text1.BorderStyle = BorderStyle.None;
            text1.Multiline = true;
            text1.Size = new Size(400, 400);
            Control.FromHandle(Window.Handle).Controls.Add(text1);
        }

Biraz açıklama yapalım bu noktada :
"Control.FromHandle(Window.Handle).Controls.Add(ornek);"
 

"Control.FromHandle" – İlgili handle numarasını belirleyebileceğimiz bir fonksiyondur.

"Control.FromHandle(Window.Handle)" Window adını almış GameWindow nesnesinin handle numarasını belirtiyoruz.”GameWindow” Game sınıfında kullanabileceğimiz bir kontrol olduğuna göre GameWindow a ait Handle numarasını burada çağırabilir ve istediğimiz herhangi bir kontrolü bu Handle numarası aracılığıyla ekleyebiliriz.Aynı işlemi Windows Forms uygulamaları geliştirirken de yapmaktaydık.Kontrol ekleyip çıkartabiliyorduk.Bu istinaden şunu belirtmem de yarar vardır ki; "Form ile GameWindow sınıfları birbirlerine bu açıdan benzemektedirler".Bu şekilde düşünecek olursak Windows Forms uygulamaları geliştirirken yaptığımız RAD(Rapid Application Development) ı aynı şekilde XNA uygulamaları geliştirirken de yapabiliyor olacağız.

Uygulamamıza geri dönecek olursak,çalıştırdığımızda ilgili TextBox a herhangi bir yazı giremediğimizi görüyor olacaksınız.

Bunun sebebi Textbox sınıfı "System.Windows.Forms.Keys" parametrelerini kabul ederken XNA’in sadece "XNA.Framework.Input.Keys" kabul etmesidir.Bu anlamda uygulamaya veri girişi aşamasında bir problem söz konusudur.Windows yöntemine göre veri girişi sağlayamayacağımıza göre bu işi XNA in kabul ettiği yönteme göre yapmamız gerekecektir.

string var_text1;

var_text1 adında bir string tanımlayarak XNA uygulamasında herhangi bir tuşa basıldığında tüm tuşları ilgili değişkenimizde depolayacağız.

Update fonksiyonunu aşağıda gördüğünüz üzere güncelleyiniz.
 
protected override void Update(GameTime gameTime)
 {
  
 base.Update(gameTime);
  
 if (text1.Focused)
    {
     
 KeyboardState ns = Keyboard.GetState();
     
 foreach (Microsoft.Xna.Framework.Input.Keys a in ns.GetPressedKeys())
        {
 
          var_text1 = a.ToString();
          text1.Text += var_text1;
        }
     }
 }

ns.GetPressedKeys() fonksiyonunu kullanarak o anki basılmış tüm tuşları var_text1 içerisinde depoluyoruz.Daha sonra bu değişkeni kullanarak TextBox nesnemize ilgili tuşları atıyoruz.

Ugulamayı çalıştıralım.Neler olacak?


image4.gif

a tuşuna 1 kere bastığımızda bize 5 kere bastığımız şeklinde bir çıktı verecektir.Bu durumu açıklayayım hemen:

XNA’de, Udate fonksiyonunda olan her olay Gametime değerine bağlıdır.1 saniyelik işlem 5 kere tekrarlanarak ilgili oyun penceremiz istediğimiz şekilde kendini yeniler.

Peki neden 1 sn/5?

Çünkü süregelen(örnek verecek olursak; araba takip etme,bir hedefi vurma gibi) işlemlerde  her an her saniye ne olup bittiğini görebilmeliyiz.Ve bunu kullanıcıya hissettirmeden göstermeliyiz ki kullanıcıda gerçek zamanlı bir oyun oynadığı hissine kapılabilsin.bu durumdan ötürü Update olayı her saniyede 5 kere tekrarlanır.Bazen 1 sn bile çok önemlidir.


Projemize geri dönelim.Bunu nasıl çözebiliriz?

bir int değişkeni tanımlayalım;

int
 ct = 0; //başlangıç değeri 0 olmalı ki tekrar edemeyesin.

Update fonksiyonunu aşağıda gördüğünüz üzere güncelleyiniz:

protected override void Update(GameTime gameTime)
 {
  
 base.Update(gameTime);
  
 if (text1.Focused)
    {
      ct = ct -1;
     
 if(ct < 0)
       {
         ct = 5;
       }

     if(ct == 0)
       {
        
 KeyboardState ns = Keyboard.GetState();
        
 foreach (Microsoft.Xna.Framework.Input.Keys a in ns.GetPressedKeys())
          {
 
           var_text1 = a.ToString();
           Text1.Text += var_text1;
          }
       }
     }
  }


ve artık tekrar etmeden Textbox nesnesini kullanabiliyorsunuz:

image5.gif

Diğer Windows kontrollerini de aynı şekilde ekleyebilirsiniz.Şimdi WPF kısmına geçelim.


WPF User Kontrolleri eklemek:

WPF User Control ler XNA içerisine dahil edebileceğimiz en heyecan verici nesnelerdir.

WPF ile çalışmaya başlamadan önce aşağıdaki nesnelere Referans ekleme yöntemiyle sisteme dahil ediniz:

  • Presentation Core
  • Presentation Framework
  • UIAutomationProvider
  • WindowsBase
  • WindowsFormsIntegration

Bu işlemden sonra daha önceden bir WPF User Control projesi yaptığınızı varsayarak Project->"Add Existing Item..." diyerek:

image11.gif

İlgili Windows Markup File & C# Source file dosyalarını ekleyiniz:

image12.gif

Şimdi ise daha önce kullandığımız yöntem gibi sisteme ilk önce ilgili WPF User Control ün namespace ini dahil ediyoruz.


using WindowsFormsApplication1; 

 
Bu WPF User Control ün içinde bir adet combobox ve "SelectionChanged"  adında bir olay ekledik.Bu olayda bize seçili elemana dair bir mesaj kutusu görüntülemektedir.Kodları aşağıdaki gibi...


WPF UserControl(Codebehind):

private void comboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
  {
   
 ComboBoxItem cbi = ((ComboBox)sender).SelectedItem as ComboBoxItem;
   
 MessageBox.Show(cbi.Content.ToString());
  }

WPF UserControl(XAML):

<UserControl x:Class="WindowsFormsApplication1.Kontrol1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="300" Width="300">
    <Grid>
        <ComboBox Height="23" Margin="48,70,132,0" Name="comboBox1" VerticalAlignment="Top" 
SelectionChanged="comboBox1_SelectionChanged">
            <ComboBoxItem>1</ComboBoxItem>
            <ComboBoxItem>2</ComboBoxItem>
            <ComboBoxItem>3</ComboBoxItem>
            <ComboBoxItem>4</ComboBoxItem>
        </ComboBox>
    </Grid>
</
UserControl>

Şimdi bu kontrolü XNA projemize dahil edelim!

Kontrol1 kontrol11 = new Kontrol1();

kontrol11 adında yeni bir Kontrol1 nesnesi oluşturduk.Bu nesnemiz WPF User Control nesnesinin bir kopyasıdır.

"Initialize()" fonksiyonumuzun içerisine kontrol11 adındaki nesneyi bir ElementHost nesnesi içerisine dahil edilecek ve görüntülenecek şekilde ekledik.Bildiğiniz üzere ElementHost nesnesi kullanarak WPF de hazırlanmış User Control leri Windows Forms içerisinde görüntüleyebiliyorduk.Aynı yöntemi XNA’de de yapabileceğinizi unutmayın.
 

protected override void Initialize()
 {           
 
  
 base.Initialize();
  
 ElementHost elementHost1 = new ElementHost();
   elementHost1.Location =
 new System.Drawing.Point(308, 69);
   elementHost1.Name =
 "elementHost1";
   elementHost1.Size =
 new System.Drawing.Size(350, 181);
   elementHost1.TabIndex = 1;
   elementHost1.Text =
 "elementHost1";
   elementHost1.Child =
 this.kontrol11;
  
 Control.FromHandle(Window.Handle).Controls.Add(elementHost1);
 }

Uygulamayı çalıştıralım.Bakalım neler olacak?


image13.gif

Evet çok doğru! Farklı Grafiksel Arabirimleri aynı solution içerisinde kullanmaya kalkarsanız aşağıdaki gibi bir sonuç ile karşılaşmanız kaçınılmaz olacaktır.Bu yüzden uygulamanızı
Single-Threaded Apartment (STA) a çevirmelisiniz.

Program.cs dosyanızı açarak:

using System;

namespace DemoTest
{
   
 static class Program
    { 
        [
STAThread]
       
 static void Main(string[] args)
        {
           
 using (Game1 game = new Game1())
            {
                game.Run();
            }
        }
    }
}

şeklinde güncellerseniz uygulamamız çalışacaktır.Uygulamamızı çalıştırdıktan sonra test ettiğimizde anlayacağımız şu ki eğer sadece Windows tabanlı çalışacak uygulamalar/oyunlar geliştirecekseniz WPF User Control leri projenize bu makale de anlatıldığı üzere ekleyebilir ve WPF’in sunduğu RAD(Rapid Application Development) olanaklarından faydalanabilirsiniz.
image14.gif

Projemiz ilk çalıştığındaki ekran

image15.gif

image16.gif

 

Bir item seçerek bize item ın değerini mesaj olarak gösteriyor.

Bu makalemizde Demonstration açısından basit bir örnek göstermek istedim.

XNA içerisine sadece Windows tabanlı çalışacak uygulamalar geliştirecekseniz makale de anlatıldığı gibi bir yöntem izleyebilirsiniz.

Bu makale de sizlere XNA’de Windows ve WPF User Control lerin nasıl eklenebileceğini ve XNA ye RAD kazandırabileceğimizden söz ettim.

Umarım sizlere faydalı olabilmişimdir.

İlerleyen yazılarımda görüşmek üzere

 

Uygulamanın Kaynak Kodları