Linq to XML ile Bir Niteliğin Varlığının Kontrol Edilmesi
2 Eylül,2011

Pek çok uygulamanızda xml verilerle çalışma fırsatınız olmuştur ve bu verilerden ihtiyacınız olanları çekmek için parse işlemleri de yapmışsınızdır. Her ne kadar xml verileri ayrıştırmak işkence gibi gelse de Linq to Xml ile işimiz biraz daha kolaylaşmış durumda. Fakat bazı xml dosyaları standart olmayabiliyor. Özellikle niteliklerin değerlerini alırken bazı elementler almak istediğimiz niteliği içermeyebiliyor ve  bu yüzden uygulama hata veriyor. Peki elementlerin almak istediğimiz niteliği içerip içermediğini nasıl kontrol ederiz?

Nasıl yaparız?

<Book Edition="3"> 
<Name>..</Name> 
<Author>..</Author>
 <ISBN>..</ISBN> 
</Book>

Yukarıdaki gibi bir elementimizin olduğunu varsayıp Edition niteliğinin değerini okuyalım. Eğer elementimizin Edition isimli bir niteliği yoksa varsayılan olarak 1 değerini verelim.

string edition = p.Attributes("Edition").Any() ? p.Attribute("Edition").Value : "1";

Linq to Xml de Any() metodunu kullanarak elementimizin Edition isimli herhangi bir nitelik içerip içermediğine bakar. Eğer bir veya birden fazla nitelik varsa true döndürür yoksa false döndürür. Aynı şekilde herhangi bir elementin varlığını da bu metodla kontrol edebilirsiniz.

Kolay gelsin.

Etiketler: ,
Kategoriler: C#

WPF-Single Instance Uygulama Geliştirme
18 Temmuz,2011

Bu yazımızda WPF uygulamalarımızı single instance hale nasıl getirebileceğimizden bahsediyor olacağım. Single instance uygulama demek, uygulamamızın sadece bir örneğinin çalıştırılabilmesi anlamına gelir. Örneğin windows işletim sisteminde hesap makinesini ya da paint uygulamasını birden fazla açabilir ve her birinde farklı işlemler yapabilirsiniz. Çünkü her bir uygulama farklı bir prosestir ve farklı thread lerde çalışırlar. Bu da bize her birinde bağımsız olarak farklı işler yapmamıza olanak sağlar.

image

Uygulamamamızın tek bir örneğinin çalışmasını sağlamak için pek çok yöntem bulabilirsiniz. Bunlardan biri uygulama ilk çalıştığında sistemdeki tüm proseslerin listesini alıp kendisiyle aynı isimli process olup olmadığını kontrol etmektir. Eğer aynı isimli proses yoksa uygulamayı çalıştırmak varsa o anki uygulamadan çıkmaktır. Aslında mantıklı bir çözüm gibi gelse de şöyle bir senaryonun oluşma durumu da var. Ya uygulamamızla aynı ismi taşıyan farklı bir uygulama varsa? İşte bu yüzden bu seçenek pek sağlıklı durmuyor. Diğer bir seçenek de Mutex kullanmaktır.

İki ya da daha fazla thread aynı anda, paylaşılmış bir kaynağa erişmeye çalıştığında, sistemin kaynağı aynı anda sadece bir thread in kullanmasını garanti etmesi gereken bir senkronizasyon mekanizmasına ihtiyacı vardır. Bu noktada Mutex ler devreye girmektedir. İlk thread in mutex’i devradlığını düşünelim, diğer threadler ilk thread mutex’i serberst bırakana kadar askıya alınırlar. Bu kadar teknik bilgiden sonra artık uygulamaya geçmenin zamanıJ

WPF uygulamaların böyle bir yapıyı kullanmak için öncelikle uygulama başlarken bu kontrolün yapılması gerekmektedir. Bu yüzden gerekli kontrolü kend Main metodumuzda yapmalıyız. WPF de main metodumuz otomatik olarak oluşturulur. Oluşturulan metodu görmek için Visual Studio Solution penceresinden Show All Files dediğimizde obj klasörünün içinde bulunan App.g.cs dosyasında bulabilirsiniz. Uygulamayı kendi main metodumuzdan çağırmak için şu yolu izlemeliyiz:

