Jeff Fisher ทีม Google Data APIs
มกราคม 2008
ข้อมูลเบื้องต้น: ขอบเขตของตัวอย่าง
ดาวน์โหลดไฟล์ที่เรียกใช้ได้ตัวอย่าง
ข้อดีอย่างหนึ่งของ Documents List Data API คือช่วยให้นักพัฒนาแอปสร้างเครื่องมือย้ายข้อมูลสำหรับผู้ใช้ที่ยังคงใช้ Google เอกสาร ได้ เพื่อวัตถุประสงค์ในการใช้ API นี้ ฉันได้ใช้ไลบรารีของไคลเอ็นต์ .NET เพื่อสร้างแอปพลิเคชันโปรแกรมอัปโหลด .NET 2.0 ซึ่งตั้งชื่ออย่างเหมาะสมว่า "โปรแกรมอัปโหลด DocList" คุณดูแหล่งที่มาของผู้อัปโหลดได้จาก Subversion
ตัวอย่างนี้มีไว้เพื่อให้ผู้ใช้ย้ายข้อมูลเอกสารจากคอมพิวเตอร์ไปยัง Google เอกสารได้ง่ายๆ ซึ่งช่วยให้ผู้ใช้เข้าสู่ระบบบัญชี Google แล้วลากและวางไฟล์ที่รองรับเพื่ออัปโหลดโดยอัตโนมัติได้ นอกจากนี้ ตัวอย่างยังมีตัวเลือกในการเพิ่มตัวเลือกเมนูคลิกขวาไปยังเชลล์ Windows Explorer เพื่ออัปโหลดไฟล์ด้วย ตัวอย่างนี้มีให้ภายใต้สัญญาอนุญาต Apache 2.0 คุณจึงนำไปใช้เป็นจุดเริ่มต้นสำหรับโปรแกรมของคุณเองได้อย่างอิสระ
บทความนี้มีจุดประสงค์เพื่อแสดงให้เห็นว่าพฤติกรรมบางอย่างของตัวอย่างนั้นทำได้อย่างไรโดยใช้ .NET Framework โดยส่วนใหญ่ประกอบด้วยข้อมูลโค้ดที่มีคำอธิบายประกอบจากส่วนที่เกี่ยวข้อง บทความนี้ไม่ได้กล่าวถึงวิธีก่อสร้างแบบฟอร์มและคอมโพเนนต์ UI อื่นๆ ของแอปพลิเคชัน เนื่องจากมีบทความ Visual Studio มากมายที่อธิบายรายละเอียดในเรื่องนี้ หากคุณสงสัยว่ามีการกำหนดค่าคอมโพเนนต์ UI อย่างไร คุณสามารถโหลดไฟล์โปรเจ็กต์ด้วยตนเองได้โดยดาวน์โหลดไลบรารีไคลเอ็นต์และดูในไดเรกทอรีย่อย "clients\cs\samples\DocListUploader"
การสร้างแอปถาดระบบ
โดยปกติแล้ว เครื่องมือการย้ายข้อมูลจะสามารถทำงานในระบบปฏิบัติการได้อย่างราบรื่น ซึ่งจะขยายสิ่งที่ระบบปฏิบัติการทำได้โดยไม่รบกวนผู้ใช้มากนัก วิธีหนึ่งในการจัดโครงสร้างเครื่องมือดังกล่าวใน Windows คือการให้เครื่องมือทำงานนอกถาดระบบแทนที่จะทำให้แถบงานรก ซึ่งจะช่วยให้ผู้ใช้มีแนวโน้มที่จะเปิดโปรแกรมทำงานอย่างต่อเนื่องมากกว่าที่จะเปิดโปรแกรมเฉพาะเมื่อต้องการทำงานที่เฉพาะเจาะจง แนวคิดนี้มีประโยชน์อย่างยิ่งสำหรับตัวอย่างนี้ เนื่องจากไม่จำเป็นต้องจัดเก็บข้อมูลเข้าสู่ระบบการตรวจสอบสิทธิ์ลงในดิสก์
แอปพลิเคชันถาดระบบคือแอปพลิเคชันที่ทำงานโดยมีเพียง NotifyIcon ในถาดระบบ (บริเวณใกล้นาฬิกาบนแถบงาน) เป็นหลัก เมื่อออกแบบแอปพลิเคชันดังกล่าว โปรดทราบว่าคุณไม่ต้องการให้รูปแบบหลักของโปรเจ็กต์เป็นรูปแบบที่ผู้ใช้โต้ตอบด้วย แต่ให้สร้างแบบฟอร์มแยกต่างหากเพื่อแสดงเมื่อเรียกใช้แอปพลิเคชัน เราจะอธิบายเหตุผลในเรื่องนี้ให้ชัดเจนในอีกสักครู่
ในตัวอย่างของฉัน ฉันได้สร้างแบบฟอร์ม 2 แบบ ได้แก่ HiddenForm ซึ่งเป็นแบบฟอร์มหลักของแอปพลิเคชันที่มีตรรกะส่วนใหญ่ และ OptionsForm ซึ่งเป็นแบบฟอร์มที่ให้ผู้ใช้ปรับแต่งตัวเลือกบางอย่างและลงชื่อเข้าใช้บัญชี Google นอกจากนี้ ฉันยังเพิ่ม NotifyIcon ที่ชื่อ DocListNotifyIcon ลงใน HiddenForm และปรับแต่งด้วยไอคอนของตัวเอง เพื่อไม่ให้ผู้ใช้เห็น HiddenForm ฉันจึงตั้งค่าความทึบเป็น 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 วินาทีตามลำดับ อาร์กิวเมนต์ที่ 2 และ 3 ระบุชื่อและเนื้อหาบางอย่างสำหรับบับเบิล อาร์กิวเมนต์สุดท้ายช่วยให้คุณเลือกไอคอนเพื่อแสดงวัตถุประสงค์ของบับเบิลได้
การอัปโหลดเอกสาร
การอัปโหลดเอกสารนั้นทำได้ง่ายๆ การดำเนินการส่วนใหญ่จะทำโดยใช้วิธีการ UploadDocument ของออบเจ็กต์ DocumentsService กระบวนการนี้อธิบายไว้ชัดเจนยิ่งขึ้นในคู่มือสำหรับนักพัฒนาซอฟต์แวร์ Documents List 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 อาร์กิวเมนต์ที่ 2 เป็น Null แสดงว่าชื่อไฟล์ Google เอกสารจะเหมือนกับชื่อไฟล์เดิม
การจัดการการลากและวาง
เพื่อให้การอัปโหลดง่ายขึ้น จึงควรอนุญาตให้ผู้ใช้ลากและวางไฟล์จากโฟลเดอร์ลงในแอปพลิเคชันเพื่ออัปโหลด ขั้นตอนแรกคือการอนุญาตการดำเนินการวางจากไฟล์ โดยโค้ดด้านล่างจะเปลี่ยนเคอร์เซอร์เพื่อระบุว่าอนุญาตให้วางได้
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 เอกสาร จึงควรอนุญาตให้ผู้ใช้ดูเอกสารในเบราว์เซอร์ของตนเอง Windows มีฟังก์ชันในตัวที่ใช้ทำสิ่งนี้ได้ ดังนี้
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 ส่วนที่เหลือจะได้รับการจัดการโดยความมหัศจรรย์ของ .NET Framework
การเพิ่มเมนูตามบริบทของ Shell
วิธีที่ง่ายที่สุดในการเพิ่มรายการลงในเมนูตามบริบทของ Shell คือการแก้ไขรีจิสทรี สิ่งที่เราต้องทำคือสร้างรายการใน 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 อยู่ในเนมสเปซ "Local" จึงทำให้เซสชันอื่นในเครื่องสามารถเรียกใช้แอปพลิเคชันของเราแยกกันได้ อย่างไรก็ตาม คุณควรระมัดระวังเป็นพิเศษเนื่องจากเรากำลังแก้ไขรีจิสทรีส่วนกลาง
การสื่อสารระหว่างโปรเซส
เมื่อผู้ใช้คลิกรายการเมนูบริบทของ Shell สำหรับไฟล์ที่เราเพิ่มไว้ก่อนหน้านี้ ระบบจะเปิดอินสแตนซ์ใหม่ของแอปพลิเคชันของเราและระบุเส้นทางแบบเต็มไปยังตำแหน่งที่ไฟล์อยู่บนดิสก์ ตอนนี้ต้องสื่อสารข้อมูลนี้ไปยังอินสแตนซ์ของแอปพลิเคชันที่กําลังทํางานอยู่ ซึ่งทำได้โดยใช้กลไก IPC ของ .NET Framework ที่เปิดตัวในเวอร์ชัน 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 ที่เรากำหนด โดยเริ่มต้นด้วยการอ้างอิงถึงตัวมันเอง
สำหรับอินสแตนซ์ที่ต่อเนื่องของโปรแกรม สตริงที่ระบุให้กับ Main ผ่านพารามิเตอร์ args จะต้องส่งต่อไปยังอินสแตนซ์เดิม คุณเรียกใช้โค้ดต่อไปนี้เพื่อเชื่อมต่อกับช่อง 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 เพื่อจัดเตรียมยูทิลิตีการย้ายข้อมูลที่ใช้งานง่ายสำหรับ Google เอกสาร คุณยังคงเพิ่มฟังก์ชันการทำงานอีกมากมายในแอปพลิเคชันของคุณเองได้ และคุณสามารถขยายตัวอย่างให้เหมาะกับวัตถุประสงค์ของคุณเองได้
ต่อไปนี้คือแหล่งข้อมูลที่เป็นประโยชน์สำหรับนักพัฒนาแอปที่สนใจใช้ Documents List Data API รวมถึงผู้ที่ต้องการใช้ .NET กับ Google Data API อื่นๆ