Makale Özeti

Bu yazımızda VSTO 2008 ile Add-in oluşturmak istediğimizde ne gibi işlemler yapmamız gerektiğini örnek üzerinden açıklamaya çalışıyoruz.

Makale


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.

Umarım yararlı olabilmiştir.

Turhal Temizer

info@turhaltemizer.com
http://turhal.blogspot.com
Turhal Temizer 'in Blogu

Uygulamanın kaynak kodlarınaerişebilmeniz mümkündür.