1-İlk olarak App.xaml dosyasının özelliklerinden Application Definition olan build action ını Page olarak değiştirmeliyiz.

2.Kendi tanımladığımız Main metodunu “single-threaded apartment” olarak işaretlemeliyiz. Artık kontrollerimizi yapabiliriz. Aşağıdaki kod parçası amacımıza ulaşmak için yeterli olacaktır.

 

public static bool isFirstInstance=false;
 
        [STAThread]
        public static void Main()
        {            
            using (var mutex=new Mutex(true,"WPFSingleInstanceDemo",out isFirstInstance))
            {
                if (isFirstInstance)
                {
                    MainWindow window = new MainWindow();
                    window.ShowDialog();
                }
                else
                {
                    MessageBox.Show("Uygulama zaten çalışıyor!");
                    App.Current.Shutdown();
                }
            }
        }

 

Yukarıda görüldüğü gibi bir mutex nesnesi oluşturuyoruz. Yapıcı metoduna gönderdiğimiz ilk parametre ile bu mutex i uygulamamızın çalışacağı thread ile ilişkilendiriyoruz, ikinci parametremizde ise mutex in adını belirtiyoruz . Son olarak da eğer daha önce bu isimde mutex yoksa oluşturulduğunu belirtmek için true varsa false değeri döndürdüğünü belirten sonucu almamız için bir değişken veriyoruz. Değişkenin durumuna göre de uygulamanın çalışmasını yönlendirebilirsiniz.

Son olarak hatırlatmakta yarar var. Aynı yapıyı wpf uygulamalarının yanısıra winform uygulamalarında da kullanabilmek mümkün.

SingleInstanceDemo.zip (24,42 kb)

Etiketler: ,
Kategoriler: C# | WPF

Fluent Interface ve Metod Zincirleme Yöntemi
5 Haziran,2011

Metod zincirleme yöntemi yazılım mühendisliğinde Fluent Interface kavramının bir parçası olarak bilinmektedir. Fluent interface ilk olarak Eric Evans ve Martin Fowler tarafından sunulmuştur ve tek amacı kodun okunabilirliğini arttırmaktır. İşte metodları zincirleme yöntemi de bunlardan sadece bir tanesidir. Dilersiniz küçük bir örnek kod ile akıcılığı nasıl yakaladığımıza gözatalım.Örnek senaryo olarak bir oyun yazdığımızı düşünelim ve robot sınıfımızdan iyi bir robot örnekleyelim ve sırasıyla bir silah seçtirelim, belirli bir koordinata yönledirelim, hedefe kilitleyelim ve ateş açtıralım. Öncelikle klasik olarak nasıl yapardık ona bakalım.

Robot goodrobot = new Robot(0, 0);
goodRobot.SelectGun("Rocket");
goodRobot.Move(400, 200);
goodRobot.Aim();
goodRobot.Fire();

Peki metod zincirleme yöntemi ile yaparsak nasıl olur sizce?

Robot goodRobot = new Robot(0, 0);
goodRobot.SelectGun("Rocket").Move(400, 200).Aim().Fire();

Metod isimlerini sırasıyla okuduğunuzda siz de ne kadar akıcı bir kod olduğunu farkedeceksiniz. Böyle bir yapıyı oluşturmanın temelinde aşağıdaki gibi  metodların kendi bulunduğu nesneyi döndürmesi yatıyor.

public class Robot:IRobot
    {
        #region Properties
        public int PositionX { get; set; }
        public int PositionY { get; set; }
        public string GunType { get; set; }
        #endregion        
 
        public Robot(int px,int py)
        {
            PositionX = px;
            PositionY = py;
            Console.WriteLine("Robot created");
            Thread.Sleep(1000);
        }
 
        #region Public Methods
        public IRobot Move(int x, int y)
        {
            PositionX += x;
            PositionY += y;
            Console.WriteLine("Robot has moved to "+x+","+y+" coordinates");
            Thread.Sleep(1000);
            return this;
        }
        public IRobot SelectGun(string gun)
        {
            GunType = gun;
            Console.WriteLine("Gun selected:"+GunType);
            Thread.Sleep(1000);
            return this;
        }
        public IRobot Fire()
        {
            Console.WriteLine("Started to fire");
            Thread.Sleep(4000);
            Console.WriteLine("Enemy down!");
            return this;
        }
        public IRobot Aim()
        {
            Console.WriteLine("Target Locked");
            Thread.Sleep(1000);
            return this;
        }
        #endregion        
    }
    public interface IRobot
    {
        IRobot Move(int x, int y);
        IRobot SelectGun(string gun);
        IRobot Fire();
        IRobot Aim();
    }

Aşağıdaki linkten örnek kodlara ulaşabilirsiniz.

ChainingMethodSourceCode.zip (26,43 kb)

Etiketler:

LINQ'e Farklı Bir Bakış
16 Kasım,2010

LINQ deyince eminim pek çoğumuzun aklına T-SQL’e benzeyen sorgular gelmektedir ve bu da doğrudur. Adından da anlaşılacağı gibi LINQ (Language Integrated Query) yani dil ile tümleştirilmiş sorgu anlamına gelmektedir. Fakat bugün sizlerle LINQ’e farklı bir açıdan bakacağız. Örneğin LINQ’i aşağıdaki gibi convert işlemlerimizde de kullanabiliriz:

string[] strNums = { "78", "23", "35", "6", "67" };

int[] numbers = strNums.Select(p => int.Parse(p)).ToArray();

Gördüğünüz gibi örnekte LINQ’i convert işlemi için kullandık. İsterseniz biraz daha kompleks bir örnek yapalım. Diyelim ki bir Employee sınıfımız olsun ve sınıfımızda bu sınıfın bir nesne listesini döndüren bir de static bir metodu olsun. Bunun dışında bir de Contact sınıfımız olsun. Bu sınıfımızda çalışanların adı ve iletişim bilgisini temsil etmek için propertyler ve kontaktları ekrana yazdırmak için static bir metodumuz bulunmaktadır.   

public class Employee
    {
        public int ID { get; set; }

        public string Name { get; set; }

        public string Surname { get; set; }

        public double Salary { get; set; }

        public static ArrayList GetEmployee()
        {
            ArrayList al = new ArrayList();
            al.Add(new Employee
            {
                ID=1,
                Name="Ali",
                Surname="Köken",
                Salary=5000
            });

            al.Add(new Employee
            {
                ID = 2,
                Name = "Mischa",
                Surname = "Barton",
                Salary = 15000
            });

            al.Add(new Employee
            {
                ID = 3,
                Name = "Jonathan",
                Surname = "Jones",
                Salary = 9000
            });
            return al;
        }
    }

    public class Contact
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public static void PublishContact(Contact[] contacts)
        {
            foreach (var contact in contacts)
            {
                Console.WriteLine("Contact ID:{0} Contact Name:{1}",
               contact.Id,contact.Name);
            }
        }
    }

Burada tam olarak yapacağımız işlemler öncelikle  Employee nesnelerinin saklandığı diziyi IEnumerable<Employee> ‘e  Cast operatörü kullanarak cast etmemiz gerekir. Çünkü ArrayList’de System.Object sınıf tipleri saklanabilmektedir, Employee sınıf tipinin nesneleri saklanmamaktadır. Eğer Employee sınıfımızdaki metodun geri dönüş tipi Employee tipinden generic liste olsaydı bu cast işlemini yapmamıza gerek yoktu.

Daha sonra IEnumarable’daki Employee nesnelerinde select operatörünü çağırararak iterasyon yapacağız. Burada lambda ifadesi ve C#’ın object initializer özelliğini kullanarak Employee nesnelerini kullanarak yeni bir Contact nesnesi oluşturmalıyız. Son olarak da oluşturduğumuz  yeni Contact nesnelerimizi Contact dizisine atmak için aşağıdaki gibi ToArray() metodunu kullanmalıyız : 

ArrayList al = Employee.GetEmployee();
  Contact[] contacts = al.Cast<Employee>().Select(p => new Contact 
  { 
   Id = p.ID, 
   Name = p.Name 
  }).ToArray<Contact>();

  Contact.PublishContact(contacts);

Bu bir kaç satır kod sayesinde LINQ’i başka emellerimize alet ederek Employee sınıfımızı Contact sınıfına convert etmiş oldukJÖrnek uygulamayı aşağıdan indirebilirsiniz.

Kolay gelsin.

LINQtoMe.rar (26,86 kb)

Etiketler: ,
Kategoriler: C#

LINQ ve Extension Metod Aşkı
18 Nisan,2010

Geçenlerde bitirme projemle uğraşırken LINQ teknolojisini ve Extension Metodlarını oldukça sık kullandığımı farkettim. Geriye dönüp baktığımda yazdığım kodları linq kullanmadan yazmaya kalkınca gerçekten de ne kadar vakit kazandığımın bir daha farkına vardım.Projemden örnek vermek gerekirse aşağıdaki gibi bir soru sınıfım var.Her soru bu QuizQuestion sınıfı ile temsil ediliyor.Soruların bulunduğu bir listeden hangi soruların sorulup hangilerinin sorulmadığını bulup sorulmayanları seçmek için iki yöntemi aşağıda bulabilirsiniz.

 

Normal hali:

List<QuizQuestion> sorular = GetQuestions();

List<QuizQuestion> sorulmayanlar = new List<QuizQuestion>();

foreach (QuizQuestion soru in sorular)  

{         

if (soru.Asked==false)

       {

             sorulmayanlar.Add(soru); 

        }  

} 

Linq ile yazılmış hali:

List<QuizQuestion> sorular = new List<QuizQuestion>();

List<QuizQuestion> sorulmayanlar = sorular.Where(q => q.Asked == false).ToList(); 

Gördüğünüz gibi fark ortada.Tabi burada basit düşünmemek lazım çok daha  büyük ve karmaşık projelerde asıl faydası ortaya çıkacaktır.

İyi ki varsın LINQ!

 

Etiketler: ,
Kategoriler: C#

ASP.NET-Web Güvenliği #1
16 Şubat,2010

Bu yazımda  web sitelerinde  kötü niyetli dosya çalıştırma ve bir asp.net projesinde  bu saldırılardan korunma yöntemlerinden birinden bahsedeceğim.Bir web uygulamamız olduğunu ve bu uygulamamızda üyelik sistemi olduğunu düşünelim.Bu siteye üye olan kullanıcılar FileUpload kontrolü ile profiline resim yüklediklerinde www.siteadi.com/users/pictures klasöründe saklansın.Peki kendini hacker sanan bir arkadaş FileUpload ile resim yüklemek yerine index.asp dosyasını yüklerse sonuç ne olur sizce?

 

 

 

 

 

 

 

Dosyayı yüklediğinde www.siteadi.com/users/pictures/index.asp şeklinde direkt bir erişim sağlayabilir ve kendi dosyasını sizin sisteminizde çalıştırabilir,tabi önleminizi almazsanızJHatta açılış sayfanızı silip kendi sayfasını bile koyabilir.Bu tarz olaylarla karşılaşmamak için dosya yükleme yapmadan önce gerekli işlemleri yapmalıyız ve System.IO ve System.Text.RegularExpressions isim alanlarını eklememiz lazım.İlk olarak yüklenecek dosyanın uzantısını almamız gerekiyor.Bunu Path.GetExtension metodu ile şu şekilde yapabilirsiniz.

string ext = System.IO.Path.GetExtension(FileUpload1.PostedFile.FileName);

Dosya uzantısını aldıktan sonra uzantının hepsinin büyük ya da hepsinin küçük harfle başlayıp başlamadığını kontrol etmemiz gerekmektedir.Ben daha çok hepsini küçük harfe çevirerek kullanıyorum.ext = ext.ToLower();Son olarak Regex sınıfının IsMatch metodunu kullanarak yüklenecek dosyanın  istediğimiz dosya formatları ile uyuşup uyuşmadığını kontrol ediyoruz.Hepsi bu kadar!

if (Regex.IsMatch(ext,".jpg|.jpeg|.bmp|.gif"))

{    

    //TODO:Resim formatı uygun

}

else

{

  //TODO:Uygun olmayan dosya formatı

} 

Gördüğünüz gibi bu tip saldırıları önlemek oldukça kolay.Basit bir saldırı yöntemi olmasına rağmen üzücü sonuçlar yaratabilecek kadar da iyi bir yöntem.

Kolay gelsin!

Kategoriler: C#

LINQ to SQL-Veritabanına Dosya Kaydetme ve Okuma
13 Ocak,2010

LINQ to SQL , SQL Server’ın varbinary tipini desteklemektedir.Bu sayede veritabana binary olarak dosyalarımızı kaydedebiliriz.Örneğin bir web sitesinde veritabanına kullanıcı profil resimlerininin path’ini  kaydetmek yerine resmi binary olarak kaydedebiliriz.

Veritabanına dosya kaydetme

İlk olarak veritabanımızı oluşturmamız gerekli.Ben kendi veritabanımda Store isimli bir tablo oluşturdum.Tablonun design’ı aşağıdaki gibidir.

Store tablomu tasarladıktan sonra projeme Add New Item diyerek yeni bir LINQ to SQL sınıfı ekliyorum.Daha sonra Server Explorer penceresinden Store tablomu bu sınıfa sürükleyip bırakıyorum.Bu sayfayı kaydedip çıktıktan sonra  sıra geldi dosyamızı veritabanına kaydetmeye.Aşağıdaki kod ile artık dosyamızı veritabanına binary olarak kaydedebiliriz.

NorthWindDataContext ctx = new NorthWindDataContext();

//Store tipinde yeni bir Entity oluşturuyoruz

var stored = new Store();

//Kaydedilecek resmin yolu

string path = @"C:\Users\KullanıcıAdı\Desktop\lf.jpg";

//Kaydedilecek dosyayı byte dizi şeklinde okuyoruz

stored.Binary = File.ReadAllBytes(path);

//Dosyamızın ismini alıp kaydediyoruz

stored.Filename = Path.GetFileName(path);

//Artık oluşturduğumuz entity'yi veritabanına ekliyoruz

ctx.Stores.InsertOnSubmit(stored);

//DataContex'imizde değişiklik olduğu için

//bu değişikliği veritabanına kaydediyoruz

ctx.SubmitChanges();

Yukarıdaki kodun gayet açık olduğunu düşünüyorum üstelik her satırın ne iş yaptığını yorum satırlarında da belirttim.İsterseniz bir de kaydettiğimiz bu verileri veritabanından nasıl çekeceğimize bakalım.

Veritabanından binary dosya okuma

Binary olarak kaydettiğimiz verileri diske geri yazmak da kaydetmek kadar kolay.Bunu yaparken ToArray() metodu çok işimize yarayacak:)

string path = @"C:\temp";

var file = p.Stores.First();

File.WriteAllBytes(Path.Combine(path, file.Filename), file.Binary.ToArray());

Gördüğünüz gibi birkaç satırla  veritabanına kaydettiğimiz binary dosyamızı diske geri yazabiliriz.File sınıfının WriteAllBytes metodunu ve ToArray metodu ile bunu yapabiliriz.Önemli bir nokta veritabanında binary tipinde bir kolon oluşturduğunuzda max 8000 olduğudur.Eğer büyük bir dosya kaydetmeye çalışırsanız “binary data would be truncated” şeklinde bir hata alırsınız.Binary datanın küçültülmesi gerektiği uyarısını veren bu hatayı almamak için önce dosya boyutu kontrolü yapmanızı tavsiye ederim.Kolay gelsin.

Etiketler: , ,
Kategoriler: C#

LINQ-Son N Kaydı Getirme
24 Ağustos,2009

Bu yazımda projelerimde  çok işime yarayan bir LINQ sorgusundan bahsetmek istiyorum.Bir veri kaynağından o kaynaktaki son n tane kaydı çeken bir sorgu yazacağız.Dilerseniz bir Console uygulaması oluşturup örneğimize başlayalım.Örneğimizde bir önceki linq makalemdeki gibi Person tipinde nesneleri barındıran generic list ile çalışacağız.ID ve Name propertylerini içeren Person sınıfını projemize ekleyelim.Daha sonra ana fonksiyonda listeye ekleme yapalım.

static void Main(string[] args)

{

     List<Person> people = new List<Person>        

   {          

        new Person{ID=1,Name="Betül"},

        new Person{ID=3,Name="Ayşe"}, 

        new Person{ID=5,Name="Ahmet"},   

        new Person{ID=2,Name="Selim"}, 

        new Person{ID=4,Name="Kübra"}  

      };  

          GetLastN(people,3);

 }

Şimdi son n tane kaydı getiren linq sorgumuzu  sonucu ekrana yazdıran bir metod içinde yapalım.Metodumuz parametre olarak listeyi ve en son getirelecek kayıt sayısını alır.

