Visual Studio 2008 etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
Visual Studio 2008 etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

Pazartesi, Mart 22, 2010

Visual Studio – Açık Olan Dökümanları Kapatmak

Merhabalar,

Visual Studio ile uygulama geliştirirken en sık yapılan işlemlerden biri de açık olan kod dosyalarını kapatmaktır. Bu işlem Visual Studio 2010 öncesinde biraz daha uğraştırırken 2010 sürümüyle birlikte biraz daha kolaylaşmıştır.

image_thumb_1  Visual Studio 2010 öncesi…

image_thumb Visual Studio 2010…

Yukarıdaki görüntülerden de göreceğiniz üzere VS2010 ile açık olan tabları kapatmak biraz daha kolay olmakta birlikte hepsini kapatmak istediğimizde ya da sadece biri kalsın diğerlerini kapatalım dediğimizde uğraştırıcı bir işlem olmaktadır. Ayrıca her kapatma işlemi için tek tek bu kapatma yerine gelmek oldukça yorucu bir işlem olacaktır. (Bu işlemi günde ortalama 100 seferden fazla yapıyoruzdur :) )

O zaman bu dökümanları nasıl kapatabileceğimize ilişkin imkanlarımızı inceleyelim.

1.) Windows->Close All Document

Bu seçenek yardımı ile açık olan bütün dökümanları yani kod dosyalarını iki tıklama ile kapabilme imkanımız vardır. Ayrıca bu işlemin klavye kısa yolu Alt+W,L şeklindedir. Bu adımları takip ederekte basit bir şekilde açık olan bütün dökümanları kapatabilmeniz mümkündür.

img1

2.) CTRL+F4

Eski alışkanlıklarımızdan olan bu kısayol açık olan kod dosyasını kapatmaya yaramaktadır.

3.) Sağ Tıklama –> Close All But This

Eğer sadece tek dökümanı açık bırakıp diğerlerini kapatmak istiyorsak bu seçeneğin kullanılması büyük çabuk sağlamaktadır.

img2

4.) Fare Orta Tuş (Tekerlek) Tıklamak

Farenin ortasında yer alan tekerleğin tıklama özelliğini kullanarak açık olan kod dosyalarını kapatmaktır. Bu işlemin fareyi götürüp kapatma tuşu üzerinden kapatmak pek bir farkı yokmuş gibi gözükse de kapatma yerine gidilmeden kapatılmasından ötürü çok daha rahat bir kullanım sağlamaktadır.

Bu son seçenek ile temel kısayollara değinmiş olduk. Peki neden bu açık olan dökümanları kapatmak önemlidir? Çünkü tekrardan proje açıldığında bu dosyaları tek tek yüklemeye çalışacağından VS üzerinde açılma süresinde yavaşlamaya sebep olacaktır. Yapacak olduğumuz bu işlemler sonrasında ise projelerin açılma sürelerini kısmen hızlandırmış olacağız.

İyi günler…

Perşembe, Mart 04, 2010

Visual Studio 2008 - Run as Administrator

Windows Vista ya da 7 işletim sistemlerinde Visual Studio 2008 ile çalışırken IIS üzerinden geliştirilecek uygulamalar için bu yerel sunucuya erişmek istendiği durumlarda yapılması gereken bir kaç işlem vardır.
VS2008 ile yerel sunucu üzerinde uygulama geliştirmek istediğimizde bize admin olarak uygulamayı açmanız şeklinde bir mesaj vermektedir. Bu mesajı engellemek için mantıken uygulamayı administrator ile açmamız gerekmektedir. Bu işlem için yapılması gerekenler aşağıdaki gibidir.
Birinci basit çözüm Visual Studio 2008 üzerinde sağ tıklama sonrasında admin olarak çalıştır demektedir. Ancak bu işlem sadece bir seferliğine mahsus bir çözüm olmakla birlikte bir daha ki sefere tekrardan açmak istediğinizde iis ‘i göremeyiyor oluruz.
İkinci çözüm ise kalıcı olandır. Visual Studio 2008 ‘in üzerinde sağ tıklama yaptıktan sonar özellikler seçeneğinden aşağıda çıkan ekranda admin olarak çalıştır seçeneğini seçip değişiklikleri uyguladığınızda artık her açışınızda sorunsuzca çalışıyor olacaksınız.


Umarım yararlı olabilir.

Cuma, Ocak 22, 2010

WF - Exception İşlemleri

Günümüzde iş akışı teknolojilerini sayar mısınız şeklinde bir soru geldiğinde bu çözümün Microsoft kanadında ki ürünü olarak aklımıza ilk olarak Windows Workflow Foundation çözümü gelmektedir. Özellikle insanlar tarafından tetiklenen (Human Workflow) iş akışı yapıları arasında çok kullanışlı bir teknoloji olarak bulunmaktadır.

Workflow Foundation teknolojisi incelemeye hem kod yardımı ile hem de tasarım ekranı üzerinden yapmış olduğumuz basit bir uygulama ile başlamıştık. Bu yazı dizimizde ki ana amaç iş akışı ile ilgili uygulama geliştirmek istediğimizde nasıl aklımızda nasıl yapacağız şeklinde bir soru işareti kalmadan rahatlıkla yapmaktadır.

Geliştirilen bütün uygulama tiplerinde ortaya çıkan istinai durumları (Exception) tespit edip kontrol ediyor olabilmek en önemli özelliklerden birisidir. Bu yazımızda Workflow uygulamalarında istisnaları nasıl kontrol edebileceğimizi incelemeye çalışıyor olacağız.

WF 'te Exception kavramını örnek üzerinden incelersek çok daha akılda kalıcı olacaktır. Örneği, Visual Studio 2010 Beta 2 IDE 'sinde .Net Framework 3.0 sürümü üzerinde geliştiriyor olacağız. Peki, neden .Net 4.0 dururken 3.0 üzerinde geliştiriyoruz. Sharepoint uygulamaları (2010 sürümü dahil olmak üzere), daha önceden geliştirilen uygulamaları  ve .Net 3.0 ile geliştirilecek olan yeni uygulamaları göz önünde tutarak bu teknoloji üzerinde inceliyoruz.

Uygulamamız için proje şablonlarından Workflow seçeneğini ve Framework sürümü olarakta .Net 3.0 'ı seçtikten sonra Sequential Workflow Console Application 'ı seçerek çalışmaya başlayabiliriz.



Exception örneğini basitçe inceleyebilmek için karşımıza çıkan dizayn ekranına Code Activity bileşeni ekliyoruz.



Sonrasında eklemiş olduğumuz kontrolün üzerine sağ tıklama yaptıktan sonra properties ekranından events (olaylar) seçeneğinden ExecuteCode seçeneğine çift tıklayarak arka plana kod oluşturuyor ve aşağıdaki basit kod parçasını ekliyoruz.



private void codeActivity1_ExecuteCode(object sender, EventArgs e)
{
    throw new ApplicationException("Başarısız oldu...");
}

Code Activity kontrolünün çalıştığı anda çalışacak olan olayına uygulamanın istisnai duruma düşmesi için gerekli kod parçasını ekliyoruz. Bir önceki yazımızdan da hatırlayacağınız üzere WorkflowRuntime sınıfının altında yer alan WorkflowTerminated özelliğinden düşen istisnai durumları görüntüleyebilmemiz mümkündür. Eğer ki uygulamayı çalıştırır ve olayı bulunduğu delegate 'in içerisinde yer alan kod parçasının içerine breakpoint eklememiz durumunda debug anında düştüğünü ve bizim eklemiş olduğumuz mesajın yazıldığını göreceğiz.



Basit anlamda ilk istisna yakalanması işlemini sağladık. Hata durumlarını daha spesifik yakayabilmemiz için workflow ortamında iki adet daha exception tipi bulunmaktadur. Bunlar Cancel Handler ve Fault Handler 'dır.


Bizim şimdi değineceğimiz bölüm ise View Fault Handlers olacaktır. İşlemin sonuçlanamadığı durumlar için bu bölüm kullanılmakta ve istisnaları yakalamak için Fault Handler bileşeni kullanılmaktadır.



Bu bileşeni Fault Handlers bölümüne sürükledikten sonra karşımıza çıkan ekranın üzerine sağ tıklama yaptıktan sonra properties ekranına geliyor ve FaultType 'a biraz önce CodeActivity içerisinde kullanmış olduğumuz ApplicationException sınfını bularak seçiyoruz.

Not: Bu tip mscorlib 'in içerisinde yer alan System isim alanında yer alır.



Sonrasında view fault handlers ekranında eklemiş olduğumuz FaultHandler bileşeninin içerisine Code Activity sürükledikten sonra properties ekranından ExecuteCode olayına istediğimiz hata mesajını yazdırıyoruz.

 
Eklemiş olduğumuz kod parçası aşağıdaki gibi olacaktır.

private void codeActivity2_ExecuteCode(object sender, EventArgs e)
{
    Console.WriteLine("Olamaz... Yakaladın beni.");
    Console.ReadLine();
}

Daha sonra uygulamayı derleyip debug modda çalıştırdığımızda iş akışı içerisine eklemiş olduğumuz code activity 'de ilk olarak hataya düşecek ve sonrasında da faulthandler içerisine düşücektir.



