Merhabalar,

Bu makalemde sizlerle birlikte daha önceki makalemde bahsettiğim SSML yapısını inceleyeceğiz. SSML speech synthesis uygulamalarında kullanılan XML tabanlı bir dildir. Zaten açılımı Speech Synthesis Markup Language’dir. W3C tarafından standardize edilmiştir. Bu standart Sun Systems tarafından geliştirilen JSGF ve JSML standartları temel alınarak geliştirilmiştir. SSML, metin okuma işlemlerinde kullanıcıya farklı platformlarda telaffuz, ses seviyesi, hız, cinsiyet gibi parametreleri kontrol etme imkanını sağlar. SSML ile kullanılan teknikler arasında sesli yanıt sistemleri de bulunmaktadır. Bir SSML dökümanı içindeki bileşenler, XML yapısının gereği de olduğundan çoğu zaman kolay anlaşılabilir ve kullanılabilirdirler. Bir SSML dökümanı otomatik olarak değiştirilip, oluşturulabileceği gibi, elle de düzenlenebilir. Biraz önce de bahsettiğim gibi SSML, platform bağımsız metin okuma işlemlerinin yapılmasına olanak sağlar. Okuma işlemlerini çok çeşitli şekillerde gerçekleştirebilir, okunan metnin istenilen yerlerinde vurgu, duraklama gibi işlemleri rahatça yapabilirsiniz. Aynı zamanda, makalemin ilerleyen kısımlarında da belirteceğim gibi bir SSML dökümanı içerisinde birden fazla farklı dillerde okuma işlemi gerçekleştirebilirsiniz.

Şimdi standart bir SSML dökümanının yapısının nasıl olduğunu inceleyelim. SSML de temelde bir XML dökümanıdır ve her XML dökümanı gibi SSML dökümanları da bir kullanılan XML dökümanının versiyonunu belirten bir XML ifadesi ile başlamalıdır. Ardından da opsiyonel olarak DOCTYPE tag’ı gelebilir:





<?xml version=”1.0″?>
<!DOCTYPE speak PUBLIC “-//W3C//DTD SYNTHESIS 1.0//EN”
            “http://www.w3.org/TR/speech-synthesis/synthesis.dtd”>


Bir SSML dökümanının root element’i <speak> elementidir. Dökümanda okunacak olan metin <speak> ve </speak> tag‘larının arasına yerleştirilmedir. <speak> elementinde bulunması gereken attribute’lar ise version, xmlns ve xml:lang attribute’larıdır. version attribute’ının değeri “1.0″ olmalıdır. xmlns attribute’ı SSML namespace’ini işaret eder ve “http://www.w3.org/2001/10/synthesis” değerini alır. xml:lang attribute’ı ise root element için metnin okunacağı dili belirtir. xml:lang attribute’ı root dışında <voice>, speak>, <p> ve <s> elementleri için de tanımlanmıştır. Böylece bir metnin farklı yerlerinde farklı dillerde okunması sağlanabilir. Bu elementlerin anlamlarını ve kullanımlarını makalenin ilerleyen kısımlarında okuyabilirsiniz. Son olarak, root’ta bulunması zorunlu olmayan ancak tavsiye edilen attribute’lardan bir tanesi xsi:schemaLocation attribute’ıdır. Bu attribute SSML şemasının lokasyonunu belirtir. Bütün anlatılanlardan sonra bu noktaya kadar örnek bir SSML dökümanı aşağıdaki gibi olabilir:






<?xml version=”1.0″?>
<speak version=”1.0″
        xmlns=”http://www.w3.org/2001/10/synthesis
        xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance
        xsi:schemaLocation=”http://www.w3.org/2001/10/synthesis
        http://www.w3.org/TR/speech-synthesis/synthesis.xsd
        xml:lang=”en-US”>
</speak>


Bir SSML dökümanının yapısının nasıl olması gerektiğini gördükten sonra artık SSML dökümanında kullanılabilecek element ve attribute’ları incelemeye başlayabiliriz.
1- <speak>: Biraz önce de bahsettiğim gibi SSML dökümanındaki root element speak elementidir. Biraz önce bahsetmiş olduğum version, xmlns ve xml:lang gibi attribute’ları barındırır. Bunların dışında <audio>, <break>, <emphasis>, <lexicon>, <mark>, <meta>, <metadata>, <p>, <phoneme>, <prosody>, <say-as>, <sub>, <s> ve <voice> elementleri de <speak> elementi içinde kullanılabilir. Çok basit bir örnekte <speak> elementini kullarak ilk metnimizi okutalım:






<?xml version=”1.0″?>
<speak version=”1.0″
        xmlns=”http://www.w3.org/2001/10/synthesis
        xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance
        xsi:schemaLocation=”http://www.w3.org/2001/10/synthesis
        http://www.w3.org/TR/speech-synthesis/synthesis.xsd
        xml:lang=”en-US”>
        Hello World
</speak>


2- xml:lang: Biraz önce de bahsettiğim gibi xml:lang attribute’ı metnin hangi dilde okunmasını istiyorsak o dilde okunmasını sağlar. Bir metnin tamamına tek bir xml:lang attribute’ı tanımlanabileceği gibi, metnin farklı kısımlarına farklı xml:lang attribute’ları tanımlanarak farklı dilde yazılmış metinlerin ilgili dillerde okunmasını sağlayabilirsiniz. xml:lang’ın örnek bir kullanımı aşağıdaki gibidir:






<?xml version=”1.0″?>
<speak version=”1.0″ xmlns=”http://www.w3.org/2001/10/synthesis”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://www.w3.org/2001/10/synthesis
http://www.w3.org/TR/speech-synthesis/synthesis.xsd”
xml:lang=”fr”>
<p xml:lang=”fr”>Je veux manger de la glace.</p>
<p xml:lang=”en-US”>means I want to eat ice cream in English</p>
</speak>


3- <lexicon>: Okunacak olan metnin telaffuz bilgilerinin tutulduğu dökümanın belirtildiği elementtir. Lexicon elementinin uri attribute’ı telaffuz bilgilerinin yolunu belirtir. type attribute’ı ise dökümanın medya tipini belirtir. lexicon elementi sadece speak elementi içinde kullanılabilir. Bir SSML dökümanında birden fazla lexicon elementi tanımlanabilir. Örnek kullanımı aşağıdaki gibidir:






<?xml version=”1.0″?>
<speak version=”1.0″ xmlns=”http://www.w3.org/2001/10/synthesis”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://www.w3.org/2001/10/synthesis
http://www.w3.org/TR/speech-synthesis/synthesis.xsd”
xml:lang=”fr”>
<lexicon uri=”http://www.lexiconexamples.com/words.file”
           type=”media-type”/>
</speak>    


4- <p> ve <s> elementleri: <p> elementi paragraf, <s> elementi cümleyi belirtir. Kullanımları opsiyoneldir. <p> elementi <audio>, <break>, <emphasis>, <mark>, <phoneme>, <prosody>, <say-as>, <sub>, <s>, <voice> elementlerini ve metin içerebilir. <s> elementi de biraz önceki elementlerden kendisi hariç olanları ve metin içerebilir. Örnek kullanımı aşağıdaki gibidir:






<?xml version=”1.0″?>
<speak version=”1.0″ xmlns=”http://www.w3.org/2001/10/synthesis
        xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance
        xsi:schemaLocation=”http://www.w3.org/2001/10/synthesis
        http://www.w3.org/TR/speech-synthesis/synthesis.xsd
        xml:lang=”en-US”>
    <p>
        <s>This is a sentence in a paragraph.</s>
        <s>This is the second sentence.</s>
    </p>
</speak>


5- <phoneme>: Bu element okunacak olan metnin fonetik teleffuz bilgisinin tutulduğu elementtir. <phoneme> elementinin ph attribute’ı bu fonetik okunuş şeklini taşır. Bir diğer attribute olan alphabet attribute’ı ise fonetik alfabenin adını belirtir. <phoneme> elementi sadece metin içerebilir, attribute‘ı yoktur.
6-
<sub>
: Bu element içinde bulunan www, w3c gibi metinlerin açılışını alias attribute’ının içine yazdığımızda, metin okunurken alias attribute’ında yazan değer okunur. Örnek bir kullanımı aşağıdaki gibidir:





<?xml version=”1.0″?>
<speak version=”1.0″ xmlns=”http://www.w3.org/2001/10/synthesis
        xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance
        xsi:schemaLocation=”http://www.w3.org/2001/10/synthesis
        http://www.w3.org/TR/speech-synthesis/synthesis.xsd
        xml:lang=”en-US”>
<sub alias=”Hyper Text Transfer Protocol”>http</sub>
</speak>


7- <voice>: Metni okuyacak olan sesin özelliklerinin belirtildiği elementtir. xml:lang, age, variant, name attribute’larına sahiptir. xml:lang, biraz önce de bahsettiğim gibi okunacak olan metnin hangi dilde okunacağının belirlendiği attribute’dır. age attribute’ı metni okuyacak olan sesin yaşının belirlendiği attribute’dır. variant attribute’ı ise bir metni okuyan karakter dışında diğer bir opsiyon olan karakterin değişkenini belirtir. Örneğin metnin bir kısmını kız çocuk sesi okuyorsa ve başka bir kısmını farklı ikinci bir kız çocuğun okumasını istiyorsak variant attribute’ını kullanabiliriz. Son olarak name attribute’ını kullanarak bilgisayarda kayıtlı olan sesin isminden metni okutma işlemini gerçekleştirebiliriz:






<?xml version=”1.0″?>
<speak version=”1.0″ xmlns=”http://www.w3.org/2001/10/synthesis
    xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance
    xsi:schemaLocation=”http://www.w3.org/2001/10/synthesis
    http://www.w3.org/TR/speech-synthesis/synthesis.xsd
    xml:lang=”en-US”>
<voice gender=”female” age=”9″>I went to school today.</voice>
<voice gender=”female” variant=”2″>We had an exam today.</voice>
<voice name=”Michael”>We will have an exam tomorrow.</voice>
</speak>


8- <emphasis>: Bu element içinde yeralan metin vurgulu okunur. level attribute’ı vardır. Bu attribute strong, moderate, none, reduced değerlerini alır. level attribute’ının varsayılan değeri moderate’dır. Bu attribute’ın değeri normal veya moderate olduğunda metin vurgulanarak okunur. none değeri, metni okuyacak olan robotun metni vurgulama ihtimalini ortadan kaldırmak istenirse kullanılır. reduced değeri ile düşük seviyede vurgulama yapılır.






<?xml version=”1.0″?>
<speak version=”1.0″ xmlns=”http://www.w3.org/2001/10/synthesis
    xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance
    xsi:schemaLocation=”http://www.w3.org/2001/10/synthesis
    http://www.w3.org/TR/speech-synthesis/synthesis.xsd
    xml:lang=”en-US”>
I <emphasis level=”strong”> don’t </emphasis> want to go to school tomorrow!
</speak>


9- <break>: <break> elementini okunan metinde duraklama yapılmasını istediğimiz yerlerde kullanabiliriz. Bu elementin iki adet attribute’ı vardır. Bunlardan birincisi strength attribute’ıdır. strength ile yapılacak duraklamanın şiddetini belirleriz. none, x-weak, weak, medium, strong ve x-strong değerlerini alır. Default değeri medium’dur. <break> elementinin diğer bir attribute’ı da time’dır. Metnin ilgili kısmında yapılacak olan duraklamanın saniye veya milisaniye cinsinden değerini belirleyebiliriz.






<?xml version=”1.0″?>
<speak version=”1.0″ xmlns=”http://www.w3.org/2001/10/synthesis
    xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance
    xsi:schemaLocation=”http://www.w3.org/2001/10/synthesis
    http://www.w3.org/TR/speech-synthesis/synthesis.xsd
    xml:lang=”en-US”>
Internet Explorer <break strength=”weak”/> makes it easier to get the most from the World Wide Web
<break time=”5s”/> whether you are searching for new information or browsing your favorite Web sites.
</speak>


10- <audio>: Metin okunurken metnin belirli yerlerinde bir ses dosyasının çalınmasını sağlar. Bunun için tek attribute’ı olan src attribute’ının değerini ses dosyasının lokasyonu ve dosyanın adı şeklinde set etmek gerekir.






<?xml version=”1.0″?>
<speak version=”1.0″ xmlns=”http://www.w3.org/2001/10/synthesis
    xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance
    xsi:schemaLocation=”http://www.w3.org/2001/10/synthesis
    http://www.w3.org/TR/speech-synthesis/synthesis.xsd
    xml:lang=”en-US”>
    Here is some sound.
<audio src=”C:\CHIMES.WAV”>
</audio>
</speak>


Bir SSML metninde kullanabileceğiniz belli başlı elementler ve bu elementlerin attribute’larından bahsettim. SSML kullanımı ve SSML metinlerini okutabileceğiniz bir örneği daha önce yazmış olduğum makalemden bulabilirsiniz.

Işıl Orhanel

Merhabalar,

Birçoğumuz TextToSpeech uygulamalarını kullanmış ya da duymuşuzdur. .Net Framework 3.0′ın çıkması ile birlikte bu işlemi yapabilmemiz için gerekli classlar bize System.Speech.Synthesis namespace’i altında gelmektedir. İlk kullanım alanları olarak akla bir text dosyasının ses olarak dinlenmesi veya ses dosyasına çevrilmesi, bir add-in yazılarak outlook’ta gelen mailin okutulması, hata mesajı veya uyarı mesajı vereceğimiz noktalarda bu mesajları sesli olarak vermek gelmektedir.

Bu makalemde ben System.Speech.Synthesis namespace’i ile gelen SpeechSynthesizer class’ından bahsedeceğim. Bu class sayesinde biraz önce bahsetmiş olduğum örnekler gibi işlemleri ve daha fazlasını uygulamalarınızda gerçekleştirebilirsiniz.İlk olarak SpeechSynthesizer nesnesinin belli başlı property, metod ve event’lerini inceleyelim.



























































































Property


Rate


Yazının karakter tarafından ne kadar hızlı okunacağının belirlendiği property’dir. -10 ve 10 arası değer alır.


State


Yazının okunma durumunun hangi aşamada olduğunun tutulduğu property’dir. SynthesizerState enum’unun Paused, Ready ve Speaking değerlerinden birini alır.


Voice


Yazıyı okuyan sesin Name, Age, Gender, Culture gibi özelliklerinin tutulduğu VoiceInfo tipindeki property’dir.


Volume


Ses düzeyinin belirlendiği property’dir.


Method


AddLexicon


Sözlük ekler.


GetCurrentlySpokenPrompt


Geçerli olan okunan yazının alınmasını sağlar.


GetInstalledVoices


Bilgisayarda kayıtlı olan SpeechSynthesizer seslerinin alınmasını sağlar.


Pause


Okuma işleminin duraklatılmasını sağlar.


RemoveLexicon


Sözlüğün kaldırılması işlemini yapar.


Resume


Duraklamış olan okuma işleminin kaldığı yerden devam etmesini sağlar.


SelectVoice


Sistemde kayıtlı olan seslerden isim parametre gönderilerek yazının hangi ses tarafından okunacağını belirler.


SelectVoiceByHints


Sistemde kayıtlı olan seslerden Cinsiyet, Yaş gibi parametreler gönderilerek yazının okunacağı sesi belirler.


SetOutputToAudioStream


Sesi Audio Stream olarak kaydeder.


SetOutputToDefaultAudioDevice


Sesi varsayılan audio cihazına yönlendirir.


SetOutputToWaveFile


Sesi bir .wav dosyası olarak kaydeder.


SetOutputToWaveStream


Sesi bir wave stream’e atar.


Speak


Okuma işlemini yapar. Ancak okuma işlemi süresince uygulamanız kilitlenir ve uygulama üzerinde başka bir işlem yapamazsınız. Uygulama işlemi tek thread’de çalışır.


SpeakAsync


Okuma işlemini asenkron şekilde yapar. Böylece okuma devam ederken uygulamanız kilitlenmez ve uygulama üzerinde başka işlemler de gerçekleştirebilirsiniz. Asenkron olarak farklı thread’de çalışır.


SpeakAsyncCancel


Parametre aldığı bir Prompt nesnesinin okunma işlemini iptal eder.


SpeakAsyncCancelAll


Bütün okuma işlemlerini iptal eder.


SpeakSsml


SSML text’leri okumaya yarar.


SpeakSsmlAsync


SSML text’leri asenkron biçimde okur.


Event


SpeakCompleted


Okuma işlemi bittiği zaman tetiklenir.


SpeakProgress


Okuma işlemi sırasında tetiklenir.


SpeakStarted


Okuma işlemi başladığı zaman tetiklenir.


StateChanged


Okuma işleminin durumu değiştiği zaman tetiklenir.


VoiceChange


Yazıyı okuyan ses değiştiğinde tetiklenir.


Şimdi örnek bir uygulama üzerinden giderek SpeechSynthesizer class’ı ile text okunması, .wav dosyasına kaydedilmesi gibi işlemleri nasıl yapabileceğimizi inceleyelim. Yeni bir solution oluşturup, yeni bir Windows uygulaması ekleyelim. Ardından da kullanacağımız formu aşağıdaki gibi dizayn edelim:



Formda, yazıyı okuyacak olan sesi cinsiyet ve yaş kriterlerine göre belirleyebileceğimiz bir alan bulunuyor. Bunun yanısıra kullanıcı isterse Sese Göre kısmından Microsoft Sam, Michael gibi karakterleri de seçerek yazıyı okutabiliyor olacak. Okunacak olan yazının yazılacağı textbox’ın altındaki konuş butonu textbox’daki yazının cinsiyet – yaşa göre veya sese göre seçeneklerinden birine göre seçilmiş olan karakter tarafından okunmasını sağlayacak. Duraklat butonu ile okuma işlemini duraklatacağız, Devam Et butonu ile duraklamış olan okumayı devam ettireceğiz, İptal Et butonu ile okuma işlemini iptal edeceğiz. .wav yap butonu ile de yazıyı .wav dosyası olarak kaydedeceğiz. Durum label’ında SpeechSynthesizer nesnesinin State property’sinde tutulan değerleri yazı okundukça değiştireceğiz. SSML butonu ile de bir SSML metnini okuyacağız. Kodu yazmaya başlayalım:





SpeechSynthesizer s;

public
frmSynthesizer()
{
    InitializeComponent();
    s = new SpeechSynthesizer();
    s.SpeakProgress += new EventHandler<SpeakProgressEventArgs>(s_SpeakProgress);
    s.StateChanged += new EventHandler<StateChangedEventArgs>(s_StateChanged);
    s.SpeakCompleted += new EventHandler<SpeakCompletedEventArgs>(s_SpeakCompleted);
}

private
void frmSynthesizer_Load(object sender, EventArgs e)
{
    FillGenderCombo();
    FillVoiceAgeCombo();
    FillVoicesCombo();
}


Class level bir SpeechSynthesizer nesnesi tanımladıktan sonra formun constructor’ında instance’ını alıyoruz ve SpeakProgress, StateChanged, SpeakCompleted event’lerini handle etmek için tanımlıyoruz. Ardından da formun load’ında form üstündeki Cinsiyet, Yaş ve Ses combobox’larını doldurmak üzere yazdığımız metodları çağırıyoruz. Bu metodların da kodları aşağıdaki gibi:






private void FillGenderCombo()
{
    cmbGender.DataSource = Enum.GetValues(typeof(VoiceGender));
}
private
void FillVoiceAgeCombo()
{
    cmbAge.DataSource = Enum.GetValues(typeof(VoiceAge));
}
private
void FillVoicesCombo()
{
    foreach
(InstalledVoice iv in s.GetInstalledVoices())
    {
        cmbVoice.Items.Add(iv.VoiceInfo.Name);
    }
}


FillGenderCombo metodunda VoiceGender enum’unu, FillVoiceAgeCombo metodunda ise VoiceAge enum’unu ilgili combobox’lara datasource olarak atıyoruz. FillVoicesCombo metodunda ise SpeechSynthesizer nesnesinin GetInstalledVoices metodunu kullanarak aldığımız InstalledVoice nesnelerinin isimlerini Ses combobox’ına ekliyoruz. Bu aşamada yapacağımız bir şey daha bulunuyor. Ses combobox’ının selected index’i her değiştiğinde seçili olan sesin bazı bilgilerini hemen altta bulunan Ses Özellikleri label’ına yazdıralım:






private void cmbVoice_SelectedIndexChanged(object sender, EventArgs e)
{
    IEnumerable
<InstalledVoice> v = from a in s.GetInstalledVoices() where a.VoiceInfo.Name == cmbVoice.SelectedItem.ToString() select a;
    lblVoiceProperties.Text = “Cinsiyet: “ + v.First<InstalledVoice>().VoiceInfo.Gender.ToString() + “\r\nYaş: “ + v.First<InstalledVoice>().VoiceInfo.Age.ToString() +
    “\r\nAçıklama: “
+ v.First<InstalledVoice>().VoiceInfo.Description;  
}


Kodda da görüldüğü gibi SpeechSynthesizer  nesnesinin GetInstalledVoices fonksiyonu ile InstalledVoice collection’ı için ufak bir linq sorgusu yazdık. Ardından da select yaptığımız InstalledVoice nesnesinin Gender, Age ve Description property’lerini formdaki label’a yazdırdık. Buraya kadar uygulamayı çalıştırıp, geldiğimiz noktayı inceleyelim:



Uygulamayı çalıştırdığımızda Cinsiyet, Yaş ve Ses combobox’larının dolduğunu göreceğiz. Ses combobox’ındaki seçili öğeyi değiştirdiğimizde ise Ses Özellikleri label’ında ilgili sesin özellikleri yazıyor olacaktır. Şimdi Konuş butonunun kodunu yazalım:




private void btnSpeak_Click(object sender, EventArgs e)
{

    if
(rbtnGenderAge.Checked)
    {
        s.SelectVoiceByHints((System.Speech.Synthesis.VoiceGender)cmbGender.SelectedItem, (System.Speech.Synthesis.VoiceAge)cmbAge.SelectedItem);
    }

    else
if (rbtnVoice.Checked)
    {
        s.SelectVoice(cmbVoice.SelectedItem.ToString());
    }
    s.SpeakAsync(txtSubject.Text);
}


Cinsiyet – Yaşa Göre radiobutton’ı seçili ise SelectVoiceByHints metodunu, Sese Göre radiobutton’ı seçili ise SelectVoice metodunu çağırarak yazıyı okuyacak sesin özelliklerini belirliyoruz. Ardından da SpeakAsync metodu ile txtSubject textbox’ındaki yazıyı okutuyoruz. Şimdi diğer butonların kodunu yazalım:






private void btnPause_Click(object sender, EventArgs e)
{
    s.Pause();
}
private
void btnResume_Click(object sender, EventArgs e)
{
    s.Resume();
}
private
void btnCancel_Click(object sender, EventArgs e)
{
    Prompt
p = s.GetCurrentlySpokenPrompt();
   
s.SpeakAsyncCancel(p);
}
private
void btnWav_Click(object sender, EventArgs e)
{
    s.SetOutputToWaveFile(@”C:\io.wav”);
    s.SpeakAsync(txtSubject.Text); 
}
private
void btnSSML_Click(object sender, EventArgs e)
{
    s.SpeakSsmlAsync(txtSubject.Text);
}


Duraklat ve Devam Et buton’larının click event’lerinde SpeechSynthesizer nesnesinin Pause ve Resume metodlarını çağırdık. İptal butonunda ise GetCurrentlySpokenPrompt metodu ile mevcut olan okuma işlemini aldık ve SpeakAsyncCancel metoduna parametre göndererek işlemi iptal ettik. .wav dosyası kaydetmek için SetOutputToWaveFile metodunu çağırdık. SSML hakkında daha detaylı bilgiyi bir sonraki makalemde veriyor olacağım. Ancak şimdi kısaca değinmek gerekirse SSML yani Speech Synthesis Markup Language, metin okuma işlemlerini XML tabanlı metinler vasıtasıyla yapmamızı sağlayan standarda verilen isimdir. SSML, metin okuma işlemlerinde kullanıcıya farklı platformlarda telaffuz, ses seviyesi, hız, cinsiyet gibi parametreleri kontrol etme imkanını sağlar. Aşağıdaki örnek SSML metnini uygulamamızdaki textbox’a yapıştırıp SSML butonuna tıklayarak metni okutabilirsiniz:






<?xml version=”1.0″?>



<!DOCTYPE speak PUBLIC “-//W3C//DTD SYNTHESIS 1.0//EN”
            “http://www.w3.org/TR/speech-synthesis/synthesis.dtd”>
<speak version=”1.0″
        xmlns=”
http://www.w3.org/2001/10/synthesis
        xmlns:xsi=”
http://www.w3.org/2001/XMLSchema-instance
        xsi:schemaLocation=”http://www.w3.org/2001/10/synthesis
       
http://www.w3.org/TR/speech-synthesis/synthesis.xsd
       
xml:lang=”en-US”>
<p>
    <voice gender=”male”>
    <s>You can find information on the Web in a variety of ways.</s>
    <s>Click the Search button on the toolbar to gain access to a number of search providers. Type a word or phrase in the Search box.</s>
    </voice>
</p>
<p>
    <voice gender=”female”>
    Type go, find, or ? followed by a word or phrase in the Address bar. Internet Explorer starts a search using its predetermined search provider.
    </voice>
</p>
<p>
    <voice gender=”male”>
    After you go to a Web page, you can search for specific text on that page by clicking the Edit menu, and then clicking Find.
    </voice>
</p>
</speak>


Son olarak SpeechSynthesizer nesnesinin bazı event’lerine örnekler verelim:






void s_SpeakProgress(object sender, SpeakProgressEventArgs e)
{
    lblProgress.Text = e.Text;
}
void s_SpeakCompleted(object sender, SpeakCompletedEventArgs e)
{
    lblProgress.Text = “”;
}
void
s_StateChanged(object sender, StateChangedEventArgs e)
{
    lblState.Text = “Eski durum: “ + e.PreviousState.ToString() + ” Yeni durum: “ + e.State;
}


SpeakProgress event’inde yani metin okunurken lblProgress label’ının Text property’sine metnin okunan kısmını yazıyoruz. SpeakCompleted event’inde ise lblProgress label’ının text’ini temizliyoruz. StateChanged event’inde ise SpeechSynthesizer nesnesinin durumu değiştikçe lblState label’ına eski durumu ve yeni durumu yazdırıyoruz.

Işıl Orhanel

Media Player Gün içinde müzik dinlemek, film veya video izlemek için veya kod yazarken dış etkenlerden etkilenmemek için en çok kullandığımız medya çalıcısı. Peki ya media player’ın özelliklerini uygulamamıza eklemek istersek ne yapmalıyız? Özellikle addin’ler yazarak media player’ı sıklıkla kullandığımız Microsoft uygulamalarının içinden yönetebiliriz.  Bu yazımda bu işlem için kullanacağımız COM bileşeninin metodları, eventları ve property’lerinin neler olduğuna ve nasıl kullanıldığına değinmeye çalışacağım.

Yazaca
ğımız örnek uygulamamızda Media Player component’ini kullanabilmek için ilk olarak toolbox’a ekleyelim.Toolbox’a sağ tıkladığımızda açılan Choose Toolbox Items ekranındaki COM Components sekmesinde yer alan öğelerden Windows Media Player’ı seçiyoruz:


Toolbox’a Media Player component’i eklendiğinde sürükleyip forma bırakarak kullanmaya başlayabiliriz. Forma eklediğimiz kontrol bir axWindowsMediaPlayer nesnesi olacaktır. axWindowsMediaPlayer nesnesi, uygulamalarımızda Media Player kullanabilmemiz için bize property, metod ve event’leri sunan temel nesnedir. Yazacağımız uygulama media player ile yaptığımız bazı işlemleri C# ile nasıl yapabileceğimize dair örnek oluşturacak. İlk olarak formu tasarlayalım:



Formda bir tab control ve Media Player component’i bulunuyor. Her tab control’ün içinde yapacağımız farklı türde işlemlere ait kontroller bulunuyor. İlk tab ile başlayalım. Bu tab’da bulunan Dosya Aç butonu ile tek bir dosyayı, Klasör Aç ile bir klasör içinde bulunan tüm müzik dosyalarını, Playlist Aç butonu ile de kayıtlı bir playlist’teki müzik dosyalarını açacağız:






private void btnOpenFile_Click(object sender, EventArgs e)
{
    openFileDialog1.ShowDialog();
    axWindowsMediaPlayer1.URL = openFileDialog1.FileName;
    //veya;
   
//WMPLib.IWMPMedia media = axWindowsMediaPlayer1.newMedia(openFileDialog1.FileName);
    //axWindowsMediaPlayer1.currentPlaylist.appendItem(media);
    //axWindowsMediaPlayer1.Ctlcontrols.play(); 
}
private
void btnOpenFolder_Click(object sender, EventArgs e)
{
    folderBrowserDialog1.ShowDialog();
    foreach
(string fileName in Directory.GetFiles(folderBrowserDialog1.SelectedPath))
    {
        IWMPMedia
media = axWindowsMediaPlayer1.newMedia(fileName);
        axWindowsMediaPlayer1.currentPlaylist.appendItem(media);
    }
    axWindowsMediaPlayer1.Ctlcontrols.play();
}
private void btnOpenPlayList_Click(object sender, EventArgs e)
{
    axWindowsMediaPlayer1.currentPlaylist = axWindowsMediaPlayer1.mediaCollection.getByName(“isilPlayList”);
    for
(int i = 0; i < axWindowsMediaPlayer1.currentPlaylist.count; i++)
    {
        listBox1.Items.Add(axWindowsMediaPlayer1.currentPlaylist.get_Item(i).name);
    }
}


Bir medya dosyası açmak için formdaki axWindowsMediaPlayer1 nesnesinin URL property’sine dosyanın adını yazmak yeterli olacaktır. Bir klasörde bulunan birden fazla dosyayı açmak için de IWMPMedia değişkeni tanımlanıp, axWindowsMediaPlayer1 nesnesinin newMedia fonksiyonu çağırılabilir. Ardından da playlist’e eklenip, play metodu çağırılarak dosyalar açılabilir. Bu yöntemi btnOpenFile butonunun click event’indeki comment out’lu kısımdaki gibi tek bir dosya açarken de kullanabilirsiniz. Kayıtlı bir playlist’i açmak içinse axWindowsMediaPlayer1 nesnesinin currentPlaylist property’sine yine aynı nesnenin mediaCollection property’sinin getByName metodunu çağırabilirsiniz. Aynı şekilde playlist yerine getByAlbum metodunu kullanarak albüm ismine göre, getByGenre metodu ile müzik türüne göre playlist oluşturabilirsiniz. btnOpenPlaylist butonunun click event’indeki for döngüsünde playlist içindeki şarkıların isimleri formdaki listbox’ın içine dolduruluyor. Playlist’teki şarkıların isimlerini axWindowsMediaPlayer1 nesnesinin currentPlaylist property’sinin get_Item fonksiyonuna şarkının indeksini parametre geçirerek alabiliriz. Bu fonksiyon bize IWMPMedia nesnesi döndürür. Bu nesnenin name property’si ise şarkının ismidir. Kodu çalıştırıp, bilgisayarımda kayıtlı olan isilPlayList’i açalım:



Form üzerindeki ikinci tab’da temel medya dosyası çalma, durdurma, ileri alma işlemlerini yapacağız:




private void btnStop_Click(object sender, EventArgs e)
{
    axWindowsMediaPlayer1.Ctlcontrols.stop();
}
private
void btnPlay_Click(object sender, EventArgs e)
{
    axWindowsMediaPlayer1.Ctlcontrols.play();
}
private
void btnPrevious_Click(object sender, EventArgs e)
{
    axWindowsMediaPlayer1.Ctlcontrols.previous();
}
private
void btnNext_Click(object sender, EventArgs e)
{
    axWindowsMediaPlayer1.Ctlcontrols.next();
}
private
void btnFastForward_Click(object sender, EventArgs e)
{
    axWindowsMediaPlayer1.Ctlcontrols.fastForward();
}
private
void btnReverse_Click(object sender, EventArgs e)
{
    axWindowsMediaPlayer1.Ctlcontrols.fastReverse();
}


Kodda da görüldüğü gibi çalma, durdurma, duraklatma, ileri-geri sarma, önceki-sonraki şarkıya geçme gibi işlemler axWindowsMediaPlayer nesnesinin Ctlcontrols property’sinin metodları ile yapılabilir:



Üçüncü tab’da dosya attribute’larını okuma ve readonly olmayanları değiştirme işlemlerini yapalım. Formda çalan şarkının bilgilerini getirecek olan bir buton, şarkının saniye cinsinden süresinin, dakika:saniye formatında süresinin ve dosya yolu bilgilerinin gösterildiği bir label ve tüm attribute’larının listelendiği bir listview kontrolü olsun. En altta ise seçtiğimiz bir attribute’ın yeni değerini girmemizi ve ayarlamamızı sağlayacak olan bir textbox ve buton olsun:



Media Bilgilerini Al butonunun kodu aşağıdaki gibidir:




private void btnGetMediaInfo_Click(object sender, EventArgs e)
{
    IWMPMedia
media = axWindowsMediaPlayer1.currentMedia;
    lblMediaInfo.Text = “Süre (Saniye): “ + media.duration.ToString() + “\r\nSüre: “ + media.durationString + “\r\nDosya Yolu: “ + media.sourceURL;
    for
(int i = 0; i < media.attributeCount; i++)
    {
        ListViewItem
lvi = new ListViewItem();
        ListViewItem
.ListViewSubItem lvi2 = new ListViewItem.ListViewSubItem();
        ListViewItem
.ListViewSubItem lvi3 = new ListViewItem.ListViewSubItem();
        lvi.Text = media.getAttributeName(i);
        listView1.Items.Add(lvi);
        lvi2.Text = media.getItemInfo(lvi.Text);
        lvi3.Text = media.isReadOnlyItem(lvi.Text).ToString();
        lvi.SubItems.Add(lvi2);
        lvi.SubItems.Add(lvi3);
    }
}


Kodda media adında bir IWMPMedia nesnesi tanımladıktan sonra lblMediaInfo label’ının text’ine duration, durationString ve sourceURL property’lerinde taşınan değerleri yazdırıyoruz. Ardından da media nesnesinin attribute’larını for döngüsü içinde listview’a ekliyoruz. Listview’ın Text property’sine attribute adını (getAttributeName), ilk subitem’ının text’ine bu attribute’ın değerini (getItemInfo), son subitem’ın text’ine de bu attribute’ın readonly olup olmadığı bilgisini yazdırıyoruz (isReadOnlyItem). Sırada Set butonunun kodu var:






private void btnSet_Click(object sender, EventArgs e)
{
    IWMPMedia
media = axWindowsMediaPlayer1.currentMedia;
    if
(!media.isReadOnlyItem(listView1.SelectedItems[0].Text))
    {
        media.setItemInfo(listView1.SelectedItems[0].Text, txtAttributeValue.Text);
        listView1.SelectedItems[0].SubItems[1].Text = media.getItemInfo(listView1.SelectedItems[0].Text);
    }
}


Set butonunda ise listview’da seçili item eğer readonly değilse, txtAttributeValue textbox’ında yazılı değeri setItemInfo ile ilgili attribute’a değer olarak atıyoruz. Ardından da listview’daki öğenin değerini yeniliyoruz.

Son tab’da ise media player’ı fullscreen gösterme, çalan şarkının kaçıncı dakikasında olduğunu alma ve ses ayarlama işlemlerini yapacağız:








private void btnFullScreen_Click(object sender, EventArgs e)
{
    axWindowsMediaPlayer1.fullScreen = true;
}
private
void btnPosition_Click(object sender, EventArgs e)
{
    MessageBox
.Show(axWindowsMediaPlayer1.Ctlcontrols.currentPositionString);
}
private
void btnVolume_Click(object sender, EventArgs e)
{
    axWindowsMediaPlayer1.settings.volume = Convert.ToInt32(txtVolume.Text);
}


Görüldüğü gibi son tab’da da istediğimiz işlemleri axWindowsMediaPlayer nesnesi ile çok basit bir şekilde gerçekleştirebiliyoruz.

Işıl Orhanel

Geliştirmiş olduğumuz uygulamalarda topladığımız veriler içerisinde koordinatlar var ise kullanıcıların bu koordinatların temsil ettiği alanı, noktayı veya doğruyu harita üzerinde görmek istemeleri kaçınılmazdır. Bunun yanısıra yazmış olduğumuz GPS takip benzeri uygulamalardan gelen koordinatlar eşliğinde bir aracın veya bir kişinin nerede olduğunun son kullanıcıya gösterilmesi gerekmektedir. İşte Virtual Earth, uygulamalarımızda bu tür işlemleri yapmamıza yardımıcı olan bir JavaScript bileşenidir. Tüm bu işlemleri bir harita üzerinde kullanabilmek için Windows Live Virtual Earth api’lerini uygulamalarımızda nasıl kullanabileceğimizi, 2 ve 3 boyutlu haritalar üzerinde nokta, doğru gibi işaretlerin nasıl gösterilebileceğini bu makalede inceleyeceğiz. Virtual Earth ile haritaları çizerken tek ihtiyaç duyacağımız şeyler ise javascript, html ve css olacak.

Virtual Earth ile yapabileceklerimizi bir projede adım adım inceleyelim.

Yeni bir web uygulaması oluşturalım ve İlk olarak Virtual Earth’ün varsayılan haritasını uygulamamızda gösterelim:




< html>
   <head>
      <title></title>
      <meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″>
      <script type=”text/javascript” src=”http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6″></script>
      <script type=”text/javascript”>
      var map = null;
      function GetMap()
      {
         map = new VEMap(‘myMap’);
         map.LoadMap();
      }  
      </script>
   </head>
   <body onload=”GetMap();”>
      <div id=’myMap’ style=”position:relative; width:600px; height:400px;”></div>
   </body>
</html>

Kodu yazıp uygulamayı çalıştırdığımızda Amerika haritasının görüntülendiğini görürüz. Haritanın sol üst kısmında ise zoom, navigasyon, haritayı 2 veya 3 boyutlu gösterme gibi işlemleri yapmamıza olanak sağlayan toolbar vardır.



Varsayılan haritayı yüklemek yerine LoadMap metodunu kullanarak kendi belirlediğimiz özelliklerde bir harita yükleyebiliriz. LoadMap metodunun 7 tane parametresi vardır ve tamamının kullanımı opsiyoneldir:





VEMap.LoadMap(VELatLong, zoom, style, fixed, mode, showSwitch, tileBuffer);

Bu parametreleri sırasıyla inceleyecek olursak;

VELatLong: Haritanın merkezinin hangi enlem ve boylamda olacağının belirlendiği parametredir.
zoom: Harita gösterilirken ne kadar zoom yapılarak gösterileceğinin belirlendiği parametredir. 1′den 19′a kadar değer alır.
Style: Haritanın gösterim stilinin belirlendiği parametredir. Bu stiller aşağıdaki gibidir:
    * Road – Yol haritası şeklinde gösterilir.
    * Shaded – Yine yol haritası şeklinde gölgelendirilmiş şekilde yeryüzü şekilleri de gösterilir.
    * Aerial – Havadan görünümü gösterilir.
    * Hybrid – Havadan görünümü ancak bu sefer yer isimlerinin ve diğer bilgilerin de çizilerek gösterildiği şekildir.
    * Oblique – dik açı olmayan şekilde gösterilir.
    * Birdseye – kuşbakışı görünüm şeklinde görüntülenir.
Fixed: Kullanıcının harita üzerinde değişiklik yapıp yapamayacağının belirlendiği parametredir. Boolean tipinde değer alır, default değeri false’dır.
mode: Haritanın kaç boyutlu yükleneceğinin belirlendiği parametredir. HArita iki boyutlu (VEMapMode.Mode2D) veya üç boyutlu (VEMapMode.Mode3D) yüklenebilir. Default değeri VEMapMode.Mode2D’dir.
showSwitch: Haritanın üzerinde bulunan kullanıcının 2 veya 3 boyutlu görüntüleme seçmesine olanak sağlayan ayarların görüntülenip görüntülenmeyeceğinin belirlendiği boolean tipinde parametredir. Default değeri true’dur.

Bu parametreleri inceledikten sonra şimdi Load metodu ile özelliklerini kendimiz belirlediğimiz bir harita oluşturalım.
Türkiye yol haritasını çizelim:
 




<html>
   <head>
      <title></title>
      <meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″>
      <script type=”text/javascript” src=”http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6″></script>
      <script type=”text/javascript”>
         var map = null;
         function GetMap()
         {
            map = new VEMap(‘myMap’);
            map.LoadMap(new VELatLong(39.28634694578469, 35.20161335951931), 6, ‘r’, false);
         }     
      </script>
   </head>
   <body onload=”GetMap();”>
      <div id=’myMap’ style=”position:relative; width:950px; height:500px;”></div>
   </body>
</html>



Kodu yazıp uygulamayı çalıştırdığımızda harita aşağıdaki gibi görüntülenecektir:



Haritayı 3 boyutlu olarak göstermek istersek LoadMap metodunda yazmamız gereken kod aşağıdaki gibi olacaktır:





….
map.LoadMap(new VELatLong(39.28634694578469, 35.20161335951931), 6, ‘h’, false, VEMapMode.Mode3D, true);
……

Şimdi sayfamıza ekleyeceğimiz kontrollerle harita üzerinde zoom yapma işlemlerini inceleyelim. Sayfada iki adet buton olsun ve bu butonlar yardımıyla zoom in ve zoom out işlemlerimizi yapalım. Ayrıca biraz önce bahsetmiş olduğum zoom parametresinin değerini de forma koyduğumuz bir textbox’a yazarak set edelim.
 




<html>
<
head>
    <title></title>
    <meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″>
    <script type=”text/javascript” src=”http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6″></script>
    <script type=”text/javascript”>
      var map = null;
      function GetMap()
      {
         map = new VEMap(‘myMap’);
         map.LoadMap(new VELatLong(39.28634694578469, 35.20161335951931), 6, ‘r’, false);
      }
      function ZoomIn()
      {
         map.ZoomIn();
      }
      function ZoomOut()
      {
         map.ZoomOut();
      }
      function ZoomYap()
      {
         var zoom = document.getElementById(‘zoomSeviyesi’).value;
         map.SetZoomLevel(zoom);
      }
      </script>
</head>
<
body onload=”GetMap();”>
    <div id=’myMap’ style=”position: relative; width: 400px; height: 400px;“>
    </div>
    <div>
        <input type=’button’ value=’+’ onclick=’ZoomIn();’ />
        <input type=’button’ value=’-’ onclick=’ZoomOut();’ />
    </div>
    <div>
        Zoom:<input id=’zoomSeviyesi’ type=’text’ style=’width: 15px; value=’10′ />
        <input type=’button’ value=’Ayarla’ onclick=’ZoomYap();’ />
    </div>
</body>
</
html>


Yazmış olduğumuz ZoomIn ve ZoomOut fonksiyonları map kontrolünün ZoomIn ve ZoomOut metodlarını çağırıyorlar. Yine map kontrolünün SetZoomLevel metodunu kullanarak da haritanın ne kadar yakından görüntüleneceğini set edebiliyoruz. Uygulamayı çalıştırdığımızda sayfamız aşağıdaki gibi görüntülenir. +, – butonlarına tıklayarak zoom yapabilir, textbox’a 1′den 19′a kadar değer girip zoom seviyesini belirleyebiliriz:



Şimdi haritanın sol üst köşesinde yeralan toolbar üzerinde yapabileceğimiz değişiklikleri inceleyelim. Bu toolbar, map kontrolünün SetDashboardSize metodu kullanılarak isteğe göre 3 farklı şekilde gösterilebilir (VEDashboardSize.Normal, VEDashboardSize.Small, VEDashboardSize.Tiny). Şu ana kadarki resimlerde görülen şekli default olarak yüklenen Normal’dır. Small olarak ayarlandığında zoom in ve zoom out butonları ile Road, Aerial ve Hybrid butonları ekranda görüntülenir. Tiny olarak ayarlandığında ise sadece zoom in ve zoom out butonları görüntülenir. Kullanımı ise aşağıdaki kodda görüldüğü gibidir:
 






<html>
   <head>
      <title></title>
      <meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″>
      <script type=”text/javascript” src=”http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6″></script>
      <script type=”text/javascript”>
         var map = null;
         function GetMap()
         {
            map = new VEMap(‘myMap’);
            map.SetDashboardSize(VEDashboardSize.Tiny);
            map.LoadMap(new VELatLong(39.28634694578469, 35.20161335951931), 5, ‘h’, false);
         }    
      </script>
 </head>
   <body onload=”GetMap();”>
      <div id=’myMap’ style=”position:relative; width:400px; height:400px;“></div>
      </div>
   </body>
</html>

Tiny modda yüklenecek olan harita aşağıdaki gibi görüntülenir:



Bu toolbar’ı hiç göstermek istemezseniz map kontrolünün HideDashboard metodunu kullanabilirsiniz. Gizlenmiş olan toolbar’ı tekrar visible yapmak için de ShowDashboard metodunu kullanabilirsiniz.

Şimdi haritada belirli yerleri raptiye ile nasıl gösterebileceğimizi inceleyelim. Aşağıda yazdığım kod, haritanın altında bulunan üç şehrimizin linkine tıklandığında ilgili şehrin üzerine bir raptiye yerleştiriyor ve bize üzerine gelindiğinde hangi şehir olduğu ile ilgili bilgi veriyor:
 






   <head>
      <title></title>
      <meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″>
      <script type=”text/javascript” src=”http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6″></script>
      <script type=”text/javascript”>
      var map = null;
      var pinid = 1;

      function GetMap()
      {
         map = new VEMap(‘myMap’);
         map.LoadMap(new VELatLong(39.28634694578469, 35.20161335951931), 5, ‘r’, false);
      }
      function Ankara()
      {
        return [new VELatLong(39.56, 32.52), 'Ankara'];
      }
      function Istanbul()
      {
        return [new VELatLong(41.01, 28.58), 'İstanbul'];
      }
      function Izmir()
      {
        return [new VELatLong(38.25, 27.09), 'İzmir'];
      }
      function AddPushpin(sehir)
      {
          var shape = new VEShape(VEShapeType.Pushpin, sehir[0]);
          shape.SetTitle(‘Raptiye’);
          shape.SetDescription(pinid + ‘: Burası ‘ + sehir[1]);
          pinid++;
          map.AddShape(shape);
      }
      </script>
   </head>
   <body onload=”GetMap();”>
      <div id=’myMap’ style=”position:relative; width:400px; height:400px;“></div>
      <div>Aşağıdaki şehirleri harita üzerinde işaretlemek için tıklayınız.</div>
      <div><a href=’#’ onclick=’AddPushpin(Ankara());’>Ankara</a></div>
      <div><a href=’#’ onclick=’AddPushpin(Istanbul());’>İstanbul</a></div>
      <div><a href=’#’ onclick=’AddPushpin(Izmir());’>İzmir</a></div>
   </body>
</html>

Kodda ilk olarak üç şehir için enlem ve boylam bilgilerinin tutulduğu
VELatLong nesnesini ve şehir ismini döndüren fonksiyonları yazdık. Ardından AddPushpin adında bir fonksiyon oluşturduk. Bu fonksiyon parametre olarak biraz önce yazdığımız fonksiyonlardan dönen değerleri aldı. Fonksiyonda ilk olarak haritaya eklemek için yeni bir VEShape nesnesi tanımladık. Tanımlarken VEShapeType’ını Pushpin yani raptiye olarak belirledik, raptiyenin yerleştirileceği enlem ve boylam bilgisini de sehir parametresinin ilk değerinden aldık. Yerleştireceğimiz raptiyenin üzerine gelindiğinde açılacak olan baloncuğun başlığını Raptiye olarak set ettik. SetDescription metodunu kullanarak baloncuk içinde yer alacak olan açıklamayı belirledik. Ardından da haritaya AddShape metodunu kullanarak raptiyeyi ekledik. Son olarak da forma eklemiş olduğumuz linklerin onclick event’lerinde AddPushpin fonksiyonlarını çağırdık. Uygulamayı çalıştırdığımızda, linklere tıkladığımızda ve raptiyelerden birinin üstüne geldiğimizde harita aşağıdaki gibi görünecektir:



Şimdi de bu üç şehir arasını çizgiyle birleştirelim yani haritamıza polyline ekleyelim:
 






<html>
   <head>
      <title></title>
      <meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″>
      <script type=”text/javascript” src=”http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6″></script>
      <script type=”text/javascript”>


      var map = null;
      function GetMap()
      {
        map = new VEMap(‘myMap’);
        map.LoadMap(new VELatLong(39.28634694578469, 35.20161335951931), 5, ‘a’, false);
      }
      function
Ankara()
      {
        return
new VELatLong(39.56, 32.52);
      }
      function
Istanbul()
      {
        return new VELatLong(41.01, 28.58);
      }
      function
Izmir()
      {
        return
new VELatLong(38.25, 27.09);
      }
      function
AddPolyline()
      {
        var
shape = new VEShape(VEShapeType.Polyline, [Ankara(), Istanbul(), Izmir()]);
        shape.SetTitle(‘Polyline ekledik.’);
        shape.SetDescription(‘Ankara – İstanbul – İzmir’);
        map.AddShape(shape);
        map.SetMapView([Ankara(), Istanbul(), Izmir()]);
      }
    </
script>
    </
head>
<
body onload=”GetMap();”>
    <
div id=’myMap’ style=”position:relative; width:400px; height:400px;“></div>
    <
div><a href=’#’ onclick=’AddPolyline();’>Polyline Ekle</a></div>
</
body>
</html>



Kodda biraz önce yaptığımız gibi Ankara, İstanbul, İzmir fonksiyonlarımızı tekrar yazdık. Ardından da AddPolyline adında bir fonksiyon oluşturduk. Bu fonksiyonda shape adında yeni bir
VEShape nesnesi tanımladık ve VEShapeType’ını Polyline olarak set ettik, ardından da ikinci parametre olarak Ankara, İstanbul, İzmir fonksiyonlarından dönen enlem ve boylam bilgilerini sırayla yazdık. SetTitle metoduyla başlığını, SetDescription metoduyla açıklamasını belirledik ve haritamıza ekledik. Bu noktada makalede daha önce bahsetmediğim SetMapView metodundan bahsedeceğim. Kodda da görüldüğü gibi map nesnesinin SetMapView metodu, VELatLong nesnesinden oluşan array’i parametre olarak alıyor. Bunun dışında VEPolyline nesnesinden oluşan array’i veya VELatLongRectangle nesnesini de parametre olarak alabilir. SetMapView metodu aldığı bu parametrelerin barındırdığı değerlere göre haritayı belirlenmiş olan büyüklüğünün içine sığdırır. Yani bir bakıma aldığı koordinatların tamamı haritada görünecek şekilde haritanın görünümünü değiştirir. Biz de örnekte Ankara, İstanbul ve İzmir’in enlem boylam bilgilerini SetMapView metoduna parametre olarak gönderdik. Ardından da haritanın altında bulunan linkten de AddPolyline fonksiyonunu çağırdık. Kodu çalıştırdığımızda ve tıkladığımızda haritamız aşağıdaki gibi görüntülenecektir:



Bu makalemde Virtual Earth ile yapabileceğiniz temel bazı işlemlerden bahsettim. Siz de web uygulamalarınızda haritalama, lokasyon gösterimi, lokasyon arama gibi işlemleri Virtual Earth ile başarılı bir şekilde gerçekleştirebilirsiniz. Daha ayrıntılı bilgiye http://dev.live.com/virtualearth/sdk adresindeki interaktif Virtual Earth SDK’sından ulaşabilirsiniz.

Merhabalar,

Artık günlük hayatta kullandığımız son model pocket pc’lerin çoğunda tümleşik GPS bulunmaktadır. Bu GPS’ler son kullanıcılar tarafından sadece bir yol bulma uygulaması olarak anlaşılsa da yazılımcıların bu konuya bakış açısı farklıdır. Donanımsal olarak bulunan bu özellik koordinat belirlemenin mümkün olması ve internet tabanlı harita sunucularının SDK’larını dışarı açmasıyla yazılımcıların ufkunu açmıştır. Birçok yazılımcı araç takip, personel takip uygulamasından, satışta olan emlakların gösterilmesine kadar çok geniş bir yelpazede uygulamalar yazmayı düşünmüştür. İleride GPS cihazlarının bir web servis aracılığı ile yol yoğunluk durumuna göre karar verebilir duruma gelmesi bizi farklı bir boyuta taşıyacaktır. Şimdi bu özelliği mobil uygulamalarımızda nasıl kullanabileceğimizi bir örnek ile inceleyelim.

Örnek uygulamamız, kullanıcının mobil cihazından aldığımız yer bilgilerini harita üzerinde gösterdiğimiz bir uygulama olacak. Mobil cihazda çalışan uygulama server’a kullanıcının GPS ile aldığı yer bilgilerini gönderecek. Ardından da server’da tuttuğumuz enlem boylam bilgilerinden kullanıcının bulunduğu yerleri Virtual Earth kullanarak harita üzerinde görüntüleyeceğiz. Virtual Earth’ün uygulamalarımızda kullanımı, detaylı bilgi ve bu makalede kullanacağım Virtual Earth ile yer bilgilerinin haritada nasıl gösterebileceğinize yazmış olduğum başka bir makaleden ulaşabilirsiniz.

İlk olarak GPS ile edinilmiş yer bilgilerinin tutulacağı veritabanını basitçe tasarlayalım. Veritabanında GPS verisi takip edilecek kullanıcı bilgilerinin tutulduğu bir tablo ile GPS verilerinin tutulduğu tabloları oluşturmak yeterli olacaktır. Tabloları aşağıdaki gibi tasarlayalım:






Kullanıcı bilgilerinin tutulduğu tabloyu GPSUsers, GPS’den elde edilen verilerin tutulduğu tabloyu ise GPSInformation olarak adlandıralım. Şimdi sırada kullanıcının yer bilgilerini server’a gönderecek olan uygulama var. Yeni bir solution oluşturup, device application ekleyelim. Bu uygulamada kullanıcı seçimi yapılacak bir form olsun, diğer bir forma da bir timer ekleyelim ve kullanıcı seçimi yapıldıktan sonra GPS Başlat adında bir menu item’a tıklandığında 10 saniyede bir GPS ile elde edilen enlem boylam bilgileri server’a gönderilsin. Formları aşağıdaki gibi tasarlayalım:





İlk olarak kullanıcı seçme formunun kodunu yazalım:




private void frmChooseUser_Load(object sender, EventArgs e)
{

    GPSUser
insGPSUser = new GPSUser();
    DataTable
insDataTableGPSUser = insGPSUser.SelectGPSUsers();
    cmbUser.DataSource = insDataTableGPSUser;
    cmbUser.ValueMember = “GPSUserId”;
    cmbUser.DisplayMember = “GPSUserName”;
}

private
void btnOK_Click(object sender, EventArgs e)
{

    ApplicationVariables
.UserId = Convert.ToInt32(cmbUser.SelectedValue);
    this
.Close();
}

Kullanıcı seçme ekranının Load’ında GPSUser nesnesinin SelectGPSUsers fonksiyonunu kullanarak veritabanındaki GPSUsers tablosundaki kullanıcıları kullanıcı combobox’ına dolduruyoruz. OK butonuna tıklandığında ise ApplicationVariables adındaki static class’ta tuttuğumuz UserId değişkenine combobox’ta seçili olan değeri atıyoruz. UserId değişkenini anaformda GPS verilerini veritabanına insert ederken kullanacağız. ApplicationVariables class’ı aşağıdaki gibidir:




public static class ApplicationVariables
{
    private
static int _UserId;
    public
static int UserId
    {
        get
{ return ApplicationVariables._UserId; }
        set
{ ApplicationVariables._UserId = value; }
    }
}

Sırada anaformumuz var. İlk olarak Kullanıcı Seç menuitem’a tıklandığında Kullanıcı Seçme formumuzu açtıracak kodu yazalım:




private void menuItem3_Click(object sender, EventArgs e)
{
    frmChooseUser
insFrmChooseUser = new frmChooseUser();
    insFrmChooseUser.ShowDialog();
}

Şimdi sıra GPS işlemlerini yaptırmaya geldi. GPS işlemleri, GPS API’si kullanılarak yapılabiliyor. http://www.treaple.com/ArticlesDetails.aspx?id=62 adresinde GPS API’si hakkında detaylı bilgiye erişebilirsiniz. Ben de bu adresteki örnek kodu indirdim ve class’ları projeye ekledim. Bu class’lar içindeki içindeki GPS class’ı ile kalan işlemlerimize devam edebiliriz. Formumuzda insGPS adında class level bir GPS tipinde değişken tanımlayalım ve Load’da GPS nesnesinin Open metodunu kullanarak cihazımızdaki GPS’i etkinleştirelim:




GPS insGPS = new GPS();
private
void frmGPS_Load(object sender, EventArgs e)
{
    insGPS.Open();
}

Ardından formdaki timer’ın Tick event’ine aşağıdaki kodu yazalım:




private void timer1_Tick(object sender, EventArgs e)
{
   
try
   
{
        if
(insGPS.GetDeviceState().DeviceState == GpsServiceState.On)
        {
            GPSInformation
insGPSInformation = new GPSInformation();
            GPSPosition
insGpsPosition = insGPS.GetPosition();
            insGPSInformation.GPSUserId = ApplicationVariables.UserId;
            insGPSInformation.Latitude = Convert.ToDecimal(insGpsPosition.Latitude);
            insGPSInformation.Longitude = Convert.ToDecimal(insGpsPosition.Longitude);
            insGPSInformation.InsertGPSInformation(insGPSInformation);
        }
    }
    catch
(Exception)
    {
        throw
;
    }
}

Kodda, insGps nesnesinin GetPosition() fonksiyonunu kullanarak insGpsPosition nesnesini oluşturuyoruz. insGpsPosition nesnesinin Latitude ve Longitude property’lerinden kullanıcının enlem ve boylam bilgilerini alıyoruz. Ardından oluşturduğumuz GPSInformation nesnesinin property’lerine değerleri atayarak, veritabanında GPSInformation tablosuna insert ediyoruz.

Son olarak menüdeki GPS Başlat menuitem’ına tıklanınca, timer’ı enabled edelim:




private void menuItem2_Click(object sender, EventArgs e)
{
    if
(ApplicationVariables.UserId == 0)
    {
        MessageBox
.Show(“Lütfen kullanıcı seçiniz.”);
        return
;
    }
    timer1.Enabled = true;
}

Böylece bu noktaya kadar kullanıcının yer bilgilerini alacağımız uygulamamızı yazmayı tamamlamış oluyoruz. Şimdi sırada topladığımız bu bilgileri Virtual Earth kullanarak haritada gösterme işlemine geldi. Solution’a yeni bir web uygulaması ekleyelim. Uygulamada kullanıcının GPS bilgilerini almak istediği iki tarih arasını seçebileceği bir calendar kontrolleri, kişileri seçebileceği bir checkboxlist, Virtual Earth haritası ve bir buton olsun. Ardından da butonun click event’ine aşağıdaki kodu yazalım:




protected void btnGPS_Click(object sender, EventArgs e)
{
    GPSInformation
insEntGPSInformation = new GPSInformation();
    DateTime
dtBaslangic = new DateTime(calBaslangic.SelectedDateTime.Value.Year, calBaslangic.SelectedDateTime.Value.Month, calBaslangic.SelectedDateTime.Value.Day, Convert.ToInt32(txtBaslangicSaat.Text), Convert.ToInt32          (txtBaslangicDakika.Text), 0);
    DateTime
dtBitis = new DateTime(calBitis.SelectedDateTime.Value.Year, calBitis.SelectedDateTime.Value.Month, calBitis.SelectedDateTime.Value.Day, Convert.ToInt32(txtBitisSaat.Text), Convert.ToInt32(txtBitisDakika.Text), 0);
    List
<int> ids = new List<int>();
    for
(int i = 0; i < cblKisiler.Items.Count; i++)
    {
        if
(cblKisiler.Items[i].Selected)
        {
            ids.Add(Convert.ToInt32(cblKisiler.Items[i].Value));
        }
    }
    string
addpolyline = “function AddPolyline(){ “;
    foreach
(int id in ids)
    {
        DataTable
insDataTableGPSInformation = insEntGPSInformation.SelectGPSInformationByGPSUserIdAndGPSDataTime(id, dtBaslangic, dtBitis);
        string
script = “function GetPoints” + id.ToString() + “(){return new Array(“;
        foreach
(DataRow dr in insDataTableGPSInformation.Rows)
        {
            script = script + “new VELatLong(“ + dr["Latitude"].ToString().Replace(“,”, “.”) + “,” + dr["Longitude"].ToString().Replace(“,”, “.”) + “),”;
        }
        if
(script.EndsWith(“,”))
        {
            script = script.Substring(0, script.Length – 1);
        }
        script = script + “);}”;
        ScriptManager
.RegisterClientScriptBlock(this, this.GetType(), “koordinat” + id.ToString(), script, true);
        Color
c = GetColor(id);
        addpolyline = addpolyline + “var points” + id.ToString() + ” = GetPoints” + id.ToString()
            + “(); var shape” + id.ToString() + ” = new VEShape(VEShapeType.Polyline, “ +
            “points”
+ id.ToString() + “);” +
            ” shape”
+ id.ToString() + “.SetLineColor(new VEColor(“ + c.R.ToString() + ” , “ + c.G.ToString() + “,” + c.B.ToString() + “, 1.0)); “ +
            ” shape”
+ id.ToString() + “.SetFillColor(new VEColor(“ + c.R.ToString() + ” , “ + c.G.ToString() + “, “ + c.B.ToString() + “, 0.5)); “
           
+ ” map.AddShape(shape” + id.ToString() + “); shape” + id.ToString() + “.HideIcon(); map.SetMapView(points” + id.ToString() + “); “;
    }
    addpolyline = addpolyline + “}”;
    ScriptManager
.RegisterClientScriptBlock(this, this.GetType(), “rota”, addpolyline, true);
    ScriptManager
.RegisterClientScriptBlock(this, this.GetType(), “harita”, “GetMap();”, true);
}

Kodda veritabanında GPSInformation tablosundan GPSUserId ve GPSDataTime alanlarına göre GPS yer bilgilerini çekiyoruz ve bu çektiğimiz enlem boylam bilgileri ile Virtual Earth map kontrolünde gösterilmek üzere javascript kodunu oluşturuyoruz. Bu arada birden fazla kullanıcı seçilmişse çizilen çizgilerde renkler farklı olsun diye GetColor fonksiyonunu kullanarak SetLineColor ve SetFillColor metodlarıyla her kişiye ait olan çizgiyi farklı renkte çiziyoruz. Dizayn kısmını ve sayfadaki kodun kalanını örnek uygulamayı indirip inceleyebilirsiniz.

Web uygulamasını çalıştırıp veritabanında kayıtlı tarihleri ve kullanıcıları seçerek Haritada Göster butonuna tıkladığımızda aşağıdaki gibi şekilde haritamız çizilecektir:




Haritada bir kullanıcı turuncu, diğeri ise lacivert renkte gösterilmiştir. Hangi kullanıcının hangi lokasyondan ne zaman geçtiği gibi bilgileri de uygulamaya ekleyebilir, haritaya çizgiden farklı olarak poligon gibi şekiller çizebilirsiniz. Görüldüğü gibi GPS ve Virtual Earth birlikte kullanıldığında ortaya oluşturması ilginç ve bir o kadar da zevkli uygulamalar çıkabilir.

Umarım makalem sizin için faydalı olmuştur.

ClickOnce, geliştirilen Windows uygulamalarının dağıtım ve güncellemelerinin daha kolay yapılabilmesine olanak sağlayan Visual Studio 2005 ile gelen teknolojidir. Click Once ile ilgili daha detaylı bilgiye önceden yazmış olduğum makale serisinden ulaşabilirsiniz. Bu makalemde ise geliştirdiğimiz mobil uygulamaların dağıtım, kurulum ve güncellemeleri için Click Once teknolojisine benzer yapıda bir örnek uygulama yapacağız. Bu örnek uygulamamızın amacı yazmış olduğumuz bir mobil uygulamanın en son versiyonunun mobil cihazda mevcut olup olmadığının kontrolünü yapmak, eğer yeni bir versiyonu bulunuyorsa kurulum dosyasının mobil cihaza indirilmesi ve kurulumu işlemlerinin yapılmasını sağlamak olacak. Böylelikle makalenin sonunda, Windows uygulamaları için kullanabildiğimiz Click Once teknolojisine benzer yapıda bir örnekte, bütün özellikleriyle olmasa bile mobil uygulamalar için de nasıl oluşturabileceğimize dair bilgi sahibi olacağız.
İlk olarak uygulamayı geliştirirken izleyeceğimiz adımları özetle geçmek faydalı olacaktır:
* Versiyon bilgisi kontrol edilecek örnek bir mobil uygulama oluşturmak
* Örnek uygulamanın kurulum dosyasını oluşturmak
* Uygulamanın yayınlanmış olan tüm versiyon bilgilerinin tutulduğu bir veritabanı oluşturmak
* Oluşturulmuş olan veritabanından en son versiyon bilgisini çekecek web servisi yazmak
* Web servisten aldığı versiyon bilgisine göre yeni bir versiyon bulunmuşsa uygulamanın yeni versiyonunun kurulum dosyasının indirilip kurulması veya yeni versiyon bulunmaması durumunda uygulamanın mevcut halinin çalıştırılması işlemlerini yapacak uygulamayı yazmak
İlk olarak yeni bir solution oluşturalım ve mobil cihazımıza kuracağımız ve versiyon kontrolünü yapacağımız basit bir örnek uygulama için yeni bir proje ekleyelim:
 

Uygulamada bir buton olsun ve tıklandığında Hello World messagebox’da çıksın.






private void btnHello_Click(object sender, EventArgs e)
{
    MessageBox
.Show(“Hello World”);
}
privatevoid
frmHelloWorld_Closed(object sender, EventArgs e)
{
    Application
.Exit();
}

Şimdi örnek uygulama için kurulum dosyalarını oluşturacak olan bir setup projesini solution’a ekleyelim:



Ardından setup projesine sağ tıklayarak açılan menüden Add -> Project Output’a tıklayarak MySampleApplication uygulamasının setup dosyasının oluşturulması için açılan ekranda PrimaryOutput’u seçelim:



Artık örnek bir uygulamamız ve uygulamamızın mobil cihaza kurulumunu yapabilmemiz için bize gerekli setup dosyasını verecek olan setup projemizi oluşturmuş bulunmaktayız. Çok basit olan bu adımları geçtikten sonra sıra uygulamanın versiyon bilgilerinin tutulacağı veritabanının tasarlanmasına geldi.

Veritabanının adını MobileClickOnceDB, tablonun adını da ApplicationVersions olarak belirleyelim. Veritabanında versiyon bilgilerinin tutulacağı tabloda VersionId, VersionNumber, VersionDate ve VersionPath alanları olsun. VersionNumber uygulamanın versiyon numarasının, VersionDate ise ilgili versiyonun yayınlanma tarihi bilgisinin tutulacağı alanlar olacaktır. VersionPath alanında ise uygulamanın cab dosyasının indirileceği adres bilgisini saklayacağız. Tablonun dizaynı tamamlandığında aşağıdaki gibi olacaktır:



Şimdi veritabanında kullanacağımız tek stored procedure’i yazalım. SP, ApplicationVersions tablosundanVersionDate ve VersionNumber alanlarına göre en güncel olan versiyon bilgilerini getirecek:




CREATE PROC [dbo].[SelectLatestApplicationVersion]
AS
SELECT TOP 1 * FROM ApplicationVersions ORDER BY VersionDate DESC, VersionNumber DESC



Şimdi sırada uygulamanın en son versiyon bilgisini çekecek olan web servisi yazmaya geldi. Solution’a ApplicationVersionUpdateCheckerWebService adında yeni bir web servis uygulaması ekleyelim. Ardından da ApplicationVersionUpdateChecker adında bir web servis ekleyelim. Web servis’e ApplicationVersion adında yeni bir class ekleyelim ve biraz önce yazdığımız SelectLatestApplicationVersion SP’sini çağıralım:
 




public ApplicationVersion SelectLatestApplicationVersion()
{
    DataTable
insDataTable = new DataTable();
    ApplicationVersion
av = new ApplicationVersion();
    SqlConnection
con = new SqlConnection(ConnectionString);
    SqlCommand
com = new SqlCommand(“SelectLatestApplicationVersion”, con);
    com.CommandType = CommandType.StoredProcedure;
    SqlDataAdapter
da = new SqlDataAdapter(com);
    da.Fill(insDataTable);
   
    av._VersionDate = Convert.ToDateTime(insDataTable.Rows[0]["VersionDate"]);
    av._VersionId = Convert.ToInt32(insDataTable.Rows[0]["VersionId"]);
    av._VersionNumber = insDataTable.Rows[0]["VersionNumber"].ToString();
    av._VersionPath = insDataTable.Rows[0]["VersionPath"].ToString();


     return av;
}


Şimdi sıra web servisten bu fonksiyonu çağırmaya geldi. asmx dosyasına da aşağıdaki kodu yazarak web servisi oluşturma işlemini tamamlayalım:




[WebMethod]
public
ApplicationVersion SelectLatestApplicationVersion()
{
    ApplicationVersion
av = new ApplicationVersion();
    return
av.SelectLatestApplicationVersion();
}

Son adımda uygulamanın versiyon bilgisini kontrol edecek olan uygulamayı yazacağız. Kısaca özetlemek gerekirse bu uygulama, MySampleApplication adındaki asıl uygulamamız yerine kullanıcı tarafından çalıştırılacak, server’a bağlanıp versiyon bilgisini alacak ve yeni versiyon olup/olmaması durumuna göre gerekli işlemleri yürütecek. İlk olarak solution’a yeni bir uygulama ekleyelim ve formu aşağıdaki gibi tasarlayalım:



Formda eğer uygulamanın yeni bir versiyonu mevcutsa kullanıcının tıklayacağı Güncellemeyi Yap ve Güncellemeyi Yapma butonları bulunuyor. En altta da dosya indirilmesi işlemleri sırasında gelinen aşamayı göstermesi açısından bir progressbar var.
İlk olarak biraz önce yazdığımız web servisi referans ekleyelim. Class level bir ApplicationVersion nesnesi tanımlayalım (ac) ve ardından da formun load’ına aşağıdaki kodu yazalım:




private void frmUpdater_Load(object sender, EventArgs e)
{
   
ApplicationVersionUpdateChecker.ApplicationVersionUpdateChecker uc = new MyUpdateChecker.ApplicationVersionUpdateChecker. ApplicationVersionUpdateChecker();
   
ac = new ApplicationVersion();
    string
version = “”;
    if
(Registry.LocalMachine.GetValue(“MyAppVersionNumber”) != null)
    {
        version = Registry.LocalMachine.GetValue(“MyAppVersionNumber”).ToString();
    }
    ac = uc.SelectLatestApplicationVersion();
    if
(ac.VersionNumber != version)
    {
        MessageBox
.Show(“Yeni versiyon bulundu.”);
    }
   
else
   
{
        if
(File.Exists(
@”\Program Files\MySampleApplicationSetup\MySampleApplication.exe”))
        {
            Process
.Start(
@”\Program Files\MySampleApplicationSetup\MySampleApplication.exe”, “”);
            Application
.Exit();
        }
       
else
       
{
            MessageBox
.Show(“Uygulama kurulu değil. Uygulamayı kurunuz.”);
            return
;
        }
    }
}

Kullanıcı update butonuna bastığında Registry’de MyAppVersionNumber adında uygulamanın kurulu versiyon bilgisini tutacağız. Formun load’ında da bu değer eğer mobil cihazda mevcutsa string tipinde tanımladığımız version değişkenine o değeri atıyoruz. Ardından da uygulamanın en son versiyon bilgisini web servisiyle alıyoruz ve tanımlamış olduğumuz ApplicationVersion (ac) nesnesine atıyoruz. ApplicationVersion nesnesinin VersionNumber property’sinde taşınan değer version değişkeni ile aynı değilse kullanıcı yeni versiyon bulunduğuna dair bilgilendiriliyor. Eğer yeni versiyon yoksa da uygulamayı çalıştırıp, updater uygulamasını sonlandırıyoruz.

Uygulamanın yeni bir versiyonu bulunuyorsa ve kullanıcı Güncelle butonuna basmışsa çalışacak kodu yazalım:




private HttpWebRequest insRequest;
private
HttpWebResponse insResponse = null;

private
void btnUpdate_Click(object sender, EventArgs e)
{
    Cursor
.Current = Cursors.WaitCursor;
    insRequest = (HttpWebRequest)HttpWebRequest.Create(ac.VersionPath);
    insRequest.Timeout = 10000;
    insRequest.AllowWriteStreamBuffering = false;
    insResponse = (HttpWebResponse)insRequest.GetResponse();
    int
totalCount = (int)insResponse.ContentLength;
    Stream
insStream = insResponse.GetResponseStream();
    FileStream
insFileStream = new FileStream(GetCurrentDirectory() + @”\setup.cab”, FileMode.Create);
    byte
[] buffer = new byte[256];
    int
count = insStream.Read(buffer, 0, buffer.Length);
    pBar.Maximum = totalCount;
    totalCount = 0;
    while
(count > 0)
    {
        if
(totalCount – count < 0)
        {
            pBar.Value = 0;
        }
       
else
       
{
            pBar.Value = totalCount – count;
        }
        Application
.DoEvents();
        insFileStream.Write(buffer, 0, count);
        count = insStream.Read(buffer, 0, buffer.Length);
        totalCount += count;
    }
    insFileStream.Close();
    insStream.Close();
    insResponse.Close();
    if
(ac.VersionNumber == “” || ac.VersionNumber == null)
    {
        ac.VersionNumber = “1.0.0.0″;
    }
    Registry
.LocalMachine.SetValue(“MyAppVersionNumber”, ac.VersionNumber);
    Cursor
.Current = Cursors.Default;
    string
cabname = GetCurrentDirectory() + @”\setup.cab”;
    Process
p = Process.Start(cabname, “”);
    int
processId = p.Id;
    while
(!Process.GetProcessById(processId).HasExited)
    {
        Thread
.Sleep(1000);
    }
    Process
.Start(@”\Program Files\MySampleApplicationSetup\MySampleApplication.exe”, “”);
    Close();
}
private
string GetCurrentDirectory()
{
    return
Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName);
}

Update butonunun click event’inde ilk olarak uygulamanın son versiyonu ApplicationVersion nesnesinin VersionPath property’sinde tutulan adresten HttpWebRequest ve HttpWebResponse kullanılarak cihaza indiriliyor. Bu arada da form üstündeki progressbar da indirilen dosya boyutuna göre ilerliyor. Dosya indirildikten sonra Registry’e uygulamanın son versiyon numarası
MyAppVersionNumber olarak yazılıyor. Ardından indirilmiş olan cab dosyası çalıştırılıyor, cihaza kurulum yapıldıktan sonra da uygulama çalıştırılıp, updater uygulaması kapatılıyor.

Son olarak kullanıcı uygulamayı güncelleme butonuna bastığında çalışacak olan kodu yazalım:





private void btnDoNotUpdate_Click(object sender, EventArgs e)
{
    if
(File.Exists(@”\Program Files\MySampleApplicationSetup\MySampleApplication.exe”))
    {
        Process
.Start(@”\Program Files\MySampleApplicationSetup\MySampleApplication.exe”, “”);
        Application
.Exit();
    }
   
else
   
{
        MessageBox
.Show(“Uygulama kurulu değil. Uygulamayı kurunuz.”);
        return
;
    }
}

Kodda da görüldüğü gibi uygulama cihazda mevcutsa direkt olarak çalıştırılıyor ve updater uygulaması kapatılıyor.

Güncelleme iptal butonunun kodunu yazdıktan sonra uygulamanın kodlama kısmı da tamamlanmış oldu. Veritabanındaki ApplicationVersions tablosuna uygulamamızın versiyon bilgisiyle ilgili aşağıdaki gibi bir kayıt ekleyelim:


Updater uygulamasını başlattığımızda bize yeni versiyon bulundu messagebox’ı gösterilecek:


 



OK’e bastığımızda uygulama ekranı açılacaktır:



Güncellemeyi Yap butonuna bastığımızda uygulama kurulacaktır:



OK’e bastığımızda ise updater uygulaması kapanacak, indirip kurmuş olduğumuz uygulama çalışacaktır:



Bu makalemde mobil uygulamalar için ClickOnce teknolojisinin mantığına benzer yapıda bir uygulamayı nasıl yazabileceğimizi anlattım. Böyle bir uygulamaya yazdığınız mobil uygulamalarda ihtiyaç duyduğunuzda validasyon ve hata yakalama işlemlerinin de koda dahil edilerek kullanabilirsiniz.

Umarım faydalı olmuştur.

Işıl Orhanel