جف فیشر، تیم Google Data APIs
ژانویه 2008
مقدمه: محدوده نمونه

یکی از چیزهای خوب در مورد Documents List Data API این است که به توسعه دهندگان این امکان را می دهد تا ابزارهای مهاجرت را برای کاربرانی که هنوز در Google Docs مستقر هستند ایجاد کنند. برای اهداف اجرای این API، من از کتابخانه مشتری دات نت برای ایجاد یک برنامه آپلود کننده دات نت 2.0، با عنوان مناسب «آپلودکننده فهرست اسناد» استفاده کرده ام. شما می توانید منبع آپلود کننده را از براندازی دریافت کنید.
این نمونه به این منظور است که انتقال اسناد خود را از رایانه خود به Google Docs برای کاربر آسان کند. این به کاربران اجازه می دهد تا به حساب Google خود وارد شوند و سپس فایل های پشتیبانی شده را بکشید و رها کنید که سپس به طور خودکار آپلود می شوند. نمونه همچنین گزینه ای را برای اضافه کردن گزینه منوی کلیک راست به پوسته Windows Explorer برای آپلود فایل ها ارائه می دهد. این نمونه تحت مجوز Apache 2.0 ارائه شده است، بنابراین شما می توانید از آن به عنوان نقطه شروع برای برنامه های خود استفاده کنید.
هدف این مقاله نشان دادن چگونگی انجام برخی از رفتارهای نمونه با استفاده از چارچوب دات نت است. بیشتر شامل کدهای مشروح شده از بخش های مربوطه است. این مقاله نحوه ساخت فرمها و سایر مؤلفههای رابط کاربری برنامه کاربردی را پوشش نمیدهد، زیرا مقالات ویژوال استودیو زیادی وجود دارد که در این مورد به جزئیات میپردازند. اگر کنجکاو هستید که اجزای رابط کاربری چگونه پیکربندی شدهاند، میتوانید فایل پروژه را خودتان بارگیری کنید، با دانلود کتابخانه سرویس گیرنده و نگاه کردن به دایرکتوری فرعی «clients\cs\samples\DocListUploader».
ساخت اپلیکیشن سینی سیستم