Düşündüğümüz senaryolar sorunsuzca çalışıyor. Şimdi ise iş akışı tasarım ekranına Sequence ekliyoruz ve daha önce oluşturmuş olduğumuz codeActivity1 isimli kontrolü bu bileşenin içerisine ekliyoruz.


Sonrasında daha önce fault handlers durumunda yapmış olduğumuz işlemlerin hepsini tekrarlayarak aşağıdaki durumu sağlıyoruz.


Sonradan eklemiş olduğumuz sequence 'in içerisine eklemiş olduğumuz FaultHandler bileşeninin içerisine düşen istisnai durumları artık gözlemleyebiliriz. Projemizi derledikten ve hatasız olduğunu anladıktan sonra debug mod ile çalıştırdıktan sonra sequence içerisine eklemiş olduğumuz hata bilgisine geldiğini ve bu mesajı gösterdiğini gözlemleyeceğiz.


Ayrıca uygulama çalıştırılırken gözlemleneceği gibi ilk olarak en iç tarafta yer alan yerel iş akışları tamamlanmakta ve sonrasında dış akışlara geçilmektedir. Bu mantıktan ötürüde en son eklediğimiz sequnce bileşeninin içerisinde bulunan FaultHandler 'a girecek ve akışı burada kesecektir. Bu sayede iç kontroller esnasında eğer ki akışta bir problem çıkarsa akışı durdurup hata mesajını en son adımı beklemeden verdirebilme olanağımız olacaktır.

Hazırlanmış olan proje çalışırken herhangi bir aksaklık (Fault) ortaya çıkması durumunda istediğimiz mesajları gösterebileceğimizi gördük. Ancak daha önceden de değindiğimiz gibi uygulama esnasında ortaya çıkan sorunlar ApplicationException sınıfında tutulmaktadır. Bu sebepten ötürü iş akış diyagramına eklemiş olduğumuz FaultHandler bileşeninin özellikler kısmında yer alan Fault kısmını kullanarak belirtmiş olduğumuz hata mesajını verdiyor olacağız. Hatırlayacağınız üzere yazımıza ilk başladığımızda ApplicationException sınıfını kullanmış,  WorkflowRuntime sınıfının workflowTerminated olayını tetiklemiş ve oluşan uygulama aksaklıklarında "Başarısız oldu..." mesajını verdirmiştik. Bu durumu şimdi FaultHandler bileşenini eklemiş olduğumuz yerlerde nasıl kullanacağımıza göz atalım.

FaultHandler bileşenin üzerinde sağ tıklama yaptıktan sonra karşımıza çıkan ekrandaki özelliklerden Fault özelliğine değeri atayacağız.

Bu değer için ApplicationException sınıfında bir özellik (property) tanımlayıp kullanıyor olacağız.

private ApplicationException myException;

public ApplicationException MyException
{
    get { return myException; }
    set { myException = value; }
}

Özelliği tanımladıktan sonra biraz önce göstermiş olduğumuz yere yol olarak MyException özelliğini veriyoruz.



Artık uygulamamızı çalıştırıp hata mesajımızı olabilirim. Debug modda çalıştırdığımız karşımıza çıkan durum tam istediğimiz gibi olacaktır. Daha önceden MyException sınıfını kullanarak belirttiğimiz hata mesajını belirttiğimiz her yerde görebilme olanağımız vardır.


Gözlemlediğimiz üzere istediğimiz mesaj değer olarak karşımıza çıkmıştır.

Şimdi yapacağımız işlemde ise Sequence içerisinde yer alan codeActivity 1 'i silmek ve yerine Throw bilşenini eklemek olacaktır. Bu sayede proje çalışırken karşılaşılan herhangi bir istisnai durumda fırlatılan hatayı kontrol etmiş ve istediğimiz mesajı verebilmiş olacağız.

Throw bileşeninde kullanılmak üzere ApplicationException sınfından yararlanılarak bir özellik oluşturuyoruz.

public ApplicationException ExceptionToThrow
{
    get { return (new ApplicationException("Pardon!")); }
}


Throw bilşeninde özelliği aşağıdaki gibi kullanabiliriz.


ThrowActivity bileşenine sağ tıklama yaptıktan sonra Fault özelliğine daha önceden tanımlamış olduğumuz özelliği atıyoruz.



Bu işlem sonrasında artık projemizi oluşturup hazırlanan property 'ler yardımı throw bilşenini kontrol edebiliriz. Debug ile breakpoint noktasına geldikten sonra MyException.Message 'dan dönecek olan değere ExceptionToThrow a atamış olduğumuz değeri görürüz.


Görüldüğü üzere istediğimiz hata mesajını göstermiş oluyoruz.

Bu örnek ile geldik bir yazımızın daha sonuna, bu yazımızda Windows Workflow Foundation 3.0 ile Exception kavramını detaylı bir şekilde incelemeye çalıştık. Hem kod yardımı ile hemde gerekli WF bileşenlerinin kullanarak nasıl kullanabileceğimize değinmiş olduk.

Umarım yararlı olabilmiştir.

Turhal Temizer

info@turhaltemizer.com
turhal.temizer@csharpnedir.com

Perşembe, Ekim 22, 2009

WPF - DataGrid Pratikleri

Windows Presentation Foundation ile ilgili kontrolleri özellikle de .Net Framework 4.0 ile çok daha sık kullanacak olduklarımızı incelmeye devam ediyoruz. Bir önceki yazımızda WPF DataGrid kontrolünün nasıl kullanabileceğini ve özelliklerinden nasıl yararlanabileceğimizi anlatmaya çalışmıştık. Bu yazımızda yine DataGrid kontrolünü incelerken detaylı bir şekilde proje geliştirme esnasında DataGrid kontrolünü nasıl kullanabileceğimizi açıklamaya çalışıyor olacağız.

Son kullanıcıya veri göstermek istenildiğinde akla gelen ilk kontrollerdendir DataGrid kontrolü.  Asp.Net uygulamalarında DataGrid, Windows Form uygulamalarında DataGridView kontrolü olarak geçmektedir. Ayrıca  üçüncü parti kontrol geliştiren yazılım firmalarınında ilk olarak değindikleri ve özelleştirdikleri kontroldür. Bu durumda bu kontrolün WPF ilk çıktığı zamanlarda bileşen panelinin içerisinde yer almaması oldukça büyük bir eksiklik olarak hissediliyordu. .Net Framework 3.5 'in SP1 sürümünün çıkması ve sonrasında WPF Toolkitin iyice kullanılabilir hale gelmesi ile WPF için yeni yeni kontroller geliştiricilere sunulmaya başladı. Bu geliştirilen kontrollerden şu anda için en sık kullanılan DataGrid, DateTimePicker ve Calendar kontrolleri özelleştirilebilir halleri ile .Net Framwork bünyesine de eklenmiştir. Zaman içerisinde kontrol panelinin içeriğinin artacağından kimsenin şüphesi yoktur.

Peki DataGrid kontrolünü VS2010 ile kullanırken herşey çok kolay. Zaten Toolbar içerisinde ekli olan bütün kontroller yer almaktadır. Peki ben geliştirme işlemini VS2008 içerisinde yapacağım, hem de WPF içerisinde DataGrid kontrolleri kullanmak istiyorum dersek neler yapmamız gerekiyor. Alt kısımlarda verecek olduğumuz linklerden yararlanarak kullanabilirsiniz. O zaman ilk olarak VS2008 ile neler yapmak gerekmektedir. Bu konulara değindikten sonra DataGrid pratiklerini incelemeye başlayalım.

Visual Studio 2008 içerisinde olmayan WPF kontrollerini kullanmak istediğimizde yapmamız gereken en önemli iş WPF Toolkit 'i kurmak yeterli olacaktır. Çalışmak için gereklilikleri aşağıda ver alan linklerden yararlanarak kurabilmeniz mümkündür.

.Net Framework 3.5 SP1 kurulması gerekmektedir.
WPF Toolkit
• WpfToolkit.msi dosyasını kurduktan sonra WPFToolkit.dll 'i program files dosyasının içerisine ekler. Projeye içerisine referans ederek kullanılabilmesi mümkündür.

Visual Studio 2008 ile anlatmış olduğumuz uygulamayı yapmak istersek nasıl yapabileceğimizi neler gerektiğini açıkladık. Artık uygulamamızı geliştirmeye başlayabiliriz. Bakalım DataGrid kontrolü ile neler yapabileceğiz.

DataGrid kontrolleri içerisinde CRUD (Create-Read-Update-Delete) işlemlerini nasıl yapabileceğimizi açılmaya çalışıyor olacağız. Bu işlem normalde hazırladığımız örneklerden biraz daha farklı olarak DataSet yardımı ile kullanmayı inceleyeceğiz. Uygulama içerisinde Northwind veri tabanından yararlanıyor olacağız. Oluşturacak olduğumuz dataset 'in içerisine aşağıda resimde de gördüğünüz Customers ve Orders tablolarını ekliyoruz.



İlk olarak DataSet 'e eklemiş olduğumuz Customers tablosunu DataGrid kontrolü içerisine ekleyelim. Bunun için ise, XAML sayfanın içerisine DataGrid kontrolünü ekledikten sonra ItemSource değerine {Binding} değerini atıyoruz.

Windows1.xaml
<Window x:Class="DataGridPratikleri.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Northwindler" Height="231" Width="500">

    <Grid>
        <DataGrid ItemsSource="{Binding}" Height="193" HorizontalAlignment="Left" Name="dataGrid1" VerticalAlignment="Top" Width="478" />
    </Grid>
</Window>


WPF uygulamasına DataGrid kontrolünü ekledik. Şimdi ise DataSet içerisine eklemiş olduğumuz tabloları kullanarak otomatik olarak oluşturulan veri getirme fonksiyonundan yararlanarak verileri gösterelim. Table Adapter yardımı ile

Windows1.xaml.cs

public Window1()
{
    InitializeComponent();

    DataSet1 dataset = new DataSet1();

    CustomersTableAdapter adapter = new CustomersTableAdapter();
    adapter.Fill(dataset.Customers);

    this.DataContext = dataset.Customers.DefaultView;
}


Verileri görüntüleyebilmek için gerekli işlemleri gerçekleştirdik. Yapmamız gereken son işlem ise Grid içerisine yerleşecek olan kolonlar otomatik olarak ekleneceği için AutoGenereteColumns özelliğine True değerini atamak olacaktır. Sonrasında ise aşağıdaki gibi bir görünüme sahip oluruz.



Yapmış olduğumuz işlemler sonrasında DataGrid içerisinde verilerin sorunsuzca gözüktüğünü gözlemledik. DataGrid içerisinde gösterilen verilerin satır ve sütunlarının otomatik olarak oluşturuluyor. Ayrıca bu otomatik oluşturulma işleminin dışında bizlerde DataGrid kontrolü üzerinde sağ tıklama yaptıktan sonra karşımıza çıkan menüden DataGrid seçeneğinin altında yer alan Add/Edit yardımı ile gerekli düzenlemeleri yapabiliriz. Ancak unutulmaması gereken bir konu, verileri otomatik olarak oluşturduğumuz için bu seçenek içerisinde şu anda bir deneme yaptığınızda herhangi bir değer çıkmayacaktır.

Veri kontrolüni sağlanabileceği bir başka yöntem ise ObjectDataProvider kullanımıdır. Bu sınıf yardımı ile XAML içerisinde veri kaynağı olarak kullanılacak olan örnekleri sağlamaya yardımcı olur. Bı işlem sonrasında da denetimleri veri kaynağı üzerinde yaptırabilme olanağı da vardır. (Not:İlerleyen yazılarımızda ObjectDataProvider kullanımı detayları ile açıklayacağız.)

ObjectDataProvider 'i kullanacağımız CustomerDataProvider isimli sınıfı oluşturarak işlemlerimize başlıyoruz. Bu sınıf içerisinde DataSet içerisinde yer alan objelerden de yararlanıyor olacağız.


public class CustomerDataProvider
{
    private CustomersTableAdapter adapter;
    private DataSet1 dataset;

    public CustomerDataProvider()
    {
        dataset = new DataSet1();
        adapter = new CustomersTableAdapter();
        adapter.Fill(dataset.Customers);
    }

    public DataView GetCustomers()
    {
        return dataset.Customers.DefaultView;
    }
}


Projenin içerisine yeni bir sınıf ekleyiruz ve yazmış olduğumuz kod bloklarının ObjectDataProvider ile uyumlu olması için yukarıda da görmüş olduğunuz kod parçacıklarını ekledik.. Şimdi sıra XAML kod tarafının içeriğini bu yapı ile çalışabilecek duruma getirmemiz gerekmektedir.

Windows1.xaml

<Window ...
    <Window.Resources>
        <!-- create an instance of our DataProvider class -->
        <ObjectDataProvider x:Key="CustomerDataProvider"
                ObjectType="{x:Type local:CustomerDataProvider}"/>
        <!-- define the method which is invoked to obtain our data -->
        <ObjectDataProvider x:Key="Customers"
                ObjectInstance="{StaticResource CustomerDataProvider}"
                MethodName="GetCustomers"/>
    </Window.Resources>
    <DockPanel DataContext="{Binding Source={StaticResource Customers}}">
        <DataGrid Name="dataGrid" ItemsSource="{Binding}" />
    </DockPanel>
<Window>


XAML tarafında ki düzenlemeleri de tamamladıktan sonra dizayn kısmına gereken DataGrid 'in üzerine sağ tıklama yaptığımız da DataGrid menüsünün altında yer alan seçeneklerden GenerateColumns,Add/EditColumns ve Remove Columns seçenekleri yer alacaktır. Bunlardan GenerateColumns seçeneğini seçersek bize DataBound kullanılacak şekilde bütün kolonları belirler. Tabii bu işlemi ObjectDataProvider kullandığımız için yapabiliyoruz. Biraz önceki kontrolümüzde bu seçenekler yer almıyordu. Ayrıca Add/Edit columns seçeneğine tıkladığımızda ilk etapta hiç bir değer gelmeyecektir. Ancak ASP.NET uygulamalarında kullanmış olduğumuz DataGrid konrollerinden de alışık olduğumuz gibi kolonları buradan düzenleyebilmemiz, tipini belirleyebilmemiz mümkündür. Bu açıkladığımız bilgileri görsel olarak görmek gerekirse, ekran görüntüleri aşağıdaki gibi olacaktır.



Add/Edit Columns seçeneğine tıkladıktan sonra herhangi bir kontrol ekleyip düzenle dediğimizde ise aşağıdaki gibi bir görüntü ile karşılaşırız.



Sizlerinde de fark etmiş olacağınız gibi Asp.Net uygulamalarında kullanmış olduğumuz yöntemlerin benzerlerini WPF uygulamalarında da kullanabilmemiz mümkün durumdadır. Bu ekran yardımı ile yapmış olduğunuz değişikler sonrasında XAML kod tarafında DataBound edilebilecek şekilde kolon bilgilerinin eklendiğini gözlemleyebilirsiniz.

Güncelleme İşlemleri
DataGrid kullanılan uygulamalarda son kullanıcılargenellikle Grid içerisinde yer alan kolonlara girmiş oldukları değelerin kayıt olabilmesini istemektedirler. DataTable içerisinde veri gösterildiği için son kullanıcı tarafından yapılan bu değişiklikleri farklı yötemler ile veri tabanına kayıt edebilmemiz mümkündür. Örnek olarak biraz anlatmak gerekirse, kullanabileceğimiz yöntemlerden biri bütün değişiklieri yapmak ve sonrasında misal olarak sonlandır isimli bir buton ile bütün değişikleri kaydebiliriz. Ya da her değişiklik yapıldığı anda da DataTable üzerinde kayıt işleminin yapılabilmesi mümkündür.

Satır üzerinde yapılan değişikleri yakalayabilmek için DataTable 'in RowChanged ve RowDeleted olaylarından yararlanabiliriz. Bu işlemler sonrasında istediğimiz işlemleri sorunsuzca yaptırabilir duruma geliyoruz.

CustomerDataProvider sınıfını oluşturduğumız sınıfın içeriğini aşağıdaki gibi olacak şekilde güncelliyoruz. Bu güncelleme işleminde DataSet 'i de bir ObjectDataProvider olarak tanımlıyoruz. Ayrıca RowChanged ve RowDeleted olaylarını da yakalayarak güncelleme işleminin yapılmasını sağlıyoruz.

Günceleme işlemi için ilk olarak yapacağımız DataSet bağlantısını da ObjectDataProvider yapısına uydurmak olacaktır. Sonrasında ise CustomerDataProvider isimli sınıfımız içerisinde güncelleme işleminin yapılabilmesi için bir takım değişikler yapacağız. Yapmış olduğumuz değişiklikler sonrasında DataGrid içerisinde yapılan herhangi değişiklik sonrasında başka bir kolona geç dediğimiz anda değişiklikler veri tabanında güncellenmiş olacktır. Sınıfın içerisindeki değişiklik aşağıdaki gibidir.

Windows1.xaml

using Test.DataSet1TableAdapters;
using System.Data;

namespace Test
{
    public class NorthWindDataProvider
    {
        private static DataSet1 northwindDataSet;

        public static DataSet1 NorthwindDataSet
        {
            get
            {
                if (northwindDataSet == null)
                {
                    northwindDataSet = new DataSet1();
                }
                return NorthWindDataProvider.northwindDataSet;
            }
        }
    }
    public class CustomerDataProvider
    {
        private CustomersTableAdapter adapter;

        public CustomerDataProvider()
        {
            DataSet1 dataset = NorthWindDataProvider.NorthwindDataSet;

            adapter = new CustomersTableAdapter();
            adapter.Fill(NorthWindDataProvider.NorthwindDataSet.Customers);

            dataset.Customers.CustomersRowChanged += new DataSet1.CustomersRowChangeEventHandler(CustomerRowModified);
            dataset.Customers.CustomersRowDeleted +=new DataSet1.CustomersRowChangeEventHandler(CustomerRowModified);

        }

        void CustomerRowModified(object sender, DataSet1.CustomersRowChangeEvent e)
        {
            adapter.Update(NorthWindDataProvider.NorthwindDataSet.Customers);
        }

        public DataView GetCustomers()
        {
            return NorthWindDataProvider.NorthwindDataSet.Customers.DefaultView;
        }
    }
}


Değişikliğimiz sonrasında XAML kod bölümünde herhangi bir değişiklik yapmıyoruz. Uygulamamızı çalıştırıp değişiklikleri tamamladıktan sonra tekrardan uygulamamızı çalıştırdığımızda değişikliklerin kaydolduğunu ve DataGrid içerisinde yeni değerinin yer aldığını gözlemleriz.

Sonuç olarak DataGrid içerisinde veri tabanında yer alan verileri göstermeyi ve üzerinde değişiklik yapmak istediğimiz zaman hangi işlemleri uygulamayacağımızı anlatmaya çalıştık.

Umarım yararlı olabilmiştir.
Kaynak
www.windowsclient.com

RegisterForEventValidation can only be called during Render();

This error comes when you are exporting the GridView control in the Visual Studio.NET 2005-2008-2010 version. I am using the released version so I never got this error. Anyway, you can solve this error but the trick is very dirty. You have to turn the eventValidation off in order for this to work.

You can do this in the web.config file but in this case the eventValidation will be turned off for all the pages.

or you can do this in the Page directive which will turn off the validation for a single page.


I hope this helps!

Çarşamba, Eylül 30, 2009

Asp.Net MVC Framework - OutputCache Kullanımı ve Dinamik İçerik Görüntüleme

Asp.Net MVC Framework ile geliştirilen uygulamaların performanslarının arttırılması için bir ön bellekleme kullanılması en çok tercih edilen yöntemlerden biridir. Web sayfalarının görüntülenmesi esnasında benzer veriler görüntülenecekse bu sayfaların önbellekte tutulması sayfa görüntüleme performansını arttıracak. Ana sayfada görüntülenen bir veriyi önbellekleme kullanarak diğer kullanıcılar içinde hızlıca açılabilmesine olanak tanırız. Bu konu hakkında kafanıza takılan konular var ise OutputCache Kullanımı ve Performans başlıklı bir önceki yazımızı okuyabilirsiniz.

Ön bellek kullanarak benzer verileri bellekte tutulması yöntemi ile bir çok kullanıcıya hızlı bir şekilde gösterebiliriz. Evet performans açısında başarılı bir çözüm ancak bu kullanımda kesinlikle dikkat edilmesi gereken bir nokta var. Web sayfası üzerinde yer alan dinamik içerikler. Bu tür sayfalarda 4-5 adet reklam rastgele bir şekilde sayfayı açan kullanıcılara gösterilecektir. Eğer ki bizler bütün sayfayı önbelleğe aldırırsak sayfa her yenilendiğinde kullanıcı aynı reklamı görmeye devam edecektir. Bu durumda web sitelerinin reklamlar üzerinden gelir elde ettiğini düşünürsek hiçte istenen bir durum olmadığı gözlenir. Çünkü siteler parayı yalnızca açılmış olan tek reklam üzerinde alacaklardır ki kullanıcılar da bir kez tıkladığı reklamı ikinci kez tıklamama ihtimali oldukça yüksektir.

Bu noktada MVC ile uygulama geliştirmek istiyoruz ancak reklamda kullanmak istiyoruz. Ayrıca uygulamanın performansının da hızlı olması için önbellekte kullanılması gerektiğini gözlemliyoruz. Peki, yapılabilecek bir işlem yok mu? Neyse ki bu sorunun çözümü oldukça basittir. Asp.Net Framework 'ü içerisinde yer alan post-cache substitution 'ı çağırmak yeterli olacaktır.

Not: Normal durumda OutputCache özniteliği yardımı ile sayfalar hem sunucu hem de son kullanıcının bilgisayarında yer alan internet tarayıcısında önbelleğe alınabiliyor. Değişken bir içerik kullanılacaksa (banner reklamlar gibi) post-cache substitution kullanımı ile sayfanın o bölümü sunucu üzerinde önbelleğe alınarak kullanılır.

Post-Cache Substitution Kullanımı

Post-Cache Substitution kullanımını iki adımda tamamlanabilinmesi mümkündür. Birinci adım, geri dönüş değeri string olan bir metot hazırlayarak dinamik içeriğin görüntülenecek olduğu önbelleğe alınacak olan bir sayfa oluşturmak olacaktır. Daha sonraki adımda ise dinamik içeriğin görüntüleneceği sayfaya HttpResponse.WriteSubstitution() metodunu kullanabilmek için ekliyoruz.

Bahsettiğimiz bu konuları bir örnek ile pekiştirmek gerekirse, ekran üzerinde rastgele veri gösteren değerleri önbelleğe alınacak dinamik bir web sayfası oluşturalım. Bu işlem için basitçe RenderNews() isimli bir metot oluşturalım. Geri dönüş olarak stringlist in içerisinde yer alan değerleri rastgele olarak göstersin.

Models\News.cs
using System;
using System.Collections.Generic;
using System.Web;

namespace DynamicContentCache.Models
{
    public class News
    {
        public static string RenderNews(HttpContext context)
        {
            var news = new List<string>
            {
                "Dünya 1",
                "Dünya 2",
                "Dünya 3"
            };

            var random = new Random();
            return new string[random.Next(news.Count)];
        }
    }
}

Model içerisine oluşturulan verileri view içerisinde oluşturacak olduğumuz sayfa içerisinde göstereceğiz. Bu sayfa içerisinde HttpResponse.Substitution() metodundan yararlanarak önbelleğe alınmış sayfa içerisinde dinamik içeriğin rastgele olarak görüntülenebilmesine olanak tanınacaktır.

Post-cache substitution yöntemini kullanabilmek için HttpResponse.WriteSubstitution() metodunu kullanıyor olacağız. Bunun için view sayfa içerisinde dinamik olarak değiştirilmesi istenilen değerler bu metot içerisine eklenilerek kullanılır.

Views\Home\Index.aspx
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<DynamicContentCache.Models.News>>" %>
<%@ Import Namespace="DynamicContentCache.Models" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Index
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <div>
        <% Response.WriteSubstitution(News.RenderNews); %>
        <hr />
             OutputCache içeriği
             <%= DateTime.Now %>
    </div>
</asp:Content>

WriteSubstitution() metodundan yararlanarak RenderNews() içerisinde yer alan değerleri her yenileme esnasında rastgele olarak getirilmesini sağlamaktadır.

Controller katmanında yer alan Home Controller sınıfının içerisindeki Index() Action unda da sayfa üzerinde gösterilecek olan değerlerin herhangi bir parametre almadan 60 saniyede bir önbelleğe alınmasını sağlayacak şekilde gerekli değişiklikleri yapıyoruz. Bir önceki yazımızda da değindiğimiz gibi önbelleğe alma işlemlerini [OutputCache] özniteliğini kullanarak yapabildiğimizi detaylı bir şekilde incelemiştik.

Controllers\HomeController.cs
using System.Web.Mvc;

namespace DynamicContentCache.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        [OutputCache(Duration=60, VaryByParam="none")]
        public ActionResult Index()
        {
            return View();
        }
    }
}

HomeController içerisinde yer alan Index() 'in içerisinde yer alacak olan verilerin ön belleğe alınmasını sağlamış durumdayız. Bu işlemler sonrasında Index.aspx sayfasının içerisinde yer alan değerler bir dakika boyunca önbellekte kalmaya devam edecektir. Ancak dinamik içeriklerin değişmesi istemiştik. WriteSubstitution() metodu sayesinde web sayfası önbelleğe alınmış olmamıza rağmen sorunsuzca dinamik verilerin güncellendiğini gözlemleyeceğiz. Yapmış olduğumuz işlemler sonrasında karşımıza çıkan ekran görüntüsü aşağıdaki gibi olacaktır.



Web sayfası görüntülenirken sayfayı her yenilediğimizde önbelleğe alınan kısım bir dakika boyunca sabit kalırken dinamik olarak değişmesini istediğimiz değer de sorunsuzca kendisini yenileme işlemini gerçekleştirmektedir.

Post-Cache Substitutionu Helper Metot içerisinde Kullanmak

WriteSubstitution() metodunu view sayfaların içerisinde kullanabilmekle birlikte bizlerin hazırlamış olduğu harici HTML Helper metotlar yardımı ile de kullanılabilmesi mümkündür. Bunun için projemize bir adet AdHelper.cs isimli helper sınıfı ekliyoruz ve aşağıdaki işlemleri uyguluyoruz.

Not: HTML Helper metotların nasıl oluşturulacağı konusunda yeterince bilgiye sahip olduğunuza inanmıyorsanız daha önceden yayınlanmış olan yazılarımızı okuyarak bu eksiğinizi tamamlayabilirsiniz.

Helpers\AdHelper.cs
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Mvc;

namespace DynamicContentCache.Helpers
{
    public static class AdHelper
    {
        public static void RenderBanner(this HtmlHelper helper)
        {
            var context = helper.ViewContext.HttpContext;
            context.Response.WriteSubstitution(RenderBannerInternal);
        }

        private static string RenderBannerInternal(HttpContext context)
        {
            var img = new List<string>
            {
                "images/tuural.jpg",
                "images/turhal.jpg"
            };

            var random = new Random();
            var ad = img[random.Next(img.Count)];
            return String.Format("<img src='{0}' />", ad);
        }
    }
}

Statik olarak iki tane metot oluşturuyoruz. Bunlar, RenderBanner() ve RenderBannerInternal() 'dir. Helper metot olarak kullanılacak olan RenderBanner() 'ı hazırken WriteSubstitution 'nun callback yapacak olduğu değerileri alabilmesini sağlayan RenderBannerInternal() dir. Asp.Net MVC HtmlHelper sınıfından yararlanarak view sayfalarda Html.RenderView() şeklinde kullanılmasına olanak tanımış oluyoruz.

RenderView() metodu çağırıldığında HttpResponse.WriteSubstitution() metodu doğrulayarak RenderViewInternal() metodunu kullanacaktır. Ayrıca private bu metot içerisinde rastgele olarak seçilen banner resimlerin görüntülenmesi için gerekli işlemler sağlanmaktadır. Biraz önce yazılar için yapmış olduğumuz dinamik içeriklerin güncellenmesi işlemini şimdi de resimler için hazırlamış olduk.

Tekrardan kısaca oluşturulan harici HtmlHelper ları nasıl view sayfalarda görüntüleyeceğimizi hatırlayalım. İlk olarak yapılması gereken oluşturulmuş olan Helpers sınıfını web sayfasına <% import%> şeklinde eklemek olacaktır. Sonrasında ise helpers sınıfı içerisinde yer alan metotlardan HtmlHelper 'a göre oluşturulmuş olanlar biz web sayfasında <% Html. %> şeklinde bir kullanım esnasında otomatik tamamlama ekranında çıkacaktır. Sonrasında ise tek yapmamız gereken kendi Helper Metodunuzu projenizde kullanmak olacaktır. Bu ön bilgi sonrasında biraz önce oluşturulmuş olan Index.aspx sayfasını aşağıdaki biçimde güncelliyoruz.

Views\Home\Index.aspx
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<DynamicContentCache.Models.News>>" %>
<%@ Import Namespace="DynamicContentCache.Models" %>
<%@ Import Namespace="DynamicContentCache.Helpers" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Index
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <div>
       
<% Response.WriteSubstitution(News.RenderNews); %>
       <hr />
        <% Html.RenderBanner(); %>
        <hr />
             OutputCache içeriği
             <%= DateTime.Now %>
    </div>
</asp:Content>

Yapmış olduğumuz değişiklikler sonrasında Index.aspx sayfasına Html.RenderView() helper ini çalışacak şekilde ekledik ve uygulamayı hazır hale getirdik. Artık web sayfalarında gösterecek olduğumuz reklamları ve diğer aklımıza gelmeyen öğeleri önbelleğe alınmış olan sayfalarda dinamik içerik olarak oluşturabilir duruma getiriyoruz. Uygulamanın çalışması sonrasında aşağıdaki gibi bir ekran görüntüsüne sahip olacaktır.



Sonuç gayet başarılı... Her yenileme sonrasında zamanı gösteren değer sabit kalırken resim ve üstündeki yazı bizim belirttiğimiz gibi rastgele olarak değişmektedir.

Geldik bir yazımızın daha sonuna. Bu yazımızda önbelleğe alınmış olan sayfalarda yer alan verilerin belirli bir zaman aralığı boyunca yenilenemeyeceği ana fikrinden yola çıkarak dinamik olarak değişecek bir içeriğin bu sayfalar içerisinde nasıl kullanılacağını detaylı bir şekilde incelemeye çalıştık.

Umarım yararlı olabilmiştir.

Cumartesi, Ağustos 15, 2009

Ado.Net Entity Framework – Entity Model ile Windows Form Uygulamaları ve Bug

Veri tabanı ile ilişkisel olarak yapmış olduğumuz işlemleri Ado.Net Entity Framework ile yaptığımızda çok daha hızlı ve kolay bir şekilde yapabilinmesi mümkündür. Ne kadar basit yapılabileceğine ilişkin basit bir örnek verelim.

Visual Studio 2008 ile Windows From projesi oluşturuyor ve sonrasında entity data model template ‘ini projeye ekliyoruz. edmx ‘in içerisine gerekli olan tabloları ekledikten sonra işlemleri yapmaya başlayabiliriz.

Windows Formun üzerine aşadağıdaki kontrolleri ekliyoruz.

Windows Form

Tasarımı oluşturduktan sonra aşağıdaki kodları ekliyoruz.

using System;
using System.Windows.Forms;
using System.Data.Objects;
using System.Data.Objects.DataClasses;

namespace CourseManager
{
    public partial class CourseViewer : Form
    {
        public CourseViewer()
        {
            InitializeComponent();
        }
        private SchoolEntities schoolContext;
        private void closeForm_Click(object sender, EventArgs e)
        {
            schoolContext.Dispose();
            this.Close();
        }

        private void CourseViewer_Load(object sender, EventArgs e)
        {
            schoolContext = new SchoolEntities();
            ObjectQuery<Department> departmentQuery = schoolContext.Department.Include("Course").OrderBy("it.Name");
            try
            {
                this.departmentList.DisplayMember = "Name";
                this.departmentList.DataSource = departmentQuery;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void departmentList_SelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                Department department = (Department)this.departmentList.SelectedItem;
                courseGridView.DataSource = department.Course;
                courseGridView.Columns["Department"].Visible = false;
                courseGridView.Columns["CourseGrade"].Visible = false;
                courseGridView.Columns["OnlineCourse"].Visible = false;
                courseGridView.Columns["OnSiteCourse"].Visible = false;
                courseGridView.Columns["Person"].Visible = false;

                courseGridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void saveChanges_Click(object sender, EventArgs e)
        {
            try
            {
                schoolContext.SaveChanges();
                MessageBox.Show("Changes saved to the database");
                this.Refresh();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
    }
}

Yukarıdaki kod bloglarını da projeye ekledikten sonra projemizi çalıştırdığımız aşağıdaki gibi bir görünüme sahip olacağız.

Debugging Windows Form

Evet herşey güzel gözüküyor.

Peki, bug nerede?

            {
                Department department = (Department)this.departmentList.SelectedItem;
                courseGridView.DataSource = department.Course;
                courseGridView.Columns["Department"].Visible = false;
                courseGridView.Columns["CourseGrade"].Visible = false;
                courseGridView.Columns["OnlineCourse"].Visible = false;
                courseGridView.Columns["OnSiteCourse"].Visible = false;
                courseGridView.Columns["Person"].Visible = false;

                courseGridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.AllCells);
            }
Veri tabanında yer alan tabloların gözükmemesi için görünebilirliğini pasif yapıyoruz. Ancak bu işlem esnasında yanlışlıkla CourseGrade yerine CourseGrades yazdım ve karşımıza aşağıdaki gibi bir sonuç çıkmıştır.

EntityBug

Dikkat ederseniz yanlış yazdığımız bölüm sonrası kapatmış olduğumuz bütün tablo isimleri gözükmüştür.

Ancak her ne olursa olsun Entity Framework ‘ün uygulamalarımıza kattıkları oldukça fazladır. Bu tür Bug ‘lar da .Net Framework ‘ün tamamlanması ile büyük ihtimal ortadan kalkacaktır.

Eğer ki sizler de bu tür buglar keşkefederseniz www.connect.microsoft.com adresinden hata merkezine belirtebilirsiniz.

Umarım yararlı olabilmiştir.

Perşembe, Temmuz 23, 2009

VSTO 2008 - Genel Özellikler ve Outlook 2007 için Form Uygulaması Oluşturulması

Microsoft 'un en çok beğenilen uygulamalarından biri olan Office System 2007 bile bazen ihtiyaçlarımıza cevap veremeyebilir. İşte bu noktada ne yapmalıyız sorusunu kendimize sormaya başlarız. Bu noktada yapmış olduğumuz araştırmalar sonrasında da Visual Studio 2008 ile Office uygulamalarına Add-in yazılabileceğimizi öğreniriz. Peki bu işlemi nasıl gerçekleştirebiliriz ve hangi uygulamaların bilgisayarımızda kurulu olması gerekmektedir.

Gerekli olan uygulamalar:
1.)
Visual Studio 2008
2.) Office System 2007
3.) Windows XP veya üstü bir işletim sistemi

Yukarıda saymış olduğumuz uygulamaları bilgisayara kurduktan sonra artık Office Add-in yardımı ile çalışabilir duruma geliyoruz. Bu eklentileri Visual Studio 2008 ile hazırladığımızda adlandırılma olarak VSTO 2008 olmaktadır.

İşlemleri nasıl yapacağımızı bir örnek üzerinde değerlendirelim. Örnek olarak Outlook 2007 kontaklarını resim olarak listeleyen ve üzerene tıkladığımızda da bize o kişinin kontak detaylarını getirecek. Şimdi adım adım nasıl yapacağımızı incelemeye çalışalım.

İlk yapılması gereken Visual Studio 2008 üzerinde proje oluşturulması olacaktır. Bu işlem için proje şablonları arasından Outlook 2007 Add-in projesini seçeceğiz ve sonrasında karşımıza gelen ekrandan Outlook 2007 Add-in projesini seçtikten sonra geliştirmeye başlayabilir duruma geliyoruz.



Proje şablonunu oluşturduktan sonra ThisAddin isimli sınıf oluşmaktadır. Bu sınıfın ana amacı oluşturulan Add-in açıldığından ve kapatıldığında ne iş yapılacağını açıklamaktadır. Fakat unutulmaması gereken bir nokta var ki o da, oluşturmak istediğimiz Add-in 'nin Outlook ta herhangi bir formun içerisinde mi açılacağı yoksa Outlook ile birlikte mi çalışacağıdır. Biz uygulamamız esnasında içte açılan bir yöntemi tercih edip işlemlerimize devam edelim.

Not: Outlook dışı çalışmasını istiyorsak yapmamız gereken Add->New Item seçeneğinden Windows form ya da WPF Form 'ı ekledikten sonra işlemleri yapmak olacaktır...

ThisAddin sınıfının içerisinde neler varmış bir göz atmak gerekirse,

ThisAddin.cs
namespace Contacts
{
    public partial class ThisAddIn
    {
        private void ThisAddIn_Startup(object sender, System.EventArgs e)
        {
        }

        private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
        {
        }

        #region VSTO generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InternalStartup()
        {
            this.Startup += new System.EventHandler(ThisAddIn_Startup);
            this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
        }

        #endregion
    }
}

Daha önceden de değindiğimiz üzere ThisAddin sınıfının içerisinde Add-in başladığı ve sonlandığı anda yer alan olaylar yer almaktadır. Outlook içerisinde kontaklar menüsünde uyumlu bir şekilde çalışacak olan içeriğini oluşturmaya başlayalım.

Uygulamanın üzerinde sağ tıklama yaptıktan sonra Add->NewItem adımlarını izliyor ve karşımıza çıkan ekrandan Office tabının içerisinde yer alan Outlook Form Region form şablonunu ekle diyerek işlemlerimize devam ediyoruz.



Ayrıca yukarıdaki ekrandan göreceğiniz üzere Ribbon yer alan formlarda hazırlayabilmemiz mümkündür. Bu kullanım şeklini de zaman içerisinde incelemeye çalışacağız. Seçeneği seçtikten ve ekle dedikten sonra karşımıza çıkan ekranda yeni bir Outlook Form mu tasarlayacağımızı yoksa var olan bir formdan mı yararlanacağımızı sormaktadır.



Yukarıda karşımıza çıkan seçenekten Design a new form region seçeneğini seçerek devam ediyoruz. Bir sonraki ekranda karşımıza ekleyecek olduğumuz formun ekran üzerinde nasıl yer alacağına ilişkin bilgiler girmemizi istemektedir.



Bu adımda Outlook ekranında Ribbon altında yer alan alanın hepsini dolduracak olan ve Ribbon içerisinde yeni bir ek yapacak olan Separate seçeneğini seçeceğiz. Diğer seçeneklerin ne işe yaradığını açıklamak gerekirse,

*Adjoining: Form 'u çalışma alanın alt kısmında tutar ve Add-in 'i var olan bir menü içerisine ekleme yapar.
*Replacement: Varsayılan form alanının içerisine ekleme yapar ve Add-in 'i var olan bir menü içerisine ekleme yapar.
*Replace-All:  Ribbon menüyü bizim istediğimiz şekilde yapılandırır ve Add-in 'i var olan bir menü içerisine ekleme yapar.

Separete seçeneğini tıkladıktan sonra bir sonraki adıma geçiş yapıyoruz. Bu ekranda bizden ekleyecek olduğumuz Add-in görünecek adını, üzerine fare ile tutulduğunda ne gibi bir açıklama vereceğini, açıklamasını ve kullanım alanının içinde nasıl çalışacağını ilişkin bilgiler girmemizi istemektedir.



Form Region 'a ilişkin bilgileri de girdikten sonra bir sonraki adımımızda işlemimizi var olan bir sınıf üzerinde mi yapacağız yoksa biz harici sınıf mı oluşturacağız diye sormaktadır. Yazının başından da hatırlayacağınız üzere örneğimizi kontaklar (Contact) üzerine yapacaktık. Bu durumda karşımıza çıkan ekrandan IPM.Contact seçeneğini seçmek isteklerimizi karşılayacak bir çözüm olacaktır.



Evet artık VSTO (Visual Studio Tools for Office) ile Add-in 'ni boş form ile oluşturmuş oluyoruz. Otomatik hazırlanın formun içerisinde FormRegionClosed ve FormRegionShowing olaylarını incelersek arka planda FormRegionMyContacts sınıfının içerisinde yer alan  kod bloklarına götürdüğünü göreceksiniz.

FormRegionMyContacts.cs
namespace Contacts
{
    partial class FormRegionMyContacts
    {
        #region Form Region Factory

        [Microsoft.Office.Tools.Outlook.FormRegionMessageClass(Microsoft.Office.Tools.Outlook.FormRegionMessageClassAttribute.Contact)]
        [Microsoft.Office.Tools.Outlook.FormRegionName("Contacts.FormRegionMyContacts")]
        public partial class FormRegionMyContactsFactory
        {
            // Occurs before the form region is initialized.
            // To prevent the form region from appearing, set e.Cancel to true.
            // Use e.OutlookItem to get a reference to the current Outlook item.
            private void FormRegionMyContactsFactory_FormRegionInitializing(object sender, Microsoft.Office.Tools.Outlook.FormRegionInitializingEventArgs e)
            {
            }
        }

        #endregion

        // Occurs before the form region is displayed.
        // Use this.OutlookItem to get a reference to the current Outlook item.
        // Use this.OutlookFormRegion to get a reference to the form region.
        private void FormRegionMyContacts_FormRegionShowing(object sender, System.EventArgs e)
        {
        }

        // Occurs when the form region is closed.
        // Use this.OutlookItem to get a reference to the current Outlook item.
        // Use this.OutlookFormRegion to get a reference to the form region.
        private void FormRegionMyContacts_FormRegionClosed(object sender, System.EventArgs e)
        {
        }
    }
}

Şimdi uygulamamız üzerinde herhangi bir değişiklikte bulunmadığımız haliyle çalıştıralım ve Outlook içerisinde nasıl bir görünüm alacağına göz atalım. Oluşturulmuş olan proje derlendiğinde Outlook 'un otomatik olarak açılacak ve geliştiricinin gerekli testleri yapması için beklemeye başlayacaktır. Daha sonra derleme işlemi sonlandırıldığında Outlook uygulaması da kendisini otomatik olarak kapatacaktır. Biz uygulamamızı kontaklar içerisine eklenmesini istemiştik. Bakalım istediğimiz gerçekleşmiş mi?



Oldukça başarılı... İstediğimiz sonucu almışa benziyoruz. Peki biz bu Add-in 'nin üzerine tıkladığımızda karşımıza nasıl bir form görüntüsü gelecek. Tabii ki boş. Çünkü hiç bir düzenleme işlemi yapmamıştık. Şimdi ise eklemiş olduğumuz Add-in 'i nasıl geliştirebileceğimizi incelemeye çalışalım.

Örneğimizde aynı kategori içerisinde yer alan tüm kişileri göstermek istiyoruz. Yazımızın başında da bahsettiğimiz üzere kontak listemizin hepsini sadece resimleri ile göstermek istiyoruz. bu işlemi gerçekleştirebilmek için OutlookItem nesnesini kullanmamız gerekmektedir. Eriştiğimiz Outlook öğesinin alt özelliklerinde ContactItem 'a erişeceğiz ve bütün kontakları sorunsuzca kullanıcılara getirebileceğiz. Eğer kontaklarımızda kategori uygulanmış ise biz kişileri gösterirken bu kategoriye de göz önüne alarak bir işlem yapmamız gerekmektedir. Bu işlem için string filtre uygulamak mantıklı bir işlem olacaktır. Örneğin Outlook kontakları içerisinde yer alan arkadaşlar grubuna ilişkin kayıtları göstermek istiyorum. Bu noktada yapılması gereken bir ListView içerisine Contact nesnesinin içerisinde yer alan resim özelliklerini bu kontrolde doldurmak olacaktır. Genel olarak istediklerimizi açıkladığımıza göre işlemlerimizi yapmaya devam edebiliriz.

İlk olarak filtre hazırlama işlemini nasıl gerçekleştireceğimize göz atalım.

FormRegionMyContacts.cs
/// <summary>
/// Filtreleme işlemi için yapıldı.
/// </summary>
/// <param name="messageClass">İstenen mesaj sınıfı</param>
/// <param name="categoryList">kontaklar için kategori listesi</param>
/// <returns></returns>
string BuildSQLFilter(string messageClass, string[] categoryList)
{
    //StringBuilder nesnesinin kullanıyoruz.
    StringBuilder filter = new StringBuilder(250);
    //SQL Prefix (Ön eki)
    filter.Append(@"@SQL(");
    //MessageClass yardımı ile duruma özel mesaj verdirir.
    filter.Append(string.Format(@"(""http://schemas.microsoft.com/mapi/proptag/0x001a001e"" = '{0}')",
                        messageClass));
    //Kategori var mı diye kontrol eder ve sonrasında AND koşulunu uygular.
    if (categoryList.Length > 0) filter.Append("AND(");
    //Kategori var mı diye kontrol eder ve sonrasında AND koşulunu uygular.
    for (int index = 0; index < categoryList.Length; index++)
    {
        if (index > 0) filter.Append("OR");
        filter.Append(string.Format(@"(""urn:schemas-microsoft-com:office:office#Keywords"" LIKE '%{0}%')",
                                                    categoryList[index].Trim()));

    }
    //AND blogunun sonu
    if (categoryList.Length > 0) filter.Append(")");
    //SQL blogunun sonu
    filter.Append(")");
    return filter.ToString();
}

Kontaklar içerisinde yer alan kategorilerden isteğimize erişebilir ve içinde yer alan kayıtları görebilir duruma gelmiş bulunuyoruz. Şimdi ise filtre sonucunda gelecek olan kontakları yavaş yavaş göstermeye başlayalım. Outlook dosyaları Outlook.MAPIFolder içerisinde tutmaktadır. Bu durumda dosyalara erişmek istediğimizde bu nesneyi kullanmamız gerekecek. Ayrıca ListView için tablo kullanmak gerekecek. Outlook.Table nesnesini kullanarak bu işlemi de gerçekleştirebilmemiz mümkündür. Ayrıca dikkat etmemiz gereken bir nokta var. Biz bu işlemleri yaparken arka planda çalışmasını sağlayacak bir yapıda kurmamız gerekmektedir. Bu işlemi de uygulamamız esnasında hazırlıyor olmamız gerekmektedir.

Kontaklara eriştikten sonra verilere ulaşmak için kullandığımız yöntem aşağıdaki gibi olacaktır.

FormRegionMyContacts.cs
private readonly Size _imagesize = new Size(50, 75);

// Occurs before the form region is displayed.
// Use this.OutlookItem to get a reference to the current Outlook item.
// Use this.OutlookFormRegion to get a reference to the form region.
private void FormRegionMyContacts_FormRegionShowing(object sender, System.EventArgs e)
{
    Outlook.ContactItem contact;
    Outlook.MAPIFolder folder;
    Outlook.Table table;
    try
    {
        //Outlook ContactItem 'dan değerleri getiriyor...
        contact = this.OutlookItem as Outlook.ContactItem;
        //kontak klasörünü nesne olarak getirir.
        folder = contact.Parent as Outlook.MAPIFolder;

        //kontakları kategorilere göre getirir.
        string categories = contact.Categories ?? string.Empty;
        string[] categoryList = categories.Split(';');

        //Hazırlanmış olan SQL filtre metoduna göre gerekli işlemler gerçekleştiriliyor.
        string filter = BuildSQLFilter("IPM.Contacy", categoryList);
        //filtrelenmiş değerleri tabloya dolduruyoruz.
        table = folder.GetTable(filter.ToString(), Outlook.OlTableContents.olUserItems);
        //Gelen değerleri tablo içerisine kolonlara doldurulur.
        table.Columns.RemoveAll();
        table.Columns.Add("EntryID");
        table.Columns.Add("FileAs");
        table.Columns.Add(@"urn:schemas:contacts:profession");
        table.Columns.Add("Email1Address");
        table.Columns.Add(@"urn:schemas:contacts:businesshomepage");
        table.Columns.Add(
                    @"http://schemas.microsoft.com/mapi/id/{00062004-0000-0000-C000-000000000046}/8062001F");
        table.Columns.Add("Categories");
        // MAPI özelliklerinden resim değerlerini nasıl alacağını göstereceğiz.
        table.Columns.Add(
                    @"http://schemas.microsoft.com/mapi/id/{04200600-0000-0000-C000-000000000046}/8015000B");
        // resim listesi oluşturma ve Resim yok görüntüsnün eklenmesi
        ImageList imageList = new ImageList();
        imageList.ImageSize = _imagesize;
        imageList.ColorDepth = ColorDepth.Depth24Bit;
        //resim listesinde resim yoksa listview üzerinde yer alan resimler resim yok görüntüsünün gözükmesi
        listviewContacts.LargeImageList = imageList;
        imageList.Images.Add(string.Empty, Properties.Resource.NoPicture);

        //Kontaklar ListView 'ın içerisine resimleri ile birlikte dolduruluyor...
        List<string> contactsWithPicture = new List<string>();

        //Outlook kontaklarında Business Card olarak girilmiş olan bilgilerde yer alan
        //resimlerin görütülenmesini sağlamaktadır.
        while (!table.EndOfTable)
        {
            Outlook.Row row = table.GetNextRow();
            //satır bilgileri dolduruluyor.
            ContactInfo info = new ContactInfo(row);
            if (info.HasPicture)
            {
                contactsWithPicture.Add(info.EntryId);
            }
            if (contact.EntryID != info.EntryId)
            {
                ListViewItem listViewItem = this.listviewContacts.Items.Add(info.FileAs, 0);
                listViewItem.Tag = info;
                listViewItem.Name = info.EntryId;
                listViewItem.ToolTipText = info.EmailAddress;
            }
            else
            {
                UpdateBusinessCard(info, GetContactPicture(info.EntryId));
            }

            //uzun süren çalışmaların için arka planda çalışabilme özelliği
            _backgroundWorker = new BackgroundWorker();
            _backgroundWorker.WorkerSupportsCancellation = true;
            _backgroundWorker.DoWork += new DoWorkEventHandler(_backgroundWorker_DoWork);
            _backgroundWorker.RunWorkerAsync(contactsWithPicture);
        }
    }
    finally
    {
        table = null;
        folder = null;
        contact = null;
    }
}

Şimdiye kadar kontaklara ilişkin verileri getirecek ve resim yoksa görüntü yok ekranını gösterecek, ayrıca çok fazla veri gelecekse bu çalışmayı ekran yüklenirken değil de arka planda çalışabilmesine olanak verecek yapının hazırlanması için temel adımları gerçekleştirmiş bulunuyoruz. Şimdi ise yukarıdaki kod bloğunda kullanmış olduğumuz metotların ve event ların içeriklerini doldurma işlemini gerçekleştirelim. Bu amaçla yapacağımız ilk işlem BusinessCard kontak tipini eklenen resimlerin ListView içerisinde gözükmesi olacaktır.

FormRegionMyContacts.cs
private Image GetContactPicture(string entryID)
{
    var contact =
        Globals.ThisAddIn.Application.Session.GetItemFromID(entryID, null) as Outlook.ContactItem;
    Image img = null;
    string tempPath = Environment.GetEnvironmentVariable("TEMP");
    if (contact != null)
    {
        foreach (Outlook.Attachment attachment in contact.Attachments)
        {
            if (attachment.FileName == "ContactPictıre.jpg")
            {
                string fileName = Path.Combine(tempPath, entryID + ".jpg");
                attachment.SaveAsFile(fileName);
                FileStream stream = new FileStream(fileName, FileMode.Open);
                Bitmap bmp = new Bitmap(Image.FromStream(stream, true));
                if (bmp.Width > bmp.Height)
                {
                    Bitmap tempBmp = new Bitmap(_imagesize.Width, _imagesize.Height);
                    Graphics g = Graphics.FromImage(tempBmp);
                    g.FillRectangle(Brushes.White, 0, 0, _imagesize.Width, _imagesize.Height);
   
                    float ratio = (float)bmp.Height / bmp.Width;
                    int newHeight = (int)(ratio * bmp.Height);
                    int top = (_imagesize.Height - newHeight) / 2;
                    g.DrawImage(bmp, new Rectangle(0, top, _imagesize.Width, _imagesize.Height));
                    img = tempBmp;
                    g.Dispose();
                }
                else
                {
                    img = new Bitmap(bmp, _imagesize);
                }
                    stream.Dispose();
                    File.Delete(fileName);
                    break;
            }
        }
    }
    contact = null;
    return img;
}

Arka plan çalışma işlemlerinin detaylarını girelim şimdi de. İlk olarak resimlerin çalışma prensipleri ile alakalı işlem yapmamız gerekmektedir. Hatırlayacağınız üzere BusinessCard görünümünde yer alan resimleri EntryId 'lerinden yararlanarak resimlerini göstereceğimizden bahsetmiştik. Şimdi bu işlemi gerçekleyecek olan kod bloğunu ekleyelim.

FormRegionMyContacts.cs
private void listViewContacts_DoubleClick(object sender, EventArgs e)
{
    Point position = listViewContacts.PointToClient(Control.MousePosition);
    ListViewItem listViewItem =
    listViewContacts.GetItemAt(position.X, position.Y);
    if (listViewItem == null) return;

    OpenItem(listViewItem.Name);
}

private void OpenItem(string entryId)
{
    Outlook.ContactItem contact = Globals.ThisAddIn.Application.Session.GetItemFromID(entryId, null) as Outlook.ContactItem;
    contact.Display(false);
    contact = null;
}

private void listViewContacts_ItemMouseHover(object sender, ListViewItemMouseHoverEventArgs e)
{
    UpdateBusinessCard((ContactInfo)e.Item.Tag, listViewContacts.LargeImageList.Images[e.Item.ImageIndex]);
}

Metotlarımızı hazırlarken çok sık olarak ContactInfo sınıfını oluşturalım. Bu sınıf içerisinde BusinessCard içerisinde yer alan iletişim bilgilerine ilişkin elemanlar yer almaktadır.

ContactInfo.cs
namespace Contacts
{

public class ContactInfo
{

    /// <summary>
    /// Outlook satır ve sütunlarında yer alan bilgiler için kullanırlar
    /// </summary>
    /// <param name="row"></param>
    public ContactInfo(Microsoft.Office.Interop.Outlook.Row row)
    {
        EntryId = (string)row[1];
        FileAs = (string)row[2];
        JobTitle = (string)(row[3] ?? string.Empty);
        EmailAddress = (string)(row[4] ?? string.Empty);
        Homepage = (string)(row[5] ?? string.Empty);
        MessengerAddress = (string)(row[6] ?? string.Empty);
        string categories = (string)(row[7] ?? string.Empty);
        Categories = categories.Split(';');
        HasPicture = (bool)(row[8] ?? false);
    }

    public string EntryId { get; private set; }
    public string FileAs { get; private set; }
    public string JobTitle { get; private set; }
    public string EmailAddress { get; private set; }
    public string Homepage { get; private set; }
    public string MessengerAddress { get; private set; }
    public string[] Categories { get; private set; }
    public bool HasPicture { get; private set; }
    }
}

Sürekli olarak kod tarafında neler yapacağımıza göz attık. Peki bu hazırlamış olduğumuz tasarımın tasarım ekranı nasıldır?



Tasarım tarafında keyfimize istediğimiz .Net kontrolünü kullanabilmemiz mümkündür. Sıra kontak bilgilerinin gösterilmesine geldi. Son kullanıcı resmin üzerine çift tıklama ya da başka bir işlem yaptığında kontak eklediği ekran açılarak değişiklik yapabilmesine olanak tanıyacağız.

FormBusinessCard.cs
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Diagnostics;
using Microsoft.Office.Interop.Outlook;
using Outlook = Microsoft.Office.Interop.Outlook;
using System.Runtime.InteropServices;
using System.Reflection;

namespace Contacts {
    public partial class FormBusinessCard : UserControl {


        public FormBusinessCard() {
            InitializeComponent();
        }

        /// <summary>
        /// BusinessCard dan bilgileri alarak doldurur
        /// </summary>
        /// <param name="info">ContactInfo nesnesi.</param>
        /// <param name="image" >Kontakın resmi</param>
        public void SetContactInfo( ContactInfo info , Image image){
            EntryId = info.EntryId;
            LastFirst.Text = info.FileAs;
            Profession.Text = info.JobTitle;
            Emailaddress.Text = info.EmailAddress;
            Messenger.Text = info.MessengerAddress;
            Homepage.Text = info.Homepage;
            Categories.Text = string.Join("\n", info.Categories);
            Image.Image = image;
        }


        /// <summary>
        /// Outlook ContactItem 'dan EntryId 'yi getirir
        /// </summary>
        public string EntryId { get; set; }
       
        /// <summary>
        /// BusinessCarda çitt tıkladığında olacak işlemler uygulanır.
        /// </summary>
        private void FormBusinessCard_MouseDoubleClick(object sender, MouseEventArgs e) {
            OpenItem(EntryId);
        }

        /// <summary>
        /// Detayları ile kişinin kontak bilgilerini getirir.
        /// </summary>
        /// <param name="entryId">Kontağın entryId 'si</param>
        void OpenItem(string entryId) {
            Outlook.ContactItem contact = Globals.ThisAddIn.Application.Session.GetItemFromID (entryId , null) as Outlook.ContactItem ;
            contact.Display(false);
            contact = null;
        }

        /// <summary>
        /// Ana ekrana link veren link clicked
        /// </summary>
        private void Homepage_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) {
            ShowHomePageInBrowser(Homepage.Text);
        }

        void ShowHomePageInBrowser(string url){
            Process p = new Process();
            p.StartInfo.FileName = "IExplore.exe";
            p.StartInfo.Arguments = url;
            p.Start();
        }

        /// <summary>
        /// Live messenger 'a tıklandığında kontak ile chat penceresinde konuşulabilmesini sağlar
        /// </summary>
        private void Messenger_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) {
            ConnectToMessenger(Messenger.Text);
        }

        /// <summary>
        /// Email adresini kullanarak anlık mesajlaşma (messenger) penceresi açar.
        /// </summary>
        /// <param name="email">the emailaddress of the partner</param>
        void ConnectToMessenger(string email) {
            try{
                // Messenger oluşturuluyor.
                Type messengerType = Type.GetTypeFromProgID ("Messenger.UIAutomation.1");
                object comObject = Activator.CreateInstance (messengerType);
   
                // e-mail adreslerine göre kişiler konuşma penceresine davet ediliyor.
                object[] arguments = new object[] { email };
                messengerType.InvokeMember ("InstantMessage", BindingFlags.InvokeMethod,null, comObject, arguments);
                Marshal.ReleaseComObject(comObject);


                } catch(System.Exception ex){
                    MessageBox.Show("Please make sure you have installed the latest Windows Messenger Live and that you are signed-in." );
            }
        }

        private void Emailaddress_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) {
            SendEmail(LastFirst.Text, Emailaddress.Text);
        }

        /// <summary>
        /// mail adresi oluşturulması isteniyor.
        /// </summary>
        /// <param name="emailAddress">kontakların mail adresleri</param>
        void SendEmail(string name,string emailAddress) {
            Outlook.MailItem mail = Globals.ThisAddIn.Application.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olMailItem) as Outlook.MailItem ;
            mail.Recipients.Add (string.Format("{0}<{1}>", name, emailAddress ));
            mail.To = emailAddress;
            mail.Display(false);
            mail = null;
        }
    }
}

Evet artık geliştirme süreçlerimizi tamamlamış oluyoruz. Sıra test yapmaya geldi. Test için yapmamız gereken uygulamamızı çalıştırmak olacaktır. Çalışma esnasında Outlook 2007 (Outlook 2010 üzerinde de sorunsuzca çalışmaktadır.) uygulaması açılacak ve eklemiş olduğumuz Add-in bizim kullanımımız için hazır durumda bulunacaktır.



Uygulamamız sorunsuz bir şekilde çalıştı ve isteklerimizi yapar duruma gelmiştir.

Yazımızı sonlandırmadan önce bu yapmış olduğumuz Add-in 'i son kullanıcıların bilgisayarlarına kurmak istediğimiz ne tür işlemler uygulamamız gerektiğine değinelim. Yazımızın içerisinde de bahsettiğimiz üzere VSTO == Visual Studio Tools for Office 'tir. Bu uygulama tipi ile hazırlanmış örnekler ise *.vsto uzantısı ile oluşturulmaktadır. Eğer ki bilgisayarlarda Visual Studio 2008 kurulu değilse bilgisayarlar vsto uzantısını tanımayacaktır. Bu durumda yapmamız gereken Microsoft Visual Studio Tools for the Microsoft Office System Runtime uygulamasını bilgisayarlara kurmak olacaktır. Sonrasında bilgisayarlar VSTO uzantısını algılayacak ve kurulum işlemini gerçekleştirecektir.

Kurumlar içerisinde çalışan geliştiriciler için en büyük sorun ise bilgisayarlara herhangi bir uygulama kurulmasına izin verilmemesinden kaynaklanmaktadır. Bu uygulama kurulurken ne yapmamız gerekmektedir peki. Bilgisayarlara ilk olarak Admin kullanıcı ile VSTO Runtime 'ı kurmamız gerekmektedir. Sonrasında ise oluşturmuş olduğumuz uygulamayı kurmak istediğimizde herhangi güvenlik engeline takılmadan kurabilmemize olanak tanınacaktır. Peki Runtime kurulurken admin kullanıcısı hakkı isterken neden uygulama kurulurken böyle bir istekte bulunmamaktadır. Çünkü VSTO uygulamaları çalışacakları Office uygulamasını doğrular ve sonrasını kendisini ekler. Yani kurmaz. Bu sebepten ötürüde kurulum esnasında herhangi bir güvenlik kontrolüne takılmayacaktır.

SoSonuç olarak Visual Studio üzerinde VSTO 2008 uygulamasını ne için oluşturacağımızda nasıl oluşturacağımıza kadar örnek üzerinde incelemeye çalıştık. Sonrasında ise geliştirmiş olduğumuz uygulamanın son kullanıcıların bilgisayarlarına nasıl kurulacağını hem kurum bazında hem de küçük işletmeler bazında değinmeye çalıştık.

Uygulamanın kaynak kodlarına buradan erişebilmeniz mümkündür.

Umarım yararlı olabilmiştir.

Turhal Temizer

info@turhaltemizer.com