Öncelikle şunu söylemekte faydar var. VS, VSTO kodlaması adına birçok işi bizim yerimize yapmış olacak. Bizim bilmemiz gereken 3 temel şey var
Excel Object Modelle uğraşmayı bi önceki sayfada gördük.
.Net çok büyük bir dünya. Aynı anda hem VS'yu kullanmayı öğrenmeniz hem de c#(veya Vb.Net) kullanmayı öğrenmeniz gerekecek. Bu uzun bir yolculuk olacak. Ben başlangıç için gerekli olan kısmı bu sitede vereceğim, kendinizi daha ileri götürmek size kalmış.
Geriye bence en önemli kısım, çalışmanızın vitrini olan Ribbon yönetimi kalıyor. İşte bu sayfada bu konuya detaylarıyla(başka hiçbir kaynakta olmayan detaylarıyla) bakacağız.
Ribbon Controlleri
Kapsayıcılar(Container elementler)
Ana kapsayıcımız Group controlüydü, bunu yukarıda gördük,
tüm controlleri bunların içine koymak durumundayız. Bunun dışında bir de alt kapsayıcılar var, bunlar Group nesnesi içindeki contolleri gruplamaya yararlar. Özellikle mantıksal(logical) bir gruplama amacıyla kullanılırlar.
İki tür kapsayıcı kontrol var. Biri Buttongroup, diğeri Box. Buttongroup sadece button benzeri controlleri(button, toggle buton, split button) ve içine buton konulan popup menüleri(menü ve gallery) alır ve bunları dizerken, Box controlü ise her tür controlü içine alır ve hem yatay hem dikey hizalayabilir. Aşağıdakilerden kırmızı olan Buttongroup, mavi olanlar Box controlüdür. Box'ın BoxStyle propertysi ile yatay/dikey ayrımı belirlenir. Bu arada her ne kadar design görünümünde etraflarında bi border çizigisi varmış gibi görünse de derlendiğinde bu çizgiler görünmez, o yüzden bi alttaki separatorleri kullanmak gerekebilir.
Box nesnesinin Items adlı bir collectionı vardır. Bu demektir ki, içindeki controlleri döngüyle dolaşabilirsiniz. İhtiyacınız olur diye söylüyorum.
Seperator
Anlamı açık diye tahmin ediyorum. Kontroller arasına ayraç koyar. Böylece aynı group içindeki kontrollerin birbirine çok yaklaşması engellenmiş olur. Yalnız, bazen design görünümünde taşma v.s görünse bile proje derlendiğinde normal görünüm alabiliyor. O yüzden nihai görünümü, çalıştırmadan bilemezsiniz diyebiliriz.
Toggle button
VBA'den biliyorsunuz.
Label ve EditBox
Label'ı açıklamaya gerek yok, VBA'den biliyorsunuz. EditBox da bildiğimiz TextBox aslında. VBA'de add-in'ler ve menüler konusunda da bahsetmiştim, bir textboxın(editboxın) ribbonda/menüde olması çok pratik bi uygulama değil bence. O yüzden şimdilik es geçiyorum.
Checkbox
Checkbox da VBA'deki gibi, açıklamaya pek gerek yok, gerçi pratikte bir kullanımı olur mu ondan da emin değilim. Bu arada checkbox'lara benzeyen radiobutton'lar Ribbonlarda kullanılamıyor.
Pop-up menüler
Menü
Menülere VBA'den aşinayız ama yine de nasıl kullanılacağını bi görelim.
Bunun için burdan itibaren ilk tab'ımızı kullanmaya başlayalım. Buna yeni bir grup ekleyelim ve adına Dosyalar diyelim. Buraya sık kullandığımız bazı dosyaları koyacağız. Normalde zaten Windows'un böyle bir özelliği var, mesela Excele sağ tıkladığınızda sık kulalndıklarınızı üst tarafa pinleyebiliyorsunuz ama biz böyle bir özellik olmadığını varsayalım ve sık kullanılan dosyalara ait bir menü yapalım. Bu arada buraya farklı dosya tiplerini de ekleyebilirsiniz(word, pdf, ppt v.s). Hatta ileride göreceğimiz gibi bunlar başka programları çalıştıran butonlar da olabilir.
Bunların click eventine de ilgili dosyaları açan kodu yazarız.
private void button13_Click(object sender, RibbonControlEventArgs e)
{
string adres = @"E:\OneDrive\Dökümanlar\bütçem.xlsx";
MyStatik.app.Workbooks.Open(adres);
}
Menü içine farklı resim-yazı kombinasyonunda butonlar ve başka menüler de konabilir.
Burada karşımızda birkaç problem durabilir
- 10 tane dosya varsa, 10'u için de ayrı ayrı mı click eventi gireceğiz.(Hayır tabiki, detaylar için Excelent proje incelemesinde)
- Dosyaların bazısı Excel dosyası değilse?(Teoriği .Net işlemlerinde, pratiği Excelent projesinde)
- Biz şuan sabit bir dosya listesi girdik, ya Kullanıcılar bunları kendi isteği gibi özelleştirmek isterse? (Aşağıda dialog launcher bölümünde settings işlemlerini yapmayı göreceğiz, açıklamalı örneği ise yine Excelent Projesinde göreceğiz)
ItemSize özelliği ile menünün içindeki elemanların boyutunun küçük mü büyük mü olacağı belirlenir. Diğer özellikleri aşağıdaki ortak özellikler içinde göreceğiz.
NOT: Bu arada butonlara atadığınız resimler/iconlar design modda görünmez. Bunların nasıl göründüğünü test etmek için projeyi çalıştırmanız gerekiyor. Ayrıca bazen buton orada olduğu halde yokmuş gibi de görünebiliyor. Bu tür durumlarda Ribbon'u kapatıp açın veya komple projeyi kapatıp açın.
Gallery
Menülere göre daha şık bir gruplama sağlarlar. Aşağıda Excel'in kendi menülerinden birinde Gallery kullanımı görüyoruz.
Gallery içine buton sürüklemek yerine, properties'ten Items collection'ına elemanlar ekliyoruz. Sonra da bunların resimleri için değer gireceğiz. Bunları az aşağıda ortak özellikler bölümünde göreceğiz. Biz şimdi resim eklemeden kodumuzu yazalım. Burada farklı butonlar olmadığı için aslında tek bir event var, o da gallerynin tıklanma eventi.
Yapacağımız şey, seçilen elemanını indeksine bakmak olacaktır.
private void gallery1_Click(object sender, RibbonControlEventArgs e)
{
switch (this.gallery1.SelectedItemIndex)
{
case 0:
MessageBox.Show("ilk item seçildi");
break;
case 1:
MessageBox.Show("ikinci item sçeildi");
break;
default:
MessageBox.Show("Buraya gelmemeli");
break;
}
}
Burada ayrıca switch-case yapısını da görmüş olduk. VBA'deki seleckt case yapısının aynısıdır. Kullanımındaki küçük ayrımı keşfetmeyi size bırakıyorum.
ItemImageSize özelliği ile itemların ebatları rakam verilerek belirtilir. Menülerde ise küçük/büyük seçenekleri vardı.
ComboBox ve DropDown
ComboBox'ların ne olduğunu biliyoruz. Dropdown'lar bunlara çok benzer; tek farkı, Dropdownlara tıkladığımızda metnin içini seç(e)meyiz, Combolarda ise metni seçmiş oluruz ve gerekirse elle bişeyler yazabiliriz. Ben dropdownları seviyorum, neresine tıklarsam tıklayayım seçenekler geliyor, comboda ise metinli bölgeye tıklarsanız oraya girmiş oluyorsunuz, seçimlerin açılması için illa yandaki oka tıklamak gerekiyor.
Bu anlamda
dropdownlar gallery'lere benziyor. Aradaki fark, dropdowndakiler hep altalta bir liste şeklinde açılırken galeryde ise bi grid(ızgara) şeklinde yerleşim sözkonusu.
Splitbutton
ButtonType=Button atanırsa aşağıdakilerden ilki gibi, Toggle seçilirse ortadaki gibi görünür.
Diğer popuplardan farkı şu: Diğerlerinde seçeneklerden birini seçmek için açılır kutuya basmamız gerekir, bunda ise görünen metnin kendisi de bir seçenektir, görünür metin default seçenek olup buraya genelde en olası seçenek yazılır, böylece hiç açılır kısmı açmadan doğrudan seçim yapılabilir. Diğer seçenekler için açılır kutuyu açmak gerekir. Dolayısıyla splitbuttonun ve içindeki butonların eventleri birbirinden ayrıdır(split ifadesi bundan dolayıdır).
Bu arada Excel'in built-in menülerinden örnek vermek gerekirse, Home menüsündeki Paste butonu bi Splitbuttondur. Doğrunda Paste'e tıkladığınızda bir seçimi olduğu gibi yapıştırır, altındaki açılır kutuya tıklayıp başka bir seçim de yapabilirsiniz.
Resource Ekleme
Gerek ribbondaki controllere, gerek formlarımızda kullanacağımız resimleri projenin içine dahil etmek istiyorsak aşağıdaki adımları ekleyerek ilerelyebilriz.
Buraya icon dosyaları(ico), ses ve video dosyaları da ekleyebiliyorsunuz.
Sonrasında, eklediğimiz dosyaları herhangi bir kontrolün Image properties'ine kolaylıkla ekleyebiliyoruz.
Hatta bu imajlara daha sonra runtime sırasında aşağıdaki gibi de ulaşabiliyoruz.
Bu arada ico uzantılı dosyaları Image porperty'sine atamak için aşağıdaki gibi bir kod yazmak gerekiyor.
Resource'dan ico dosyasını(_0.ico) seçiyoruz, sonra onu Bitmap'e çeviriyoruz. Ben bunu Ribbon_Load eventine yazdım, böylece ribbon yüklenir yüklenmez imaj ataması yapılmış oluyor.
button20.Image = Properties.Resources._0.ToBitmap();
Vb.Net'te bu kısım biraz farklı yazılıyor.
button20.Image = My.Resources._0.ToBitmap()
Ortak Properties
Burda birçok nesnenin ortak property'sine bakacağız.
ControlSize
İlgili controlün küçük mü yoksa büyük mü görüneceğini belirler. Hali hazırda Excel'de iki türü de görüyorsunuz. Mesela Home menüsündeki Paste butonu Large bir controldür. Hemen yanındaki Cut ise Regular(küçük) bir controldür. Yalnız dikkat, bir control ButtonGroup veya bir Pop-up menü içindeyken bu özellik görünmez(erişilebilir değildir) ve mecburen küçük(regular) olur. Bu boyutu, ilgili kontrol için seçilen resmin pixelinden bağımsız olarak ekranda kapladığı alan olarak düşünün. Bu yüzden küçük ölçülerde boyutlanmış bir resim Large bir controlün Image property'sine atandığında pixel pixel(tabiri caizse Minecraft karakterleri gibi) görünürken, büyük boyutlu bir resim ise küçük(regular) bir controle atandığında buruşuk(pixellerin üstüste binmesinden dolayı) görünecektir.
Image, OfficeImageId, ShowImage, ShowLabel
Bu 4 özellik birbiriyle bağıntılı olduğu için bir arada aldım.
Bir control için görsel bir etiket istiyorsak, bunun için Image özelliği için bir resim seçip atarız veya OfficeImageId özelliğine bir değer atarız. OfficeImageId değerlerini internette bulabilirsiniz. Gerçi Microsoft'un kendi sitesindeki link hata veriyor, belki ileride düzeltirler diye ben yine de bu linki buraya koyuyorum. Alternatif siteler ise şöyle:
Bir şekilde hem Image hem OfficeImageId belirlediyseniz Image baz alınır.
ShowLabel: Controlün etiketi görünecek mi görünmeyecek mi, bunu belirler. ControlSize, Large set edilmişken False atanamıyor.
ShowImage:Control'e atanmış resmin gösterilp gösterilmeyeceği belirlenir. ControlSize'da olduğu gibi, bir control bir buttongroup veya menü içindeyken bu özellik erişlebilir durumda değildir.
Pop-up menülerin bir de ShowItemLabel ve ShowItemImage özellikleri var ki, bunlar içindeki elemanlar için geçerlidir.
Image değeri verilmişken ShowImage=False denirse resim görünmez. OfficeImageID vermişseniz ShowImage=False yapamazsınız. Bunlarla oynayıp denemeniz gerek. Çeşitli kombinasyonlar çıkabiliyor, resimli-yazısız, resimli-yazılı, resimsiz-yazılı, küçük resimli, büyük resimli v.s
Aşağıda benim çeşitli denemelerimi görebilrisiniz.
Screentip ve Supertip
Sırayla, bir controlün üstüne gelindiğindeki başlık ve detay bilgileri verdiğiniz propertylerdir.
Gallery itemlarında bunlar her item için ayrı ayrı ayarlanabiliyor.
Bu arada bu screentip içine resim de eklenebiliyor ancak bunu yapmak için ribbonu Visual Designer ile değil Xml olarak yaratmamız lazımdı. Aradaki fark için en başta yazdığım açıklamaya tekrar bakın.
Dialog Launcher ekleme
Aşağıdaki Home menüsünün köşesindeki gibi küçük butonlara Dialog Launcher deniliyor. Bunlara tıklandığında ya bir dialog kutusu çıkar veya bir Taskpane, ve bunlarda çeşitli ayarlar yapılır. Bu ayarların bir kısmı tek seferliktir, bir kısmı ise File>Options'taki gibi kalıcı ayarlardır. Şimdi bunlar nasıl yaplır, ona bakaczğız.
Ribbonumuzda tab2 sekmemizinde ilk group nesnesini(group2) seçtikten sonra properties'ten aşağıdaki gibi Add DialogLauncher diyelim.
Buna tıklayınca group nesnesinin köşesine bu butoncuk eklenir. Şimdi sıra, buna tıklandığında ne yapılacağını belirlemeye geldi. Aşağıdaki gibi yıldırım butonuna tıklayarak açılan eventler kısmı boşken, çift tıklayınca otomatik bir event handler yaratılır ve kod sayfasında bunu size gösterir.
Nasıl göründüğüne bakalım:
Şimdi frmSettings1 adında bir form oluşturalım ve dialog eventimize aşağıdaki kodu yazalım
private void group2_DialogLauncherClick(object sender, RibbonControlEventArgs e)
{
frmSetting1 f = new frmSetting1();
f.Show();
}
Settings formları olşuturma
Dialog Launcher'da açtığımız settings formunu düzenleyeceğiz.
Solution Explorer'da Properties'e çift tıklayalım.(VB.Net'te MyProject) ve açılan pencerede Settings'e gelelim. Şimdi iki ayar gireceğiz. Bunlardan biri bazı makrolar için kullancıdan kullanıcıya değişebilecek klasor adresi girmek, bir diğeri de yeni bir dosya açıldığında kaç sayfa açılacak, bunu kullanıcının belirlemesine izin vermek, bunu taibiki Files>Options'tan da yapabiliyoruz ancak maksat pratik olsun. Bu arada VBA'de iken bu tür ayarları txt dosyalarına yazıp okuyarak hallediyorduk, VSTO'da ise bu şekilde hem daha pratik hallediyoruz hem de şifre tarzı bilgileri de güvenlik altına almış oluyoruz.
Şimdi aşağıdaki iki ayarı girelim.(İlki için siz de uygun klasörü girin)
Şimdi Settings formumuz açılıdığnda ayarlarda kayıtlı ne varsa onu form içindeki textboxlarda gösterelim. Bunun için formda iki kutu yaratalım.
Bu formun Load eventine aşağıdaki kodu yazarak ayarları alıyoruz.
private void frmSetting1_Load(object sender, EventArgs e)
{
this.txtKlasor.Text = Properties.Settings.Default.Anaklasor;
this.txtSheetAded.Text = Properties.Settings.Default.YeniWbSheetAdedi.ToString();
}
Kullanıcı ayarları değiştirip kaydetmek isteyecektir. Bunun için bir kaydet butonu yaratmak yerine tüm MS Office uygulamlarının da yaptığı gibi, form kapandığında otomatik kayıt yapma mantığıyla hareket edelim. O yüzden formun eventlerine gidip FormClasing'e çift tıklayarak ilgili event handlerı oluşturuyoruz ve aşağıdaki kodu yazıyoruz.
private void frmSetting1_FormClosing(object sender, FormClosingEventArgs e)
{
Properties.Settings.Default.Anaklasor = this.txtKlasor.Text;
Properties.Settings.Default.YeniWbSheetAdedi = int.Parse(this.txtSheetAded.Text); //parsing, string bir ifadeden sayısal metin çıkarma işlemidir
Properties.Settings.Default.Save(); //bunu yazmazsak ayarlar kaydolmaz
}
Kodumuzu çalıştıralım, dialog launcherı çalıştıralım, çıkan pencerede değerleri değiştirip,
tekrar açtığımızda değişmiş olduğunu görüyüoruz.
Settings kontrollerini özelleştirme
Bu arada bazı kutuların ise ayarlara kaydolmasını istemez ama kullanıcının yine de ilgili oturum içinde
bunların içeriğini değiştirebilmesine izin vermek istersiniz. Kalıcı olmayan bu bilgilerle kalıcı yaptığınız ayarlı olanların birbirinden farklılaşması için ayarlı textboxların kırmızı fontlu olmasını sağlayabilirsiniz. Bunun için her ayarlı kutu için tek tek uğraşmak yerine TextBox sınıfından türetilmiş RenkliTextBox sınıfını yaratabiliriz. Bu kavrama Inheritance(Kalıtım) denir. Çok fazla örneğini görmeyeceğiz ancak özetle mantığı şu: Bir sınıfı baz alarak başka bir sınıf tanımlıyoruz. Böylece o sınıfın bütün özellikleri yeni sınıfımızda hazır olarak bulunuyor, biz sadece değiştirmek istediğimiz kısımları değiştiriyoruz veya eklemeler yapıyoruz. Hadi gelin şimdi de ona bakalım.
Projemize Add>New item diyerek UserControl ekliyoruz, adına RenkliTextBox diyelim. Açılan pencerede controlün içine bir tane textbox sürükleyelim. Sonraki kod sayfasına geçip constrcutor metod içindeki InitializeComponent satırını altına renklendirme kodumuzu yazalım.
public RenkliTextBox() //constructor metod
{
InitializeComponent();
this.textBox1.ForeColor = Color.Red;
}
Projeyi Rebuild edelim ve controlümüzün Toolboxa geldiğini görelim.(Rebuild= Clean + Build. Bazı işlemler için Rebuild gereklidir, yeni eklenen controllerin toolboxa eklenmesi de onlardan biri)
Bakalım istediğimiz gibi çalışıyor mu?
Sekmeler arası geçişler
Çalışmanızda birden fazla sekme yaratmış olabilirsiniz. Böyle bir durumda hepsini tek seferde göstermek yerine, ilk açılışta ana sekmeyi açıp, butonlar aracılığı ile de diğer sekmeleri açıp kapatabilirsiniz.
Mesela ana sekmemiz olan tab1'e koyacağımız bir toggle_buton'a aşağıdaki gibi kod yazabiliriz. Bunun için bir sekme daha yaratmanız gerekiyor, yani tab3. Butona tıklıyken tab3 görünecek ve aktif olacak, butona tıklı değilken tab3 gizlenecek.
private void toggleButton1_Click(object sender, RibbonControlEventArgs e)
{
Globals.Ribbons.Ribbon1.tab3.Visible = toggleButton1.Checked; //ribbonu görünür kılıyoruz
if (toggleButton1.Checked)
{
Globals.Ribbons.Ribbon1.RibbonUI.ActivateTab("tab3");//sonra da aktif hale getiriyoruz
}
}
Ribbonu kaldırma
Bir üstte, Ribbon sekmelerini nasıl gösterip gizlediğimizi gördük. Ancak bazen komple Ribbonu kaldırmak isteyebiliriz. Mesela
belli bir tarihe geldiğinizde Add-in'in kullanıcılardan kalkmasını isteyebilirsiniz. Bunun için Trial Version'u olan bir proje yapmak da çözümdür ancak bunun daha karmaşık olduğu aşikardır, onun yerine Ribbon'u kaldırarak kullanıcının arayüze dolayıyısla add-ine erişimini engellemiş olursunuz. Aşağıdaki kodu Ribbon_Load veya ThisAddIn_Startup içine yazabilirsiniz.
if (DateTime.Today>Convert.ToDateTime("31.12.2019"))
{
MessageBox.Show("Add'in in süresi dolmuştur.");
Globals.Ribbons.Ribbon1.Dispose();
}