ابزارهای مهاجرت معمولاً قادرند بدون مزاحمت در سیستم عامل اجرا شوند و آنچه را که سیستم عامل قادر به انجام آن بدون حواس پرتی زیاد برای کاربر است، گسترش می دهد. یکی از راههای ساختاردهی چنین ابزاری در ویندوز این است که به جای بهم ریختن نوار وظیفه آنها، از سینی سیستم تمام شود. این امر باعث میشود که کاربران به جای باز کردن برنامه در زمانی که نیاز به انجام یک کار خاص دارند، برنامه را به طور مداوم در حال اجرا رها کنند. این یک ایده مخصوصاً مفید برای این نمونه است زیرا نیازی به ذخیره اعتبار احراز هویت در دیسک ندارد.
یک برنامه سینی سیستم برنامه ای است که عمدتاً فقط با یک NotifyIcon در سینی سیستم (منطقه نزدیک به ساعت در نوار وظیفه) اجرا می شود. هنگام طراحی چنین اپلیکیشنی، به خاطر داشته باشید که نمی خواهید فرم اصلی پروژه، شکلی باشد که کاربر با آن در تعامل است. در عوض، یک فرم جداگانه ایجاد کنید تا در هنگام اجرای برنامه نمایش داده شود. دلیل این امر تا چند لحظه دیگر مشخص خواهد شد.
در مثال خود من دو فرم ایجاد کردهام: HiddenForm، فرم اصلی برنامه با بیشتر منطق، و OptionsForm فرمی که به کاربر اجازه میدهد برخی از گزینهها را سفارشی کند و به حساب Google خود وارد شود. من همچنین یک NotifyIcon به نام DocListNotifyIcon را به HiddenForm اضافه کردم و آن را با آیکون خودم سفارشی کردم. برای اطمینان از اینکه HiddenForm توسط کاربر دیده نمی شود، opacity آن را روی 0٪، WindowState آن را به Minimized و خاصیت ShowInTaskbar آن را روی False قرار دادم.
معمولاً زمانی که یک برنامه در سینی سیستم در حال اتمام است، بستن برنامه قرار نیست برنامه را متوقف کند، بلکه در عوض هرگونه فرم فعال را مخفی می کند و فقط NotifyIcon را قابل مشاهده می کند. برای انجام این کار، باید رویداد "FormClosing" فرم خود را به صورت زیر لغو کنیم:
private void OptionsForm_FormClosing(object sender, FormClosingEventArgs e)
{
if(e.CloseReason == CloseReason.UserClosing) {
this.Hide();
e.Cancel = true;
}
}همچنین احتمالاً میخواهیم زمانی که کاربر آن را به حداقل میرساند، فرم را مخفی کنیم، زیرا دلیلی برای اشغال فضایی در نوار وظیفه وجود ندارد زیرا قبلاً یک نماد اعلان داریم. این کار با بیت کد زیر قابل انجام است:
private void OptionsForm_Resize(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
{
this.Hide();
}
} اکنون از آنجایی که به کاربر اجازه نمیدهیم OptionsForm را ببندد، میتوانیم فقط یک نمونه را در اطراف نگه داریم که به HiddenForm ما گره خورده است. وقتی می خواهیم OptionsForm را دوباره نمایش دهیم، می توانیم به سادگی متد Show آن را فراخوانی کنیم.
از آنجایی که فرم اصلی این برنامه، HiddenForm، برای کاربر قابل مشاهده نیست، باید راهی برای خروج از برنامه ما به آنها ارائه دهیم. من انتخاب کردم که یک ContextMenu به NotifyIcon با یک ToolStripMenuItem اضافه کنم تا برنامه را ببندم. برای نوشتن کنترل کننده کلیک ساده است، فقط متد Close از HiddenForm را فراخوانی کنید.
نکات بالون
بسیاری از برنامه های سینی سیستم با نشان دادن یک نوک بالون با کاربر ارتباط برقرار می کنند، که شبیه حباب گردی است که از NotifyIcon سرچشمه می گیرد. حباب را می توان به صورت زیر نشان داد:
DocListNotifyIcon.ShowBalloonTip(10000, "Title", "Example Text", ToolTipIcon.Info);
اولین آرگومان مقدار زمان بر حسب میلی ثانیه برای نمایش حباب است. توجه داشته باشید که حداقل و حداکثر زمان هایی که سیستم عامل برای این قسمت در نظر می گیرد وجود دارد که به ترتیب 10 و 30 ثانیه است. آرگومان های دوم و سوم یک عنوان و محتوایی را برای حباب مشخص می کنند. آخرین آرگومان به شما امکان می دهد نمادی را برای نشان دادن هدف حباب انتخاب کنید.
بارگذاری اسناد
بارگذاری یک سند ساده است. بیشتر کارها توسط متد UploadDocument شیء DocumentsService انجام می شود. این فرآیند در راهنمای برنامهنویس برای فهرست اسناد API به وضوح توضیح داده شده است.
service = new DocumentsService("DocListUploader");
((GDataRequestFactory) service.RequestFactory).KeepAlive = false;
service.setUserCredentials(username, password); ابتدا شی DocumentsService باید مقداردهی اولیه شود و اعتبار کاربر باید ارائه شود. به منظور جلوگیری از بروز برخی مشکلات هنگام آپلود چندین فایل، هدر HTTP "keep-alive" غیرفعال شده است زیرا مشخص است که باعث ایجاد مشکل در .NET Framework می شود.
lastUploadEntry = service.UploadDocument(fileName, null);
این قطعه فایل را در مسیر موجود در رشته fileName آپلود می کند. آرگومان دوم null بودن نشان می دهد که نام فایل Google Docs باید با نام فایل اصلی یکسان باشد.
مدیریت کشیدن و رها کردن
به منظور آسانتر کردن آپلود، منطقی است که به کاربر اجازه دهید فایلها را از پوشههای خود روی برنامه بکشد و آنها را آپلود کند. اولین مرحله اجازه دادن به عملیات drop از یک فایل است، کد زیر مکان نما را تغییر می دهد تا نشان دهد افت مجاز است:
private void OptionsForm_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop, false))
{
e.Effect = DragDropEffects.Copy;
}
}پس از حذف فایل یا گروهی از فایلها، باید با مرور هر فایل حذف شده و آپلود آن، آن رویداد را مدیریت کنیم:
private void OptionsForm_DragDrop(object sender, DragEventArgs e)
{
string[ fileList = (string[) e.Data.GetData(DataFormats.FileDrop);
foreach (string file in fileList)
{
mainForm.UploadFile(file);
}
}اسناد فهرست بندی
دریافت لیستی از اسناد از سرور راه خوبی برای یادآوری مطالبی است که کاربر قبلاً آپلود کرده است. قطعه زیر از شی DocumentsService که قبلاً مقداردهی اولیه کردیم برای بازیابی همه اسناد از سرور استفاده می کند.
public DocumentsFeed GetDocs()
{
DocumentsListQuery query = new DocumentsListQuery();
DocumentsFeed feed = service.Query(query);
return feed;
} یک راه راحت برای تجسم این داده ها استفاده از ListView است. من یک ListView به نام DocList را به OptionsForm اضافه کردم. برای زیباتر کردن آن، من همچنین یک ImageList سفارشی از نمادها برای نشان دادن انواع مختلف سند ایجاد کردم. کد زیر ListView را با اطلاعات بازیابی شده از فید بالا پر می کند:
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 انتخاب می کند که کدام تصویر در ImageList مرتبط باید برای هر ردیف استفاده شود. ویژگی Tag در اینجا برای ذخیره ورودی اصلی استفاده می شود که می تواند برای انجام عملیات روی سند بعدا مفید باشد. در نهایت، متد AutoResize برای قالب بندی خودکار عرض ستون در ListView استفاده می شود.
باز کردن اسناد در مرورگر
از آنجایی که این اسناد در Google Docs ذخیره می شوند، خوب است که به کاربر اجازه دهید سند را در مرورگر خود ببیند. برای انجام این کار در ویندوز عملکرد داخلی وجود دارد:
using System.Diagnostics;
private void OpenSelectedDocument()
{
if (DocList.SelectedItems.Count > 0)
{
DocumentEntry entry = (DocumentEntry) DocList.SelectedItems[0].Tag;
Process.Start(entry.AlternateUri.ToString());
}
}
در اینجا ورودی اصلی را از ویژگی Tag بازیابی می کنیم، سپس از AlternateUri سند انتخاب شده برای فراخوانی Process.Start استفاده می کنیم. بقیه با جادوی دات نت فریم ورک اداره می شود.
افزودن یک منوی زمینه پوسته
ساده ترین راه برای افزودن یک آیتم به منوی زمینه پوسته، اصلاح رجیستری است. کاری که ما باید انجام دهیم این است که یک ورودی تحت HKEY_CLASSES_ROOT ایجاد کنیم که به برنامه ما اشاره می کند. توجه داشته باشید که وقتی کاربر روی آیتم منو کلیک میکند، یک نمونه جدید از برنامه ما باز میشود، که در بخشهای بعدی باید به آن بپردازیم.
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\"");
} این کد کلید رجیستری را در جایی که برنامه در حال اجرا در حال اجرا قرار دارد ایجاد می کند. نماد "% 1" برای نشان دادن اینکه فایل انتخاب شده در پوسته باید در داخل این پارامتر ارسال شود استفاده می شود. KEY_NAME یک ثابت رشته تعریف شده است که متن ورودی را در منوی زمینه تعیین می کند.
public void UnRegister()
{
RegistryKey key = Registry.ClassesRoot.OpenSubKey("*\\shell\\"+KEY_NAME);
if (key != null)
{
Registry.ClassesRoot.DeleteSubKeyTree("*\\shell\\"+KEY_NAME);
}
}این روش به سادگی کلید سفارشی را که اضافه کرده ایم، در صورت وجود حذف می کند.
جلوگیری از موارد متعدد
از آنجایی که برنامه ما در سینی سیستم زندگی می کند، نمی خواهیم چندین نمونه از برنامه در یک زمان اجرا شود. ما می توانیم از Mutex استفاده کنیم تا مطمئن شویم که فقط یک نمونه در حال اجرا باقی می ماند.
using System.Threading;
bool firstInstance;
Mutex mutex = new Mutex(true, "Local\\DocListUploader", out firstInstance);
if (!firstInstance)
{
return;
} کد بالا را می توان در متد Main برنامه ما قرار داد تا اگر برنامه ما از قبل در حال اجرا است، زودتر از آن خارج شوید. از آنجایی که Mutex در فضای نام "محلی" قرار دارد، این اجازه می دهد تا یک جلسه متفاوت در دستگاه برای اجرای برنامه ما به طور جداگانه انجام شود. با این حال، از آنجایی که ما در حال اصلاح ثبت جهانی هستیم، باید مراقبت بیشتری انجام شود.
ارتباطات بین فرآیندی
هنگامی که کاربر روی آیتم منوی زمینه پوسته برای فایلی که قبلا اضافه کردیم کلیک می کند، نمونه جدیدی از برنامه ما راه اندازی می شود و مسیر کامل جایی که فایل روی دیسک قرار دارد داده می شود. این اطلاعات اکنون باید به نمونه در حال اجرا برنامه منتقل شود. این کار را می توان با استفاده از مکانیزم های IPC دات نت فریم ورک که در نسخه 2.0 معرفی شد انجام داد.
using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Ipc;
پیامی که ما ارسال می کنیم به شکل یک شی سفارشی است. در اینجا من یک شی ایجاد کرده ام که حاوی یک مرجع به HiddenForm است که حاوی منطق این برنامه است. از آنجایی که این شی بر روی نمونه اصلی میزبانی می شود، نمونه بعدی راهی برای برقراری ارتباط با شکل اصلی نمونه اصلی ارائه می دهد.
class RemoteMessage : MarshalByRefObject
{
private HiddenForm mainForm;
public RemoteMessage(HiddenForm mainForm)
{
this.mainForm = mainForm;
}
public void SendMessage(string file)
{
mainForm.HandleUpload(file);
}
}
هنگامی که اولین نمونه از برنامه مقدار دهی اولیه می شود، کد زیر آن را قادر می سازد تا برای نمونه های متوالی گوش دهد:
public void ListenForSuccessor()
{
IpcServerChannel serverChannel = new IpcServerChannel("DocListUploader");
ChannelServices.RegisterChannel(serverChannel, false);
RemoteMessage remoteMessage = new RemoteMessage(this);
RemotingServices.Marshal(remoteMessage,"FirstInstance");
} در بالا توجه داشته باشید که یک کانال IPC با نام را ثبت میکند، و یک کپی از شی RemoteMessage که ما تعریف کردیم، ارائه میکند و آن را با ارجاع به خودش مقداردهی اولیه میکند.
برای نمونه های متوالی برنامه، رشته ای که از طریق پارامتر args به Main ارائه می شود باید به نمونه اصلی منتقل شود. کد زیر را می توان برای اتصال به کانال IPC شنود و بازیابی شی RemoteMessage از نمونه اصلی فراخوانی کرد. سپس از روش SendMessage برای ارسال نام فایل به نمونه اصلی استفاده می شود.
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);
}
}سیستم پیام رسانی از راه دور بسیار قدرتمند است زیرا از طریق آن می توانیم اشیاء متعلق به یک نمونه از برنامه خود را از طریق کانال های IPC محلی برای نمونه های دیگر قابل مشاهده کنیم.
نتیجه گیری
این مقاله برخی از روشها و ترفندهای مختلف مورد استفاده در نمونه DocList Uploader را برای ارائه یک ابزار مهاجرت دوستانه برای Google Docs در سطح بالایی توضیح میدهد. هنوز قابلیتهای زیادی وجود دارد که میتوان آنها را در برنامههای کاربردی خود اضافه کرد، و شما آزاد هستید که نمونه را مطابق با اهداف خود گسترش دهید.
در اینجا چند منبع مفید برای توسعه دهندگانی که علاقه مند به کار با Documents List Data API هستند، و همچنین کسانی که می خواهند از دات نت با سایر APIهای Google Data استفاده کنند، آورده شده است: