สร้างสมุดงานจาก Delphi แสดงความคิดเห็นในเซลล์ด้วย API ความคิดเห็นแบบดั้งเดิม และ Excel 365 จะบันทึกข้อความของคุณไว้ใต้ "บันทึกย่อ" (Notes): กล่องสีเหลืองสไตล์เก่าที่ไม่มีช่องตอบกลับ ไม่มีรูปโปรไฟล์ผู้เขียน ไม่มีปุ่มแก้ไขปัญหา HotXLS เขียนความคิดเห็นแบบ threaded comment แท้ของ Excel 365 ผ่าน AddThreadedComment และ AddThreadedReply บนเอ็นจิน XLSX โดยสร้างส่วนเฉพาะ xl/threadedComments/threadedCommentN.xml ต่อแผ่นงาน และลงทะเบียนผู้สร้างทุกคนในส่วนบุคคลในระดับสมุดงาน ดังนั้นการสนทนาที่โค้ดของคุณเขียนจึงเป็นวัตถุเดียวกับที่ผู้ตรวจสอบตอบกลับใน Excel
ความแตกต่างนี้มีความสำคัญเนื่องจากฟีเจอร์ทั้งสองนี้เพียงแค่ดูคล้ายกันเท่านั้น บันทึกย่อคือกล่องข้อความลอยน้ำ ในขณะที่ threaded comment คือบันทึกการสนทนาที่มีตัวระบุที่เสถียร แผนผังการตอบกลับ ทะเบียนผู้สร้าง และแฟล็กการแก้ไขปัญหา — สิ่งต่าง ๆ ที่เวิร์กโฟลว์การตรวจสอบใช้งานจริง บทความนี้จะอธิบายโครงสร้าง OOXML ก่อน เนื่องจากเมื่อคุณเห็นโมเดลการจัดเก็บข้อมูลทั้งสองแบบเคียงข้างกัน ความแตกต่างของพฤติกรรมใน Excel ทั้งหมดจะไม่ใช่เรื่องน่าลึกลับอีกต่อไป
ทำไมความคิดเห็นของฉันจึงแสดงเป็นบันทึกย่อใน Excel 365?
คำตอบสั้น ๆ: ความคิดเห็นสองรุ่นแชร์ตารางร่วมกัน และพวกมันอาศัยอยู่ในส่วนที่แตกต่างกันอย่างสิ้นเชิงของแพ็คเกจ โมเดลรุ่นเก่า — รุ่นที่ AddComment กำหนดเป้าหมาย — จะเก็บข้อความไว้ใน xl/comments1.xml (ประเภทเนื้อหา application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml) และจัดตำแหน่งลูกโป่งผ่านชิ้นส่วนการวาด VML ที่มาคู่กัน xl/drawings/vmlDrawing1.vml คู่ดังกล่าวมีมาก่อนริบบิ้น Excel 365 ยังคงอ่านข้อมูลนี้อย่างถูกต้อง แต่จะแสดงผลลัพธ์เป็น "บันทึกย่อ" และไม่มี UI สำหรับความคิดเห็นย่อย เนื่องจากไม่มีข้อมูลในแหล่งเก็บข้อมูลที่จะนำมาเชื่อมโยงกัน ผู้สร้างคือสตริงแสดงผล ตำแหน่งคือรูปทรง VML และนั่นคือโมเดลทั้งหมด
ความคิดเห็นแบบเกลียว (Threaded comments) ซึ่งเปิดตัวพร้อมกับ Excel 365 จะละเว้นเลเยอร์ VML อย่างสิ้นเชิง แต่ละแผ่นงานที่มีการสนทนาจะมีชิ้นส่วน xl/threadedComments/threadedCommentN.xml ของตัวเอง ภายใต้ประเภทเนื้อหา application/vnd.openxmlformats-officedocument.spreadsheetml.threadedComments+xml และสมุดงานจะมีส่วน xl/person/person.xml เพียงส่วนเดียว (ประเภทเนื้อหา ...spreadsheetml.people+xml) ซึ่งแสดงรายชื่อผู้เข้าร่วมทุกคนครั้งเดียว HotXLS แสดงทั้งสองโมเดลบนเวิร์กชีตเดียวกัน: AddComment ยังคงเขียนคู่ดั้งเดิม ในขณะที่ AddThreadedComment จะเขียนส่วนใหม่ หากผลลัพธ์ของคุณแสดงเป็นบันทึกย่อ แสดงว่าคุณได้เรียกใช้ API แรกแทนที่จะเรียกใช้ตัวที่สอง โมเดลดั้งเดิมยังคงเป็นเครื่องมือที่เหมาะสมสำหรับงานบางอย่าง — บทความก่อนหน้านี้เกี่ยวกับความคิดเห็นในเซลล์และไฮเปอร์ลิงก์ในเวิร์กโฟลว์การตรวจสอบจะครอบคลุมเรื่องนี้ในเชิงลึก รวมถึงฝั่ง XLS — แต่ไม่มีอะไรในนั้นที่สามารถอัปเกรดเป็นเกลียวสนทนาได้
สิ่งที่ HotXLS เขียนลงในแพ็คเกจ
HotXLS จำลองการตอบกลับแต่ละครั้งเป็นวัตถุ TXLSXThreadedComment ซึ่งมีจุดยึดเซลล์ (Ref), ตัวระบุสไตล์ GUID (Id), ParentId เสริม, AuthorId ที่คลี่คลายผ่านรายการบุคคล, หน้าจอแสดงชื่อ DisplayName, เนื้อหาข้อความ, แฟล็กเสร็จสิ้น Done และ DateTime ในรูปแบบ ISO-8601 เมื่อบันทึก ทุกรายการจะกลายเป็นองค์ประกอบ <threadedComment> ซึ่งแอตทริบิวต์ t จะเก็บตัวระบุ แอตทริบิวต์ dT จะเก็บประทับเวลา และแอตทริบิวต์ parentT — ซึ่งจะมีเฉพาะในการตอบกลับย่อย — จะระบุชื่อตัวระบุหลัก Excel จะสร้างแผนผังการสนทนาขึ้นใหม่โดยจับคู่ parentT กับ t; ไม่มีรังนกซ้อนกันใน XML มีเพียงรายการแบน ๆ และการอ้างอิง
การสร้างสายการตอบกลับด้วย AddThreadedComment และ AddThreadedReply
AddThreadedComment จะสร้างรูทของการสนทนา: ParentId ของมันจะว่างเปล่า ซึ่งเป็นสิ่งที่ทำเครื่องหมายว่ามันเป็นจุดเริ่มต้นของกระทู้ AddThreadedReply รับพารามิเตอร์ ParentRef — การอ้างอิงเซลล์ที่รูทอาศัยอยู่ — ค้นหารูทที่จุดยึดนั้น และประทับตรา ParentId ของการตอบกลับใหม่ด้วย Id ของรูท ทั้งสองจะส่งคืนวัตถุ TXLSXThreadedComment ใหม่ ดังนั้นคุณสามารถตั้งค่าประทับเวลาหรือสถานะการแก้ไขบนวัตถุที่คุณเพิ่งสร้างขึ้นได้:
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
Root: TXLSXThreadedComment;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('forecast.xlsx');
Sheet := Book.Sheets[0];
// Conversation root; ParentId stays empty
Root := Sheet.AddThreadedComment(8, 3,
'Q3 figure looks low against the pipeline export', 'Maria Ortiz');
Root.DateTime := '2026-07-06T09:12:00Z';
// Replies anchor at the same cell; ParentRef names the root cell
Sheet.AddThreadedReply(8, 3, Root.Ref,
'Pipeline export missed the EMEA renewals, re-running', 'Jan Kowalski');
Sheet.AddThreadedReply(8, 3, Root.Ref,
'Confirmed, refreshed figure lands tomorrow', 'Maria Ortiz');
Book.SaveAs('forecast-reviewed.xlsx');
finally
Book.Free;
end;
end;
รายละเอียดสองประการในรายการนี้ควรใส่ใจ ประการแรก มีการส่งค่า Root.Ref แทนที่จะเป็นสตริงที่สร้างขึ้นเอง: การอ้างอิงที่ไลบรารีสร้างขึ้นรับประกันว่าจะตรงกับสิ่งที่ AddThreadedReply จะค้นหา ซึ่งช่วยขจัดข้อผิดพลาดเกี่ยวกับจุดยึดเซลล์ที่คลาดเคลื่อนไปหนึ่งหน่วยทั้งหมด ประการที่สอง ผู้สร้างทั้งสองถูกส่งเป็นชื่อแสดงผลธรรมดา — ทะเบียนบุคคล ตัวตน GUID และการเชื่อมต่อ authorId ทั้งหมดเกิดขึ้นเบื้องหลังการเรียกใช้งาน เมื่อเปิดไฟล์ที่บันทึกไว้ in Excel 365 จุดยึดเซลล์ ลำดับการตอบกลับ และผู้เข้าร่วมที่แตกต่างกันสองคนจะพร้อมใช้งานจริง: คลิกตอบกลับ และ Excel จะเพิ่มการตอบกลับลงในเธรดเดียวกันกับที่โค้ดของคุณเริ่มต้น
การอ่านการสนทนากลับมา: พฤติกรรมการเดินทางไปกลับ
HotXLS เดินทางไปกลับความคิดเห็นแบบเกลียวตั้งแต่เวอร์ชัน 2.122.0: การเปิดแพ็คเกจที่บันทึกไว้อีกครั้งจะสร้างรายการสนทนาของแต่ละแผ่นงานและรายการผู้เข้าร่วมสมุดงานใหม่จากส่วนประกอบต่าง ๆ ดังนั้นตัวระบุ ลิงก์ตอบกลับ และชื่อที่แสดงจะคงอยู่ตลอดวงจรการอ่าน→เขียนที่สมบูรณ์ โปรแกรมอ่านจะแยกวิเคราะห์ person.xml ก่อนเนื้อหาแผ่นงานใด ๆ ซึ่งทำให้แต่ละ authorId คลี่คลายไปยังชื่อแสดงผลในเวลาโหลด ตัวระบุบุคคลที่แยกวิเคราะห์จะถูกเก็บรักษาไว้ตามเดิมแทนที่จะสร้างขึ้นใหม่ ดังนั้นสมุดงานที่เดินทางไปมาระหว่างโค้ดของคุณกับ Excel จะรักษาตัวตนของผู้สร้างให้คงที่ตลอดการเดินทางไปกลับหลาย ๆ ครั้ง
var
i: Integer;
Cmt: TXLSXThreadedComment;
begin
Book.Open('forecast-reviewed.xlsx');
Sheet := Book.Sheets[0];
for i := 0 to Sheet.ThreadedComments.Count - 1 do
begin
Cmt := Sheet.ThreadedComments[i];
if Cmt.ParentId = '' then
Writeln('Root at ', Cmt.Ref, ' by ', Cmt.DisplayName, ': ', Cmt.Text)
else
Writeln(' reply by ', Cmt.DisplayName, ': ', Cmt.Text);
end;
Writeln('Participants: ', Book.Persons.Count);
end;
การจัดเก็บข้อมูลแบบรายการแบนบวกกับการอ้างอิงแสดงให้เห็นที่นี่โดยตั้งใจ: ThreadedComments จะระบุการตอบกลับตามลำดับส่วนประกอบ และ ParentId ที่ว่างเปล่าจะระบุแต่ละรูท หากคุณต้องการรูปร่างแผนผัง — เช่น เพื่อส่งออกบันทึกการตรวจสอบ — ให้จัดกลุ่มตาม Ref ก่อนแล้วค่อยเชื่อมโยง ParentId เข้ากับ Id ภายในกลุ่มของแต่ละเซลล์ พฤติกรรมการรักษาโครงสร้างแบบเดียวกับที่คุณแยกวิเคราะห์นี้เป็นตัวอย่างหนึ่งของนโยบายที่กว้างขึ้นในไลบรารี บทความเกี่ยวกับการเดินทางไปกลับแบบไม่มีการสูญเสียของธีม รายการส่วนขยาย และสายโซ่การคำนวณจะครอบคลุมถึงวิธีการใช้หลักการเดียวกันกับส่วนที่เหลือของแพ็คเกจ
สถานะที่แก้ไขแล้ว ประทับเวลา และตัวตนของผู้สร้าง
แอตทริบิวต์สามตัวทำหน้าที่จัดเตรียมความหมายการตรวจสอบ และทั้งสามตัวสามารถเขียนได้บนวัตถุที่ส่งคืน Done จับคู่กับแอตทริบิวต์ done — Excel จะแสดงกระทู้ที่แก้ไขแล้วถูกยุบลงพร้อมลิงก์เปิดใหม่ DateTime คือตราประทับการสร้าง UTC ในรูปแบบ ISO-8601 (dT) HotXLS จะแทนที่ตัวยึดตำแหน่งคงที่เมื่อคุณปล่อยให้ว่างเปล่าเพื่อให้ส่วนประกอบตรวจสอบความถูกต้องได้เสมอ แต่การประทับเวลาจริงจะทำให้ประวัติเธรดอ่านง่ายขึ้น ดังนั้นโปรดตั้งค่า ในด้านข้อมูลระบุตัวตน TXLSXPersonแต่ละตัวจะเปิดเผย ProviderId — โดยทั่วไปจะเป็น None สำหรับรายการที่สร้างขึ้นในเครื่อง — และ UserId ซึ่งคุณสามารถกรอกด้วย UPN หรืออีเมลเมื่อแอปพลิเคชันของคุณรู้:
var
Root: TXLSXThreadedComment;
Person: TXLSXPerson;
begin
Root := Sheet.ThreadedComments.FindAt('D9');
if Root <> nil then
Root.Done := True; // saved as done="1"; Excel shows the thread resolved
Person := Book.Persons.FindByDisplayName('Maria Ortiz');
if Person <> nil then
Person.UserId := 'maria.ortiz@example.com';
end;
ขอบเขตหนึ่งที่ต้องกล่าวอย่างชัดเจน: HotXLS เขียนองค์ประกอบ <mentions> ว่างเปล่า บันทึก @-mention — กลไกที่ทำให้ชื่อของเพื่อนร่วมงานสว่างขึ้นภายในข้อความความคิดเห็นและทริกเกอร์การแจ้งเตือนใน Microsoft 365 — จะไม่ถูกสร้างแบบจำลอง ดังนั้นข้อความที่มีเครื่องหมาย @ จะถูกจัดเก็บเป็นข้อความธรรมดา สำหรับสมุดงานตรวจสอบที่สร้างขึ้น นั่นแทบจะไม่ใช่การสูญเสียใด ๆ แต่ถ้าเวิร์กโฟลว์ของคุณขึ้นอยู่กับการแจ้งเตือนการกล่าวถึง ให้วางแผนสำหรับบุคคลที่จะเพิ่มสิ่งเหล่านั้นภายใน Excel ตัวตนของผู้สร้างในส่วนของบุคคลจะจับคู่กับฟิลด์แหล่งที่มาในระดับสมุดงานที่ครอบคลุมในข้อมูลเมตาของสมุดงานและคุณสมบัติของเอกสาร ซึ่งเป็นที่ที่ส่วนที่เหลือของบันทึกการตรวจสอบไฟล์ที่สร้างขึ้นตั้งอยู่
Excel เวอร์ชันเก่าจัดการกับ threaded comment อย่างไร?
Excel 2016 และรุ่นก่อนหน้ามีมาก่อนโมเดลแบบเธรด และจะละเว้นส่วนประกอบที่พวกเขาไม่เข้าใจโดยสิ้นเชิง เมื่อ Excel 365 บันทึกการสนทนาแบบเกลียว มันจะเขียนเงาความคิดเห็นแบบเดิมด้วย — บันทึกย่อตัวยึดตำแหน่งที่อ่านว่า "[Threaded comment]..." — เพื่อให้รุ่นเก่าแสดงผลบางอย่างในเซลล์ที่ยึดไว้ HotXLS จะแสดงส่วนที่มีเกลียวเท่านั้น โดยไม่มีเงาดั้งเดิม ดังนั้นไฟล์ที่เขียนโดยโค้ดของคุณจะไม่แสดงตัวบ่งชี้ความคิดเห็นเลยเมื่อเปิดใน Excel 2016 หากผู้ชมของคุณรวมถึงการติดตั้งก่อนรุ่น 365 และคำอธิบายประกอบต้องมองเห็นได้ที่นั่น API แบบเดิม AddComment ยังคงเป็นช่องทางที่ใช้งานร่วมกันได้ เพียงหลีกเลี่ยงการซ้อนโมเดลทั้งสองบนเซลล์เดียวกันโดยไม่ได้ทดสอบ Excel ทุกเวอร์ชันที่คุณจัดส่ง เนื่องจากวิธีที่โปรแกรมอ่านปรับยอดบันทึกย่อและเธรดบนเซลล์เดียวคือการตัดสินใจของโปรแกรมอ่าน ไม่ใช่ของคุณ
ขอบเขตที่ยากลำบากอีกประการหนึ่งคือรูปแบบไฟล์เอง Threaded comments เป็นโครงสร้าง OOXML ที่ไม่มีคู่เปรียบเทียบใน BIFF8 ดังนั้นฝั่ง .xls ของ HotXLS จึงไม่มี API ความคิดเห็นแบบเกลียว — สมุดงานที่ต้องคงอยู่ในรูปแบบไบนารีดั้งเดิมจะจำกัดอยู่แค่บันทึกย่อแบบดั้งเดิม ในทางปฏิบัติ ตารางการตัดสินใจมีขนาดสั้น: การเขียน .xlsx สำหรับ Excel 365 หรือโปรแกรมอ่าน Excel บนเว็บ/มือถือ ให้ใช้ AddThreadedComment และรับการสนทนาจริง การกำหนดเป้าหมาย Excel 2016 หรือ .xls ให้ใช้ AddComment และยอมรับโมเดลบันทึกย่อ การตรวจสอบไฟล์ที่คุณได้รับ ให้ตรวจสอบทั้ง ThreadedComments และ Comments เนื่องจากสมุดงานที่มีชีวิตอยู่ในทั้งสองโลกสามารถพกพาได้ทั้งสองอย่างถูกต้องตามกฎหมาย พื้นผิว API ทั้งหมดสำหรับทั้งสองรุ่นมีเอกสารระบุไว้ในหน้าผลิตภัณฑ์ HotXLS Delphi Excel Component