public static void GetLastN(List<Person> people,int n)   

  {

             IEnumerable<Person> lastN = (from p in people    

                                                          select p).OrderByDescending(c => c.ID).Take(n); 

             foreach (Person person in lastN)         

             {

                  Console.WriteLine(person.Name);           

              }

 }

Metodumuzun işleme mantığı kendisine parametre olarak gelen listedeki kayıtları önce azalarak sıralar ve sonra Take(int n) fonksiyonu ile ilk n kaydı alır.Daha sonra kontrol amaçlı olarak foreach ile aldığımız ilk  n kaydı ekrana basıyoruz

Teşekkürler.

Etiketler: ,
Kategoriler: C#

Speech Synthesizer Kullanımı
20 Ağustos,2009

Bugün  birkaç satır kod ile bazı uygulamalarımızda kullanabileceğimiz ve C# 3.0’ın özelliklerinden yararlanarak Extension Method’larla ilgili bir örnek paylaşacağım.

txtWord.Text=”Silverlight”;

txtWord.Text.Speak();

Yukarıdaki iki satırlık kodu çalışır kılmak mümkün müdür acaba?Tabii ki mümkün:)İlk olarak extension method için static bir class tanımlamak ve bu static class’ın içine static olan extension metodumuzu tanımlamalıyız.Ama önce System.Speech.Synthesis namespace’ini projemize referans olarak eklemeliyiz.Referansımızı ekledikten sonra  SpeechSynthesizer’ın bir instance alarak bu nesne üzerinden Speak metoduna ulaşabiliriz.   

public static class Speak   

{      

     public static void Speak(this string text)     

     {        

            SpeechSynthesizer synth = new SpeechSynthesizer();  

            synth.Speak(text);     

     }

  }

Burada diğer önemli bir nokta metoda gelen parametre tipinden önceki  this anahtar kelimesidir.Extension Method tanımlanırken this anahtar kelimesi kullanılır.Bu keyword aslında kullanım şeklinden de anlaşılacağı gibi çağrıldığı nesne üzerinden işlem yapabilmek için kullanılır.Örnekte de göreceğiniz gibi tekrar parametre göndermeye gerek kalmamıştır.Artık Extension metodumuz hazır,kullanmak içinse bir winform uygulaması açıp forma bir textbox ve bir buton koyun.Butona tıklandığında ise textboxa yazılan yazıyı seslendirsin. 

private void button1_Click(object sender, EventArgs e) 

{    

    textBox1.Text.Speak(); 

}

Teşekkürler. 

Etiketler: ,
Kategoriler: C#

LINQ-Dinamik Sıralama
15 Ağustos,2009

LINQ sorgularında dinamik sıralama  ile kullanıcının isteğine göre ayrı ayrı linq sorguları yazmaktansa tek bir sorguda bunu nasıl yapabiliriz ona değinmek istiyorum.Dilerseniz örnek uygulama üzerinden devam edelim.Örneğimizi Console uygulamasında gerçekleştireceğim.İlk olarak Person adında bir sınıf oluşturarak ID ve Name olmak üzere iki tane property tanımlaması yapalım.Daha sonra main fonksiyonu içine şu kodları yazalım.

static void Main(string[] args)
{
       List<Person> people = new List<Person>
      {
             new Person{ID=1,Name="Betül"}, 
             new Person{ID=3,Name="Ayşe"}, 
             new Person{ID=5,Name="Ahmet"},
             new Person{ID=2,Name="Selim"},    
             new Person{ID=4,Name="Kübra"} 
        };
            Sort(people,p=>p.ID);
    }

Listemize birkaç tane kişi ekledikten sonra sıralama listemizde sıralama yapan linq sorgusunu yazalım.

public static void Sort<TKey>(List<Person>people,Func<Person,TKey>selector)  
{
            var sortedCollection = from p in people 
                                            orderby selector(p) 
                                            select p; 
           sortedCollection.ToList<Person>().ForEach(p => Console.WriteLine(p.Name));        
}

Bu metodla listemizde istersek ID ye göre istersek de Name ‘e göre sıralama yaptırabiliriz.Hemde tek bir sorguda:)Örneğimizde ana fonksiyondan Sort fonksiyonunu ID ye göre çağırıyoruz ve ekranda bize ID’ye göre sıralanmış olan listedeki elemanların isimlerini ekranda göstermiş olduk.

Teşekkürler.

Kategoriler: C#