Jeff Fisher, Google Data API'leri Ekibi
Ocak 2008
Giriş: Örneklemin Kapsamı
Örnek yürütülebilir dosyayı indirin.
Doküman Listesi Verileri API'si ile ilgili güzel şeylerden biri, geliştiricilerin Google Dokümanlar'a alışma sürecinde olan kullanıcılar için taşıma araçları oluşturmasına olanak tanımasıdır. Bu API'yi kullanmak için .NET istemci kitaplığını kullanarak "DocList Uploader" adlı bir .NET 2.0 yükleyici uygulaması oluşturdum. Yükleyenin kaynağını Subversion'dan alabilirsiniz.
Bu örnek, kullanıcıların belgelerini bilgisayarlarından Google Dokümanlar'a taşımasını kolaylaştırmak için hazırlanmıştır. Bu araç, kullanıcıların Google Hesaplarına giriş yapmalarına ve ardından desteklenen dosyaları sürükleyip bırakmalarına olanak tanır. Bu dosyalar daha sonra otomatik olarak yüklenir. Örnekte, Windows Gezgini kabuğuna sağ tıklama menüsü seçeneği ekleyerek dosya yükleme seçeneği de sunulmaktadır. Bu örnek, Apache 2.0 Lisansı kapsamında sağlanır. Bu nedenle, kendi programlarınız için başlangıç noktası olarak kullanabilirsiniz.
Bu makale, örnekteki davranışlardan bazılarının .NET Framework kullanılarak nasıl gerçekleştirildiğini göstermek için hazırlanmıştır. Çoğunlukla ilgili bölümlerdeki açıklama eklenmiş kod snippet'lerinden oluşur. Bu makalede, formların ve uygulamanın diğer kullanıcı arayüzü bileşenlerinin nasıl oluşturulacağı açıklanmamaktadır. Bu konuyla ilgili ayrıntılı bilgi veren birçok Visual Studio makalesi bulunmaktadır. Kullanıcı arayüzü bileşenlerinin nasıl yapılandırıldığını merak ediyorsanız istemci kitaplığını indirip "clients\cs\samples\DocListUploader" alt dizinine bakarak proje dosyasını kendiniz yükleyebilirsiniz.
Sistem tepsisi uygulaması oluşturma
Taşıma araçları genellikle işletim sisteminde fark edilmeden çalışabilir ve kullanıcının dikkatini fazla dağıtmadan işletim sisteminin yapabileceklerini genişletebilir. Windows'da bu tür bir aracı yapılandırmanın bir yolu, görev çubuğunu karıştırmak yerine sistem tepsisinden çalıştırmaktır. Bu sayede kullanıcılar, belirli bir görevi yerine getirmeleri gerektiğinde programı açmak yerine programı sürekli olarak çalıştırmaya devam edebilir. Bu örnekte, kimlik doğrulama kimlik bilgilerinin diske kaydedilmesi gerekmediği için bu fikir özellikle faydalıdır.
Sistem tepsisi uygulaması, öncelikle sistem tepsisinde (görev çubuğundaki saatin yanındaki bölge) yalnızca bir NotifyIcon ile çalışan uygulamadır. Bu tür bir uygulama tasarlarken projenin ana biçiminin, kullanıcının etkileşimde bulunacağı biçim olmasını istemediğinizi unutmayın. Bunun yerine, uygulama çalıştırıldığında görüntülenecek ayrı bir form oluşturun. Bunun nedeni birazdan açıklanacaktır.
Örneğimde iki form oluşturdum: HiddenForm (uygulamanın çoğu mantığı içeren ana formu) ve OptionsForm (kullanıcının bazı seçenekleri özelleştirmesine ve Google Hesabı'nda oturum açmasına olanak tanıyan bir form). Ayrıca HiddenForm'a DocListNotifyIcon adlı bir NotifyIcon ekledim ve bunu kendi simgemle özelleştirdim. HiddenForm'un kullanıcı tarafından görülmemesini sağlamak için opaklığını %0, WindowState'ini Minimized ve ShowInTaskbar özelliğini False olarak ayarladım.
Genellikle, bir program sistem tepsisinde çalışırken uygulamanın kapatılması programı durdurmaz. Bunun yerine, etkin formlar gizlenir ve yalnızca NotifyIcon görünür durumda kalır. Bunu yapmak için formumuzun "FormClosing" olayını aşağıdaki gibi geçersiz kılmamız gerekir:
private void OptionsForm_FormClosing(object sender, FormClosingEventArgs e)
{
if(e.CloseReason == CloseReason.UserClosing) {
this.Hide();
e.Cancel = true;
}
}Bildirim simgemiz olduğu için görev çubuğunda yer kaplamasına gerek yok. Bu nedenle, kullanıcı formu küçülttüğünde formu gizlemek de isteyebiliriz. Bu işlem için aşağıdaki kod parçası kullanılabilir:
private void OptionsForm_Resize(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
{
this.Hide();
}
}Artık kullanıcının OptionsForm'u kapatmasına izin vermediğimiz için HiddenForm'umuza bağlı tek bir örnek tutabiliriz. OptionsForm'u tekrar görüntülemek istediğimizde Show yöntemini çağırmamız yeterlidir.
Bu uygulamanın ana formu olan HiddenForm, kullanıcı tarafından görülemediği için kullanıcılara uygulamamızdan çıkma olanağı sunmamız gerekir. Uygulamayı kapatmak için ToolStripMenuItem ile NotifyIcon'a bir ContextMenu eklemeyi tercih ettim. Tıklama işleyiciyi yazmak basittir. HiddenForm'un Close yöntemini çağırmanız yeterlidir.
Balon İpuçları
Birçok sistem tepsisi uygulaması, NotifyIcon'dan kaynaklanan yuvarlak bir balon gibi görünen balon ipucu göstererek kullanıcıyla iletişim kurar. Balon aşağıdaki gibi gösterilebilir:
DocListNotifyIcon.ShowBalloonTip(10000, "Title", "Example Text", ToolTipIcon.Info);
İlk bağımsız değişken, kabarcığın milisaniye cinsinden ne kadar süreyle görüntüleneceğini belirtir. OS'nin bu alan için izin verdiği minimum ve maksimum sürelerin sırasıyla 10 ve 30 saniye olduğunu unutmayın. İkinci ve üçüncü bağımsız değişkenler, baloncuğun başlığını ve içeriğini belirtir. Son bağımsız değişken, balonun amacını göstermek için bir simge seçmenize olanak tanır.
Belge Yükleme
Belge yüklemek kolaydır. İşin büyük kısmı UploadDocument nesnesinin DocumentsService yöntemiyle yapılır. Bu süreç, Documents List API Geliştirici Kılavuzu'nda daha net bir şekilde açıklanmaktadır.
service = new DocumentsService("DocListUploader");
((GDataRequestFactory) service.RequestFactory).KeepAlive = false;
service.setUserCredentials(username, password);İlk olarak, DocumentsService nesnesi başlatılmalı ve kullanıcının kimlik bilgileri sağlanmalıdır. Birden fazla dosya yüklerken bazı sorunları önlemek için "keep-alive" HTTP üstbilgisi devre dışı bırakıldı. Bu üstbilginin .NET Framework ile ilgili bazı sorunlara neden olduğu bilinmektedir.
lastUploadEntry = service.UploadDocument(fileName, null);
Bu snippet, fileName dizesinde bulunan yoldaki dosyayı yükler. İkinci bağımsız değişkenin boş olması, Google Dokümanlar dosya adının orijinal dosya adıyla aynı olacağını gösterir.
Sürükleme ve Bırakma İşlemlerini Yönetme
Yükleme işlemini kolaylaştırmak için kullanıcının, dosyaları yüklemek üzere klasörlerinden uygulamaya sürükleyip bırakmasına izin vermek mantıklıdır. İlk adım, bir dosyadan bırakma işlemine izin vermektir. Aşağıdaki kod, bırakma işlemine izin verildiğini belirtmek için imleci değiştirir:
private void OptionsForm_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop, false))
{
e.Effect = DragDropEffects.Copy;
}
}Dosya veya dosya grubu bırakıldıktan sonra, bırakılan her dosyayı inceleyip yükleyerek bu etkinliği işlememiz gerekir:
private void OptionsForm_DragDrop(object sender, DragEventArgs e)
{
string[ fileList = (string[) e.Data.GetData(DataFormats.FileDrop);
foreach (string file in fileList)
{
mainForm.UploadFile(file);
}
}Dokümanları Listeleme
Sunucudan doküman listesi almak, kullanıcılara daha önce yüklediklerini hatırlatmak için iyi bir yöntemdir. Aşağıdaki snippet, sunucudaki tüm dokümanları almak için daha önce başlattığımız DocumentsService nesnesini kullanır.
public DocumentsFeed GetDocs()
{
DocumentsListQuery query = new DocumentsListQuery();
DocumentsFeed feed = service.Query(query);
return feed;
}Bu verileri görselleştirmenin kolay bir yolu ListView kullanmaktır. OptionsForm adlı yere DocList adlı bir ListView ekledim. Daha güzel görünmesi için çeşitli belge türlerini göstermek üzere özel bir ImageList de oluşturdum. Aşağıdaki kod, ListView'u yukarıdaki feed'den alınan bilgilerle doldurur:
public void UpdateDocList()
{
DocList.Items.Clear();
DocumentsFeed feed = mainForm.GetDocs();
foreach (DocumentEntry entry in feed.Entries)
{
string imageKey = "";
if (entry.IsDocument)
{
imageKey = "Document.gif";
}
else if (entry.IsSpreadsheet)
{
imageKey = "Spreadsheet.gif";
}
else
{
imageKey = "Presentation.gif";
}
ListViewItem item = new ListViewItem(entry.Title.Text, imageKey);
item.SubItems.Add(entry.Updated.ToString());
item.Tag = entry;
DocList.Items.Add(item);
}
foreach (ColumnHeader column in DocList.Columns)
{
column.AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
}
}imageKey değişkeni, ilişkili ImageList'te hangi resmin her satır için kullanılacağını belirler. Tag özelliği, dokümanda daha sonra işlemler yapmak için yararlı olabilecek orijinal girişi depolamak üzere kullanılır. Son olarak, AutoResize yöntemi, ListView'daki sütun genişliğini otomatik olarak biçimlendirmek için kullanılır.
Dokümanları tarayıcıda açma
Bu dokümanlar Google Dokümanlar'da depolandığından, kullanıcının dokümanı tarayıcısında görmesine izin vermek iyi bir fikirdir. Windows'da bunu yapmak için yerleşik bir işlev vardır:
using System.Diagnostics;
private void OpenSelectedDocument()
{
if (DocList.SelectedItems.Count > 0)
{
DocumentEntry entry = (DocumentEntry) DocList.SelectedItems[0].Tag;
Process.Start(entry.AlternateUri.ToString());
}
}
Burada, orijinal girişi Tag özelliğinden geri alıyoruz, ardından Process.Start işlevini çağırmak için seçilen dokümanın AlternateUri özelliğini kullanıyoruz. Geri kalan işlemler .NET Framework'ün sihirli gücüyle halledilir.
Kabuk İçerik Menüsü Ekleme
Kabuğun bağlam menüsüne öğe eklemenin en basit yolu kayıt defterini değiştirmektir. Yapmamız gereken, HKEY_CLASSES_ROOT altında uygulamamızı işaret eden bir giriş oluşturmaktır. Kullanıcı menü öğesini tıkladığında uygulamamızın yeni bir örneğinin açılacağını unutmayın. Bu, sonraki bölümlerde ele almamız gereken bir konudur.
using Microsoft.Win32;
public void Register()
{
RegistryKey key = Registry.ClassesRoot.OpenSubKey("*\\shell\\"+KEY_NAME+"\\command");
if (key == null)
{
key = Registry.ClassesRoot.CreateSubKey("*\\shell\\" + KEY_NAME + "\\command");
}
key.SetValue("", Application.ExecutablePath + " \"%1\"");
}Bu kod, çalışmakta olan uygulamanın bulunduğu yere kayıt defteri anahtarı oluşturur. "%1" gösterimi, kabukta seçilen dosyanın bu parametrenin içine iletilmesi gerektiğini belirtmek için kullanılır. KEY_NAME, içerik menüsündeki girişin metnini belirleyen tanımlanmış bir dize sabitidir.
public void UnRegister()
{
RegistryKey key = Registry.ClassesRoot.OpenSubKey("*\\shell\\"+KEY_NAME);
if (key != null)
{
Registry.ClassesRoot.DeleteSubKeyTree("*\\shell\\"+KEY_NAME);
}
}Bu yöntem, eklediğimiz özel anahtarı varsa kaldırır.
Birden Çok Örneği Önleme
Uygulamamız sistem tepsisinde yer aldığından programın aynı anda birden fazla örneğinin çalışmasını istemiyoruz. Yalnızca bir örneğin çalışmaya devam etmesini sağlamak için Mutex kullanabiliriz.
using System.Threading;
bool firstInstance;
Mutex mutex = new Mutex(true, "Local\\DocListUploader", out firstInstance);
if (!firstInstance)
{
return;
}Yukarıdaki kod, programımız zaten çalışıyorsa erken çıkmak için uygulamamızın Main yöntemine yerleştirilebilir. Mutex, "Local" ad alanında olduğundan makinede farklı bir oturumun uygulamamızı ayrı olarak çalıştırmasına olanak tanır. Ancak küresel kayıt defterini değiştirdiğimiz için biraz daha dikkatli olmamız gerekir.
İşlemler Arası İletişim
Kullanıcı, daha önce eklediğimiz bir dosyanın kabuk bağlam menüsü öğesini tıkladığında uygulamamızın yeni bir örneği başlatılır ve dosyanın diskteki konumunun tam yolu verilir. Bu bilgiler artık uygulamanın zaten çalışan örneğine iletilmelidir. Bu işlem, .NET Framework'ün 2.0 sürümünde kullanıma sunulan IPC mekanizmaları kullanılarak yapılabilir.
using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Ipc;
İletilen mesaj, özel bir nesne biçimindedir. Burada, bu uygulamanın mantığını içeren HiddenForm öğesine geri referans veren bir nesne oluşturdum. Bu nesne orijinal örnekte barındırılacağından, daha sonraki bir örneğe orijinal örneğin ana formuyla iletişim kurma olanağı sağlar.
class RemoteMessage : MarshalByRefObject
{
private HiddenForm mainForm;
public RemoteMessage(HiddenForm mainForm)
{
this.mainForm = mainForm;
}
public void SendMessage(string file)
{
mainForm.HandleUpload(file);
}
}
Uygulamanın ilk örneği başlatıldığında aşağıdaki kod, sonraki örnekleri dinlemesini sağlar:
public void ListenForSuccessor()
{
IpcServerChannel serverChannel = new IpcServerChannel("DocListUploader");
ChannelServices.RegisterChannel(serverChannel, false);
RemoteMessage remoteMessage = new RemoteMessage(this);
RemotingServices.Marshal(remoteMessage,"FirstInstance");
}Yukarıdaki örnekte, adlandırılmış bir IPC kanalı kaydedildiği ve tanımladığımız RemoteMessage nesnesinin bir kopyasının sağlandığı, bu kopyanın kendisiyle ilgili bir referansla başlatıldığı görülmektedir.
Programın sonraki örnekleri için, args parametresi aracılığıyla Main'ya sağlanan dize orijinal örneğe iletilmelidir. Dinleme yapan IPC kanalına bağlanmak ve orijinal örnekten RemoteMessage nesnesini almak için aşağıdaki kod çağrılabilir. Ardından, dosya adını orijinal örneğe iletmek için SendMessage yöntemi kullanılır.
public static void NotifyPredecessor(string file)
{
IpcClientChannel clientChannel = new IpcClientChannel();
ChannelServices.RegisterChannel(clientChannel, false);
RemoteMessage message = (RemoteMessage) Activator.GetObject(typeof(RemoteMessage),
"ipc://DocListUploader/FirstInstance");
if (!message.Equals(null))
{
message.SendMessage(file);
}
}Uzak mesajlaşma sistemi çok güçlüdür. Bu sistem sayesinde programımızın bir örneğine ait nesneleri yerel IPC kanalları üzerinden diğer örneklere görünür hale getirebiliriz.
Sonuç
Bu makalede, Google Dokümanlar için kullanıcı dostu bir taşıma aracı sağlamak amacıyla DocList Uploader örneğinde kullanılan çeşitli yöntemler ve püf noktaları genel olarak açıklanmaktadır. Kendi uygulamalarınıza ekleyebileceğiniz birçok işlev vardır ve örneği kendi amaçlarınıza uygun şekilde genişletebilirsiniz.
Doküman Listesi Verileri API'si ile çalışmak isteyen ve .NET'i diğer Google Verileri API'leriyle kullanmak isteyen geliştiriciler için bazı faydalı kaynakları aşağıda bulabilirsiniz: