การสร้างสรรค์เมนูสุดแปลกของ DELPHI
ผู้แปล: Li Junyu อีเมล: [email protected],[email protected]
เมนูแบบกำหนดเอง ข้อความ เส้น / Delphi 4, 5
เมนูกำหนดเอง ข้อความ บรรทัด/Delphi 4, 5
เมนูแฟนซี ฯลฯ
เมนูแปลกๆ ฯลฯ
เมนูแบบกำหนดเอง ข้อความที่หมุน และบรรทัดพิเศษ
เมนูที่กำหนดเอง ข้อความที่หมุน และบรรทัดพิเศษ
ก่อน Delphi 4 การปรับแต่งเมนูเป็นเรื่องยาก (เพิ่มบิตแมป เปลี่ยนแบบอักษร ฯลฯ) เนื่องจากภาพวาดของเจ้าของ (เช่น การวาดภาพแบบกำหนดเอง) - แม้ว่าใช้งานโดย Windows - ไม่ได้ถูกเปิดเผยโดยคลาส TMainMenu อย่างไรก็ตาม สถานการณ์นี้ได้รับการแก้ไขแล้ว และเราสามารถดำเนินการตามเมนูต่างๆ ได้
ก่อน Delphi 4 การปรับแต่งเมนูเป็นเรื่องยาก (เช่น การเพิ่มรูปภาพ BMP การเปลี่ยนแบบอักษร ฯลฯ) เนื่องจากเหตุการณ์การวาดภาพของเจ้าของ (นั่นคือ เหตุการณ์การวาดภาพแบบกำหนดเอง) - แม้ว่าจะดำเนินการโดย Windows แต่ก็ไม่ปรากฏใน คลาส TMainMenu ตั้งแต่ Delphi 4
สถานการณ์นี้มีการเปลี่ยนแปลง และตอนนี้เราสามารถปรับแต่งเมนูได้แล้ว
บทความนี้จะเน้นเทคนิคบางอย่างที่คุณสามารถใช้เพื่อปรับแต่งลักษณะที่ปรากฏของเมนูในแอปพลิเคชัน Delphi ของคุณ เราจะหารือเกี่ยวกับการวางตำแหน่งข้อความ การกำหนดขนาดเมนู การกำหนดแบบอักษร และการใช้บิตแมปและรูปร่างเพื่อปรับปรุงลักษณะที่ปรากฏของเมนู ยังมีเทคนิคในการสร้างข้อความแบบหมุนและบรรทัดที่กำหนดเอง เทคนิคทั้งหมดที่กล่าวถึงในบทความนี้แสดงอยู่ใน PRojects ที่พร้อมให้ดาวน์โหลด
บทความนี้จะเน้นไปที่เทคนิคบางอย่างที่คุณสามารถใช้เพื่อปรับแต่งรูปลักษณ์ของเมนูในแอปพลิเคชัน DELPHI ของคุณ เราจะครอบคลุมถึงการวางตำแหน่งข้อความ การกำหนดขนาดเมนู การตั้งค่าแบบอักษร และการปรับปรุงด้วยไฟล์ BMP และตัวควบคุม SHAPE . เพื่อความสนุกสนาน บทความนี้จะนำเสนอเทคนิคระยะใกล้ในการหมุนข้อความและบรรทัดที่กำหนดเอง เทคนิคทั้งหมดที่กล่าวถึงในบทความนี้ได้รับการแก้ไขข้อบกพร่องในไฟล์โปรเจ็กต์แล้ว และสามารถดาวน์โหลดได้ทางออนไลน์
แบบอักษรและขนาดที่กำหนดเอง
ตั้งค่าแบบอักษรและขนาด
หากต้องการสร้างเมนูแบบกำหนดเอง ให้ตั้งค่าคุณสมบัติ OwnerDraw ของส่วนประกอบเมนู -TMainMenu หรือ TPopupMenu - เป็น True และจัดเตรียมตัวจัดการเหตุการณ์สำหรับเหตุการณ์ OnDrawItem และ OnMeasureItem ตัวอย่างเช่น ตัวจัดการเหตุการณ์ OnMeasureItem จะถูกประกาศดังนี้:
หากต้องการสร้างเมนูแบบกำหนดเอง ให้ตั้งค่าคุณสมบัติ OwnerDraw ของส่วนประกอบ TmainMenu หรือ TpopupMenu เป็น TRUE และสร้างขั้นตอนเหตุการณ์ OnDrawItem และ OnMeasureItem ตัวอย่างเช่น กระบวนการเหตุการณ์ OnMeasureItem สามารถประกาศได้ดังนี้:
ขั้นตอน TForm1.Option1MeasureItem (ผู้ส่ง: TObject;
ACanvas: TCanvas; var ความกว้าง, ความสูง: จำนวนเต็ม);
ตั้งค่าตัวแปรความกว้างและความสูงเพื่อปรับขนาดของรายการเมนู ตัวจัดการเหตุการณ์ OnDrawItem คือที่ที่คุณทำงานหนักทั้งหมด เป็นที่ที่คุณวาดเมนูและทำการตั้งค่าพิเศษใดๆ เพื่อวาดตัวเลือกเมนูด้วยแบบอักษร Times New Roman ตัวอย่างเช่น คุณควรดำเนินการดังนี้:
ตั้งค่าตัวแปรความกว้างและความสูงของรายการเมนูในขั้นตอนเหตุการณ์ด้านบนให้เป็นขนาดที่เหมาะสม สิ่งสำคัญทั้งหมดจะถูกทริกเกอร์โดยเหตุการณ์ OnDrawItem นี่คือที่ที่คุณจะวาดเมนูใหม่และทำการตั้งค่าพิเศษ ตัวอย่างเช่น หากต้องการวาดรายการเมนูใหม่ในแบบอักษร Times New Roman คุณจะต้องดำเนินการดังต่อไปนี้:
ขั้นตอน TForm1.Times1DrawItem (ผู้ส่ง: TObject;
ACanvas: TCanvas; ARect: TRect เลือกแล้ว: บูลีน);
เริ่ม
ACanvas.Font.Name := 'ไทม์นิวโรมัน';
ACanvas.TextOut(ARect.ซ้าย+1, ARect.Top+1,
(ผู้ส่งเป็น TMenuItem).คำบรรยายภาพ);
จบ;
อย่างไรก็ตาม รหัสนี้มีข้อบกพร่อง หากใช้งาน คำบรรยายเมนูจะถูกวาดให้ชิดกับขอบด้านซ้ายของเมนู คุณควรคำนวณพื้นที่ที่จำเป็นสำหรับเครื่องหมายถูกนี้ด้วยโค้ดเหมือนกับที่แสดงในรูปที่ 1 รูปที่ 2 แสดงเมนูผลลัพธ์
อย่างไรก็ตามรหัสนี้มีข้อบกพร่อง หากคุณเรียกใช้โค้ดนี้ คำบรรยายของรายการเมนูจะถูกจัดชิดทางด้านซ้ายของรายการเมนู นี่ไม่ใช่ลักษณะการทำงานเริ่มต้นของ Windows โดยปกติจะมีช่องว่างทางด้านซ้ายของเมนูสำหรับรูปภาพ BMP และส่วนที่เลือก เครื่องหมาย. ดังนั้นคุณควรใช้โค้ดเพื่อคำนวณว่าต้องใช้พื้นที่เท่าใดในการวางธงการเลือก ดังแสดงในรูปที่ 1 รูปที่ 2 แสดงเมนูที่ใช้งานจริง
ขั้นตอน TForm1.Times2DrawItem (ผู้ส่ง: TObject;
ACanvas: TCanvas; ARect: TRect เลือกแล้ว: บูลีน);
var
dwCheck : จำนวนเต็ม;
คำบรรยายเมนู: string;
เริ่ม
// รับขนาดเครื่องหมายถูก
รับจำนวนพิกเซลที่จำเป็นสำหรับโลโก้การเลือก
dwCheck := GetSystemMetrics(SM_CXMENUCHECK);
// ปรับตำแหน่งด้านซ้าย
ปรับตำแหน่งด้านซ้าย
ARect.Left := ARect.Left + LoWord(dwCheck) + 1;
MenuCaption := (ผู้ส่งเป็น TMenuItem).Caption;
// ชื่อฟอนต์คือคำบรรยายเมนู
ACanvas.Font.Name := 'ไทม์นิวโรมัน';
// วาดข้อความ
วาดข้อความ
DrawText(ACanvas.Handle, PChar(MenuCaption))
ความยาว (คำบรรยายเมนู), ARect, 0);
จบ;
รูปที่ 1: ตัวจัดการเหตุการณ์ OnDrawItem นี้วางข้อความรายการเมนูอย่างถูกต้อง
[ผู้แปลละเว้นรูปภาพทั้งหมด เช่นเดียวกับด้านล่าง]
รูปที่ 2: เมนูที่วาดด้วยแบบอักษรที่กำหนดเอง
หากข้อความมีขนาดใหญ่เกินกว่าจะวาดในเมนู Windows จะตัดให้พอดี ดังนั้น คุณควรตั้งค่าขนาดรายการเมนูเพื่อให้สามารถวาดข้อความทั้งหมดได้ นี่คือบทบาทของตัวจัดการเหตุการณ์ OnMeasureItem ที่แสดงในรูปที่ 3 .
หากข้อความยาวเกินไป Windows จะครอบตัดข้อความให้พอดีโดยอัตโนมัติ ดังนั้นคุณควรปรับขนาดเมนูเพื่อให้สามารถแสดงข้อความทั้งหมดได้ สิ่งเดียวกันควรจะเป็นจริงในเหตุการณ์ OnMeasureItem ดังแสดงในรูปที่ 3
ขั้นตอน TForm1.Times2MeasureItem (ผู้ส่ง: TObject;
ACanvas: TCanvas; var ความกว้าง, ความสูง: จำนวนเต็ม);
เริ่ม
ACanvas.Font.Name := 'ไทม์นิวโรมัน';
ACanvas.Font.Style := [];
// ความกว้างคือช่องว่างของการตรวจสอบเมนู
ความยาวนี้คือความยาวของเครื่องหมายเลือกเมนู
// บวกความกว้างของข้อความรายการ
บวกกับความยาวของรายการเมนู
ความกว้าง := GetSystemMetrics(SM_CXMENUCHECK) +
ACanvas.TextWidth((ผู้ส่งเป็น TMenuItem).คำบรรยายภาพ) + 2;
ความสูง := ACanvas.TextHeight(
(ผู้ส่งเป็น TMenuItem).คำบรรยาย) + 2;
จบ;
รูปที่ 3: ตัวจัดการเหตุการณ์ OnMeasureItem นี้ช่วยให้มั่นใจได้ว่ารายการจะพอดีกับเมนู
รูปร่างและบิตแมปที่กำหนดเอง
ตั้งค่ากราฟิกและบิตแมป
นอกจากนี้ยังสามารถปรับแต่งรายการเมนูโดยรวมบิตแมปหรือรูปร่างอื่นๆ ได้ด้วย หากต้องการเพิ่มบิตแมป เพียงกำหนดไฟล์บิตแมปให้กับคุณสมบัติ TMenuItem.Bitmap โดยใช้ Object Inspector ในขณะออกแบบ หรือด้วยโค้ด ณ รันไทม์ เพื่อวาดรูปสี่เหลี่ยมสี เป็นคำอธิบายของรายการเมนู คุณสามารถใช้ตัวจัดการเหตุการณ์ OnDrawItem ดังแสดงในรูปที่ 4 รูปที่ 5 แสดงผลลัพธ์
คุณสามารถตั้งค่าเมนูด้วยบิตแมปและกราฟิกอื่นๆ ได้ หากต้องการเพิ่มบิตแมป เพียงกำหนดไฟล์ BMP ให้กับคุณสมบัติบิตแมปของ TmenuItem ใน Object Inspector ในขณะออกแบบ หรือใช้โค้ดขณะรันไทม์ก็ได้ หากต้องการแทนที่ชื่อเมนูด้วยสี่เหลี่ยมสี คุณสามารถใช้เหตุการณ์ OnDrawItem ได้ ดังแสดงในรูปที่ 4 แสดงในรูปที่ 5 คือผลลัพธ์
ขั้นตอน TForm1.ColorDrawItem (ผู้ส่ง: TObject;
ACanvas: TCanvas; ARect: TRect เลือกแล้ว: บูลีน);
var
dwCheck : จำนวนเต็ม;
เมนูสี: TColor;
เริ่ม
// รับขนาดเครื่องหมายถูก
dwCheck := GetSystemMetrics(SM_CXMENUCHECK);
ARect.Left := ARect.Left + LoWord(dwCheck);
// แปลงคำอธิบายของรายการเมนูให้เป็นสี
แปลงชื่อรายการเมนูให้เป็นสี
เมนูสี :=
StringToColor((ผู้ส่งเป็น TMenuItem).คำบรรยาย);
// เปลี่ยนสีแปรงผ้าใบ
เปลี่ยนสีแปรงของผืนผ้าใบ
ACanvas.Brush.Color := MenuColor;
// วาดรูปสี่เหลี่ยมหากเลือกรายการ
วาดรูปสี่เหลี่ยมผืนผ้าหากเลือกรายการเมนู
// วาดเส้นขอบ
วาดเส้นขอบ
ถ้าเลือกแล้ว
ACanvas.Pen.Style := psSolid
อื่น
ACanvas.Pen.Style := psClear;
ACanvas.Rectangle (ARect.ซ้าย, ARect.Top,
ARect.Right, ARect.Bottom);
จบ;
รูปที่ 4: การใช้เหตุการณ์ OnDrawItem เพื่อวาดรูปสี่เหลี่ยมสีบนรายการเมนู
รูปที่ 5: เมนูที่มีสี่เหลี่ยมสีเป็นรายการ
มีเพียงสิ่งเดียวเท่านั้น หากคุณใช้ Delphi 5 คุณต้องตั้งค่าคุณสมบัติ AutoHotkeys ของเมนูเป็น maManual หากคุณปล่อยให้เป็นค่าเริ่มต้น maAutomatic Delphi จะเพิ่มอักขระเครื่องหมายและ (&) ให้กับคำบรรยายภาพซึ่งจะทำให้สิ่งนี้เสียหาย รหัสอีกวิธีหนึ่งคือการลบเครื่องหมายและด้วยฟังก์ชัน StripHotKey
แนวทางที่ได้รับความนิยมมากกว่าคือหากคุณใช้ Delphi 5 คุณควรตั้งค่าคุณสมบัติ AutoHotkeys ของเมนูเป็น maManual หากคุณไม่ทำเช่นนี้และปล่อยให้ค่าเริ่มต้นเป็น maAutomatic ทาง Delphi จะเพิ่มเครื่องหมายและในชื่อโดยอัตโนมัติ ซึ่งจะทำให้โค้ดเสียหาย วิธีแก้ไขปัญหาอื่นคือการใช้ฟังก์ชัน StripHotKey เพื่อลบเครื่องหมายและ
อีกวิธีหนึ่งในการใช้เหตุการณ์ OnDrawItem และ OnMeasureItem คือการเขียนข้อความในแนวตั้งบนเมนู (ดังแสดงในรูปที่ 7) เมื่อต้องการทำเช่นนี้ คุณต้องสร้างแบบอักษรที่หมุนได้ ซึ่งสามารถทำได้โดยใช้ฟังก์ชัน Windows API CreateFont หรือ CreateLogFont (ดู เคล็ดลับ "ข้อความที่หมุน" ในภายหลังในบทความนี้) จากนั้นคุณต้องวาดในตัวจัดการเหตุการณ์ OnDrawItem เหตุการณ์นี้จะเริ่มทำงานทุกครั้งที่มีการวาดรายการเมนู ดังนั้นหากมีเมนู 20 รายการจะถูกวาด 20 ครั้ง เพื่อให้เร็วขึ้น ข้อความแนวตั้งจะถูกวาดเฉพาะเมื่อเลือกรายการเมนู (เนื่องจากมีการเลือกรายการเมนูเพียงรายการเดียวในแต่ละครั้ง) ภาพที่ 6 แสดงวิธีการนำไปใช้กับโค้ด และรูปที่ 7 แสดงผลรันไทม์
การใช้งานอีกประการหนึ่งสำหรับเหตุการณ์ OnDrawItem และ OnMeasureItem คือการเขียนข้อความแนวตั้งถัดจากเมนู (ดังแสดงในรูปที่ 7) เพื่อที่จะทำสิ่งนี้ คุณจะต้องสร้างแบบอักษรที่หมุนได้ วิธีเดียวคือใช้ฟังก์ชัน CreateFont หรือ CreateLogFont ของ Windows API (ดูเทคนิค "ข้อความที่หมุน" ในบทความนี้) ดังนั้นคุณต้องวาดมันใหม่ในเหตุการณ์ OnDrawItem เหตุการณ์นี้จะดำเนินการเมื่อรายการเมนูถูกดึงออกมา ดังนั้นหากเมนูมี 20 รายการ ก็จะถูกดำเนินการ 20 ครั้ง เพื่อให้เร็วขึ้น คุณสามารถวาดข้อความแนวตั้งใหม่ได้ทุกครั้งที่เลือกรายการเมนู (แม้ว่าจะเลือกเพียงรายการเมนูครั้งละหนึ่งรายการเท่านั้น) รูปที่ 6 แสดงวิธีการทำงานของโค้ด ในขณะที่รูปที่ 7 แสดงผลลัพธ์
ขั้นตอน TForm1.VerticalDrawItem (ผู้ส่ง: TObject;
ACanvas: TCanvas; ARect: TRect เลือกแล้ว: บูลีน);
var
lf : TLogFont;
OldFont:HFont;
clFore, clBack: LongInt;
สี่เหลี่ยมผืนผ้า: TRect;
dwCheck : LongInt;
MenuHeight : จำนวนเต็ม;
เริ่ม
dwCheck := GetSystemMetrics(SM_CXMENUCHECK);
// สิ่งนี้จะเกิดขึ้นครั้งเดียวเมื่อเลือกรายการแล้ว
สิ่งนี้จะถูกดำเนินการเมื่อรายการเมนูถูกเลือก
หากเลือกแล้วให้เริ่มต้น
// สร้างแบบอักษรที่หมุนได้
สร้างแบบอักษรแบบหมุน
FillChar(lf, SizeOf(lf), 0);
lf.lfHeight := -14;
lf.lfEscapement := 900;
lf.lfOrientation := 900;
lf.lfWeight := Fw_Bold;
StrPCopy(lf.lfFaceName, 'Arial');
// เลือกแบบอักษรนี้เพื่อวาด
เลือกแบบอักษรนี้เพื่อวาด
OldFont := SelectObject(ACanvas.Handle,
CreateFontIndirect(lf));
// เปลี่ยนสีพื้นหน้าและพื้นหลัง
เปลี่ยนสีพื้นหน้าและพื้นหลัง
clFore := SetTextColor(ACanvas.Handle, clSilver);
clBack := SetBkColor(ACanvas.Handle, clBlack);
// รับความสูงของเมนู
รับความสูงของเมนู
MenuHeight := (ARect.Bottom-ARect.Top) *
((ผู้ส่งเป็น TMenuItem).พาเรนต์เป็น TMenuItem).นับ;
สี่เหลี่ยม := สี่เหลี่ยม (-1, 0, dwCheck-1, MenuHeight);
// วาดข้อความ
วาดข้อความ
ExtTextOut(ACanvas.Handle, -1, ความสูงของเมนู, Eto_Clipped,
@Rectang, 'Made in Borland', 15, ไม่มี);
// กลับคืนสู่สภาพเดิม
กลับคืนสู่สภาพเดิม
DeleteObject(SelectObject(ACanvas.Handle, OldFont));
SetTextColor(ACanvas.Handle, clFore);
SetBkColor(ACanvas.Handle, clBack);
จบ;
// วาดข้อความเมนูจริง
วาดข้อความรายการเมนูจริง
ARect.Left := ARect.Left + LoWord(dwCheck) + 2;
DrawText(ACanvas.Handle,
PChar((ผู้ส่งเป็น TMenuItem).คำบรรยาย),
ความยาว ((ผู้ส่งเป็น TMenuItem) คำบรรยาย), ARect, 0);
จบ;
รูปที่ 6: การใช้ OnDrawItem เพื่อวาดข้อความแนวตั้งบนเมนู
รูปที่ 7: เมนูพร้อมข้อความแนวตั้ง
รายละเอียดที่ยุ่งยากประการหนึ่งคือการรู้ว่าจะเริ่มวาดข้อความที่ไหน ควรเริ่มต้นที่ด้านล่างของรายการสุดท้ายบนเมนู เพื่อให้ได้ตำแหน่งเราจะได้ความสูงของรายการเมนูโดยใช้:
คุณควรรู้ว่าจะเริ่มวาดข้อความได้ที่ไหน ควรเริ่มต้นที่ด้านล่างของรายการสุดท้ายในเมนู เพื่อให้ได้ตำแหน่งนี้ เราจะได้ความสูงของรายการเมนูดังนี้
ARect.บน - ARect.ล่าง
และคูณด้วยจำนวนรายการในเมนู:
และคูณด้วยจำนวนรายการเมนู:
(((ผู้ส่งเป็น TMenuItem).พาเรนต์เป็น TMenuItem).นับ)
ข้อความที่หมุน
ข้อความที่หมุนแล้ว
Windows API ช่วยให้คุณสามารถวาดข้อความได้ทุกมุม ในการดำเนินการนี้ใน Delphi คุณต้องใช้ฟังก์ชัน API CreateFont หรือ CreateFontIndirect ดังแสดงในรูปที่ 8
Windows API ช่วยให้คุณสามารถวาดข้อความได้ทุกมุม ในการดำเนินการนี้ใน Delphi คุณต้องใช้ฟังก์ชัน API สองฟังก์ชัน CreateFont หรือ CreateFontIndirect รูปที่ 8 แสดงวิธีการประกาศ CreateFont
ฟังก์ชั่น CreateFont(
nHeight, // ความสูงแบบลอจิคัลของฟอนต์
nWidth // ความกว้างของอักขระเฉลี่ยแบบลอจิคัล
nEscapement // มุมของการหลบหนี
nOrientation // มุมการวางแนวของเส้นฐาน
fnWeight: Integer; // น้ำหนักแบบอักษร
fdwItalic, // ตั้งค่าสถานะตัวเอียงหรือไม่?
fdwUnderline // ขีดเส้นใต้การตั้งค่าแอตทริบิวต์
fdwStrikeOut, // ตั้งค่าสถานะแอตทริบิวต์ขีดฆ่า
fdwCharSet // ตัวระบุชุดอักขระ
fdwOutputPrecision // ความแม่นยำของเอาต์พุต
fdwClipPrecision // ความแม่นยำในการตัดภาพ
fdwQuality // คุณภาพเอาต์พุต
fdwPitchAndFamily: DWORD; // สนามและครอบครัว
lpszFace: PChar // ตัวชี้ไปยังสตริงชื่อแบบอักษร
): HFONT; stdcall;
รูปที่ 8: การประกาศ Object Pascal สำหรับฟังก์ชัน CreateFont Windows API
แม้ว่าฟังก์ชันนี้จะมีพารามิเตอร์มากมาย แต่โดยปกติแล้วคุณจะต้องการเปลี่ยนแอตทริบิวต์ของข้อความเพียงหนึ่งหรือสองรายการเท่านั้น ในกรณีเช่นนี้ คุณควรใช้ฟังก์ชัน CreateFontIndirect แทน โดยจะใช้เพียงอาร์กิวเมนต์เดียวเท่านั้น - บันทึกประเภท TLogFont ดังแสดงในรูป 9.
แม้ว่าฟังก์ชันนี้จะใช้พารามิเตอร์หลายตัว แต่โดยปกติแล้วคุณจะต้องเปลี่ยนคุณสมบัติของข้อความเพียงหนึ่งหรือสองรายการเท่านั้น ในกรณีนี้ คุณจะต้องใช้ฟังก์ชัน CreateFontIndirect แทน ต้องการเพียงพารามิเตอร์เดียว - พารามิเตอร์ประเภทบันทึกของ TlogFont ดังแสดงในรูปที่ 9
tagLOGFONTA = บันทึกที่อัดแน่น
ความสูง: Longint;
lfWidth: ยาว;
lfEscapement: Longint;
การวางแนว: Longint;
lน้ำหนัก: ลองจินต์;
ตัวเอียง: ไบต์;
lfUnderline: ไบต์;
lfStrikeOut: ไบต์;
lfCharSet: ไบต์;
lfOutPrecision: ไบต์;
lfClipPrecision: ไบต์;
คุณภาพ: ไบต์;
lfPitchAndFamily: ไบต์;
lfFaceName: อาร์เรย์ [0..LF_FACESIZE - 1] ของ AnsiChar;
จบ;
TLogFontA = tagLOGFONTA;
TLogFont = TLogFontA;
รูปที่ 9: บันทึก TLogFont
เมื่อดูที่เรกคอร์ดนี้ คุณจะสังเกตเห็นว่าสมาชิกตรงกับพารามิเตอร์สำหรับฟังก์ชัน CreateFont ข้อดีของการใช้ฟังก์ชัน/ชุดเรกคอร์ดนี้คือ คุณสามารถเติมสมาชิกของเรกคอร์ดด้วยแบบอักษรที่รู้จักโดยใช้ฟังก์ชัน GetObject API เปลี่ยนสมาชิกที่คุณ ต้องการและสร้างแบบอักษรใหม่
หากคุณดูประเภทเรคคอร์ดนี้อย่างใกล้ชิด คุณจะพบว่าสมาชิกของประเภทเรคคอร์ดนั้นคล้ายคลึงกับพารามิเตอร์ของฟังก์ชัน CreateFont มาก ข้อดีของการใช้การรวมฟังก์ชัน/บันทึกนี้คือ คุณสามารถใช้ฟังก์ชัน GetObject API เพื่อเติมค่าสมาชิกของบันทึกนี้ด้วยแบบอักษรที่รู้จัก จากนั้นเปลี่ยนค่าสมาชิกที่คุณต้องการเปลี่ยนเพื่อสร้างแบบอักษรใหม่
ในการวาดข้อความที่หมุน สมาชิกเดียวที่คุณต้องเปลี่ยนคือ lfEscapement ซึ่งตั้งค่ามุมข้อความเป็นสิบองศา ดังนั้น หากคุณต้องการให้ข้อความวาดที่ 45 องศา คุณต้องตั้งค่า lfEscapement เป็น 450
ในการวาดข้อความที่หมุน สมาชิกเดียวที่คุณต้องเปลี่ยนคือ lfEscapement ซึ่งกำหนดมุมของแบบอักษรเป็นสิบองศา ดังนั้นถ้าคุณต้องการให้ตัวละครหมุน 45 องศา คุณต้องตั้งค่า
lfEscapement คือ 450
โปรดสังเกตว่ามีธงให้เขียนตัวเอียง ขีดเส้นใต้ และขีดฆ่าข้อความ แต่ไม่มีธงให้เขียนข้อความตัวหนา ซึ่งทำได้ด้วยสมาชิก lfWeight ตัวเลขระหว่าง 0 ถึง 1000 400 เป็นข้อความปกติ ค่าที่สูงกว่านี้ วาดข้อความตัวหนาและค่าด้านล่างจะวาดข้อความสีอ่อน
โปรดสังเกตว่ามีเครื่องหมายสองสามตัวสำหรับทำให้ตัวเอียง ขีดเส้นใต้ และเน้นข้อความ แต่ไม่มีเครื่องหมายสำหรับทำให้ข้อความเป็นตัวหนา เนื่องจากมีการใช้สมาชิก lfWeight แทน และค่าของสมาชิกนี้อยู่ระหว่าง 0 ถึง 1000 400 เป็นค่าปกติ อะไรก็ตามที่อยู่เหนือสิ่งนี้จะเป็นตัวหนา และสิ่งใดที่ต่ำกว่านี้จะถือว่าบาง
โค้ดในรูปที่ 10 วาดข้อความที่มุมตั้งแต่ 0 องศาถึง 360 องศา ในช่วงเวลา 20 องศา เป็นตัวจัดการเหตุการณ์ OnPaint ของแบบฟอร์ม ดังนั้นข้อความจะถูกวาดใหม่ทุกครั้งที่ทาสีแบบฟอร์ม
รหัสในรูปที่ 10 ดึงอักขระทุก ๆ 20 องศาตั้งแต่ 0 ถึง 360 องศา สิ่งนี้จะเกิดขึ้นในเหตุการณ์ OnPaint ของแบบฟอร์ม ดังนั้นข้อความจะถูกวาดใหม่ทุกครั้งที่ทาสีแบบฟอร์ม ผลกระทบสามารถเห็นได้ในรูปที่ 11
ขั้นตอน TForm1.FormPaint (ผู้ส่ง: TObject);
var
OldFont, NewFont: hFont;
LogFont: TLogFont;
ฉัน : จำนวนเต็ม;
เริ่ม
// รับแฮนเดิลฟอนต์แคนวาส
รับหมายเลขอ้างอิงไปยังวัตถุแบบอักษรของแบบฟอร์ม
OldFont := Canvas.Font.Handle;
ฉัน := 0;
// รูปวาดโปร่งใส
ตั้งค่าคุณสมบัติความโปร่งใส
SetBkMode (Canvas.Handle, โปร่งใส);
// กรอกโครงสร้าง LogFont ด้วยข้อมูล
กรอกโครงสร้าง LogFont ด้วยข้อมูล
// จากฟอนต์ปัจจุบัน
จากแบบอักษรปัจจุบัน
GetObject(OldFont, ขนาดของ (LogFont), @LogFont);
// มุมมีตั้งแต่ 0 ถึง 360
จาก 0 ถึง 360 องศา
ในขณะที่ฉัน < 3600 จะเริ่มต้น
// ตั้งค่าการหลบหนีเป็นมุมใหม่
ตั้งค่าการวางแนวข้อความเป็นมุมใหม่
LogFont.lfEscapement := ฉัน;
//สร้างฟอนต์ใหม่
สร้างแบบอักษรใหม่
NewFont := CreateFontIndirect(LogFont);
// เลือกแบบอักษรที่จะวาด
เลือกแบบอักษรสำหรับเอาต์พุต
SelectObject(Canvas.Handle, NewFont);
// เขียนข้อความตรงกลางแบบฟอร์ม
ข้อความเอาท์พุตอยู่ตรงกลางของแบบฟอร์ม
TextOut (Canvas.Handle, ClientWidth div 2,
ClientHeight div 2, 'ข้อความที่หมุน', 21);
//ทำความสะอาด.
ชัดเจน
DeleteObject(SelectObject(Canvas.Handle, OldFont));
// เพิ่มมุม 20 องศา
เพิ่มขึ้นทุกๆ 20 องศา
อิงค์(i, 200);
จบ;
จบ;
รูปที่ 10: โค้ดสำหรับวาดข้อความที่หมุนในช่วง 20 องศา
รูปที่ 11: ข้อความหมุนได้ 360 องศา
แบบอักษรของแบบฟอร์มถูกตั้งค่าเป็น Arial ซึ่งเป็นแบบอักษร TrueType รหัสนี้ใช้ได้กับแบบอักษร TrueType เท่านั้น ไม่รองรับการหมุนข้อความ หากต้องการรับการตั้งค่าแบบอักษรปัจจุบันและกรอกโครงสร้าง TLogFont คุณต้องใช้ฟังก์ชัน GetObject API รหัสในรูปที่ 12 แสดงวิธีการกรอกและแสดงการตั้งค่า TLogFont สำหรับแบบอักษรของแบบฟอร์ม
แบบอักษรของแบบฟอร์มนี้ถูกตั้งค่าเป็น Arial ซึ่งเป็นแบบอักษร TrueType รหัสนี้ทำงานภายใต้แบบอักษร TrueType เท่านั้น แบบอักษรอื่นไม่รองรับการหมุนข้อความ เพื่อให้ได้การตั้งค่าแบบอักษรปัจจุบันและกรอกโครงสร้าง TlogFont คุณต้องใช้ฟังก์ชัน GetObject API ในโค้ดในรูปที่ 12 คุณสามารถดูวิธีการกรอกและแสดงการตั้งค่า TlogFont ในแบบฟอร์มได้
ขั้นตอน TForm1.Info1Click (ผู้ส่ง: TObject);
var
LogFont: TLogFont;
เริ่ม
// กรอกโครงสร้าง LogFont ด้วยข้อมูล
กรอกค่าสมาชิกของโครงสร้าง LogFont
// จากฟอนต์ปัจจุบัน
จากแบบอักษรปัจจุบัน
GetObject(Canvas.Font.Handle, ขนาดของ(LogFont), @LogFont);
// แสดงข้อมูลแบบอักษร
แสดงข้อมูลแบบอักษร
ด้วย LogFont ทำ ShowMessage(
'lfHeight: ' + IntToStr(lfHeight) + #13 +
'lfWidth: ' + IntToStr(lfWidth) + #13 +
'lfEscapement: '+IntToStr(lfEscapement) + #13 +
'lfOrientation: ' + IntToStr (lfOrientation) + #13 +
'lfWeight: ' + IntToStr(lfWeight) + #13 +
'lfItalic: ' + IntToStr(lfItalic) + #13 +
'lfUnderline: ' + IntToStr(lfUnderline) + #13 +
'lfStrikeOut: ' + IntToStr(lfStrikeOut) + #13 +
'lfCharSet: ' + IntToStr(lfCharSet) + #13 +
'lfOutPrecision: ' + IntToStr(lfOutPrecision) + #13 +
'lfClipPrecision: ' + IntToStr(lfClipPrecision) + #13 +
'lfQuality: ' + IntToStr (lfQuality) + #13 +
'lfPitchAndFamily: '+IntToStr(lfPitchAndFamily) + #13 +
'lfFaceName: ' + สตริง(lfFaceName));
จบ;
รูปที่ 12: การรับและแสดงคุณลักษณะแบบอักษร
เมื่อคุณมีการตั้งค่าในโครงสร้าง TLogFont การเปลี่ยนแปลงเพียงอย่างเดียวคือการตั้งค่า lfEscapement ให้เป็นมุมที่ต้องการและสร้างแบบอักษรใหม่ด้วย CreateFontIndirect ก่อนที่จะใช้แบบอักษรใหม่นี้ จะต้องเลือกด้วย SelectObject อีกวิธีหนึ่งคือการกำหนดหมายเลขอ้างอิง ของแบบอักษรใหม่นี้ไปยังหมายเลขอ้างอิงของแบบอักษรของผืนผ้าใบ ก่อนที่จะวาดข้อความ หลังจากวาดข้อความแล้ว งานนี้จะต้องกลับด้าน จะต้องเลือกแบบอักษรเก่า และลบแบบอักษรใหม่ ไม่ถูกลบ หน่วยความจำจะรั่วไหล และ - หากรูทีนถูกดำเนินการหลายครั้ง - Windows (โดยเฉพาะ 95/98) ทรัพยากรจะหมดและเกิดข้อผิดพลาด
เมื่อคุณตั้งค่าโครงสร้าง TlogFont แล้ว สิ่งเดียวที่ต้องทำคือเปลี่ยนค่าของ lfEscapement เป็นค่าเป้าหมาย และใช้ CreateFontIndirect เพื่อสร้างแบบอักษรใหม่ ก่อนที่จะใช้แบบอักษรใหม่นี้ คุณต้องใช้ SelectObject เพื่อเลือก อีกวิธีหนึ่งคือการใช้หมายเลขอ้างอิงของวัตถุแบบอักษรใหม่นี้กับหมายเลขอ้างอิงของวัตถุแบบอักษรของผืนผ้าใบของแบบฟอร์มก่อนที่จะวาดข้อความ หลังจากวาดข้อความแล้ว กระบวนการจะเริ่มต้นขึ้น โดยต้องเลือกแบบอักษรเก่าและลบแบบอักษรใหม่ หากไม่ลบแบบอักษรใหม่ออก จะทำให้เกิดหน่วยความจำรั่ว และ ----- หากโปรแกรมถูกเรียกใช้งานหลายครั้ง------ Windows (โดยเฉพาะ 95/98) จะหมดทรัพยากร และ
ชน.
เส้นมีสไตล์
สายยอดนิยม
เมื่อคุณวาดเส้น แต่ละพิกเซลไม่สำคัญ คุณเพียงแค่ตั้งค่ารูปแบบเส้น และ Windows เป็นผู้วาดเส้นนั้นเอง โดยใช้ฟังก์ชัน Windows API ชื่อ LineDDA ซึ่งกำหนดไว้ในรูปที่ 13
เมื่อคุณวาดเส้น แต่ละพิกเซลมักจะไม่สำคัญ เพียงคุณกำหนดประเภทของเส้น จากนั้น Windows จะต้องวาดเส้นนั้น อย่างไรก็ตาม บางครั้งคุณอาจต้องการสร้างบรรทัดพิเศษบางประเภทที่ Windows ไม่มีให้ ซึ่งสามารถทำได้โดยใช้ฟังก์ชัน API ที่เรียกว่า LineDDA ซึ่งสามารถดูคำจำกัดความได้ในรูปที่ 13
ฟังก์ชั่น LineDDA(
nXStart, // พิกัด x ของจุดเริ่มต้นของบรรทัด
จุดเริ่มต้นพิกัด X
nYStart, // พิกัด y ของจุดเริ่มต้นของบรรทัด
จุดเริ่มต้นพิกัด Y
nXEnd, // พิกัด x ของจุดสิ้นสุดของบรรทัด
จุดสิ้นสุดพิกัด X
Yend : จำนวนเต็ม; // พิกัด y ของจุดสิ้นสุดของบรรทัด
จุดสิ้นสุดพิกัด Y
// ที่อยู่ของฟังก์ชันโทรกลับที่กำหนดโดยแอปพลิเคชัน
ที่อยู่ของฟังก์ชันโทรกลับที่กำหนดโดยแอปพลิเคชัน
lpLineFunc: TFNLineDDAProc;
lpData : LPARAM // ที่อยู่ของข้อมูลที่กำหนดโดยแอปพลิเคชัน
ที่อยู่ของข้อมูลที่กำหนดโดยแอปพลิเคชัน
): BOOL;
รูปที่ 13: การประกาศ Object Pascal สำหรับฟังก์ชัน Windows API, LineDDA
พารามิเตอร์สี่ตัวแรกคือจุดเริ่มต้นและจุดสิ้นสุดของบรรทัด พารามิเตอร์ที่ห้าคือฟังก์ชันการโทรกลับที่จะถูกเรียกทุกครั้งที่มีการวาดพิกเซล ส่งผ่านไปยังฟังก์ชันการโทรกลับ คุณสามารถส่งจำนวนเต็มหรือตัวชี้ไปยังฟังก์ชันได้ เนื่องจากเป็น LParam (ใน Win32 จะถูกแปลเป็น Longint) ฟังก์ชันการโทรกลับต้องใช้แบบฟอร์มที่แสดงไว้ที่นี่:
พารามิเตอร์สี่ตัวแรกคือจุดเริ่มต้นและจุดสิ้นสุดของเส้น พารามิเตอร์ที่ห้าคือฟังก์ชันการโทรกลับซึ่งจะถูกเรียกทุกครั้งที่มีการวาดพิกเซล คุณสามารถเขียนเกี่ยวกับขั้นตอนการวาดภาพของคุณได้ที่นี่ พารามิเตอร์สุดท้ายถูกกำหนดโดยผู้ใช้และสามารถส่งผ่านไปยังฟังก์ชันโทรกลับได้ คุณสามารถส่งจำนวนเต็มหรือตัวชี้ไปยังฟังก์ชันนี้ได้เนื่องจากเป็นเช่นนั้น
ประเภท Lparam (ใน WIN32 จะถูกตีความว่าเป็นประเภท Longint) ฟังก์ชันการเรียกกลับนี้ต้องใช้แบบฟอร์มดังต่อไปนี้:
ขั้นตอน CallBackDDA (x, y: จำนวนเต็ม;
UserParam: LParam);
โดยที่ x และ y คือพิกัดของจุดที่วาด และ UserParam คือพารามิเตอร์ที่ส่งผ่านไปยังฟังก์ชัน ฟังก์ชันนี้จะต้องถูกประกาศเป็น stdcall
โดยที่ X และ Y คือจุดพิกัดที่ถูกวาด และ UserParam คือพารามิเตอร์ ฟังก์ชันนี้จะต้องถูกกำหนดย่อยเป็น stdcall โปรแกรมในรูปที่ 14 พล็อตบรรทัด BMP และรูปที่ 15 จะแสดงผลลัพธ์
พิมพ์
TForm1 = คลาส (TForm)
ImageList1: TImageList;
ขั้นตอน FormPaint (ผู้ส่ง: TObject);
ขั้นตอน FormResize (ผู้ส่ง: TObject);
จบ;
var
แบบฟอร์ม 1: TForm1;
ขั้นตอน CallDDA (x, y: จำนวนเต็ม; แบบฟอร์ม: TForm1);
การดำเนินการ
{ $R *.DFM }
ขั้นตอน CallDDA (x, y: จำนวนเต็ม; แบบฟอร์ม: TForm1);
เริ่ม
ถ้า x mod 13 = 0 แล้ว
Form.ImageList1.Draw(ฟอร์ม.Canvas, x, y, 0);
จบ;
ขั้นตอน TForm1.FormPaint (ผู้ส่ง: TObject);
เริ่ม
LineDDA(0, 0, ความกว้างของไคลเอ็นต์, ความสูงของไคลเอ็นต์,
@CallDDA จำนวนเต็ม (ตนเอง));
จบ;
ขั้นตอน TForm1.FormResize (ผู้ส่ง: TObject);
เริ่ม
ไม่ถูกต้อง;
จบ;
รูปที่ 14: โค้ดสำหรับวาดเส้นบิตแมป
รูปที่ 15: หน้าต่างที่มีเส้นแบบกำหนดเอง
รูทีนนี้จัดการเหตุการณ์ OnPaint ของแบบฟอร์ม โดยเรียก LineDDA ดังนั้นทุกครั้งที่ต้องทาสีแบบฟอร์ม จะวาดเส้นใหม่ อีกเหตุการณ์หนึ่งที่ได้รับการจัดการคือ OnResize ซึ่งทำให้พื้นที่ไคลเอ็นต์ของฟอร์มไม่ถูกต้อง ดังนั้นจึงต้องวาดเส้นใหม่เมื่อมีคนเปลี่ยนแปลง ขนาด ฟังก์ชันการโทรกลับ LineDDA CallDDA นั้นง่ายมาก ทุกๆ จุดที่ 13 จะถูกเรียก มันจะดึงบิตแมปที่เก็บไว้ใน ImageList ดังที่คุณอาจสังเกตเห็นว่า Self ถูกส่งผ่านเป็นพารามิเตอร์สุดท้าย ฟังก์ชั่นการโทรกลับเพื่อให้สามารถเข้าถึงข้อมูลอินสแตนซ์ได้
โปรแกรมนี้จัดการเหตุการณ์ OnPaint ของแบบฟอร์ม โดยเรียก LineDDA ดังนั้นโปรแกรมจะวาดเส้นใหม่ทุกครั้งที่ทาสีแบบฟอร์ม อีกเหตุการณ์หนึ่งคือ OnResize ซึ่งทำให้พื้นที่ไคลเอนต์ของแบบฟอร์มเป็นโมฆะ ดังนั้นเส้นจะถูกวาดใหม่เมื่อมีคนเปลี่ยนขนาด ฟังก์ชั่นการโทรกลับ LineDDA และ CallDDA นั้นง่ายมาก เมื่อใดก็ตามที่เรียก 13 ครั้ง มันจะดึงบิตแมปที่จัดเก็บไว้ใน ImageList คุณอาจสังเกตเห็นว่า SELF ถูกส่งผ่านเป็นพารามิเตอร์สุดท้ายไปยังฟังก์ชัน callback ดังนั้นจึงสามารถเข้าถึงข้อมูลของโปรแกรมได้
บทสรุป
สรุปแล้ว
เนื่องจากภาพวาดของเจ้าของถูกเปิดเผยบน TMainMenu ใน Delphi 4 มีหลายวิธีในการเพิ่มเมนูของคุณ การใช้เทคนิคที่เราได้กล่าวถึงในที่นี้ ช่วยให้คุณสามารถปรับปรุงเมนูของแอปพลิเคชัน Delphi ของคุณได้อย่างง่ายดายด้วยข้อความ บิตแมป และสีที่กำหนดเอง
ขณะนี้ภาพวาดของเจ้าของได้ปรากฏใน TmainMenu ใน Delphi 4 แล้ว อาจมีหลายวิธีในการขยายฟังก์ชันเมนูของคุณ การใช้เทคนิคที่เรากล่าวถึงข้างต้น ช่วยให้คุณสามารถปรับปรุงฟังก์ชันการทำงานของเมนูของแอปพลิเคชัน DELPHI ได้อย่างง่ายดายด้วยข้อความ บิตแมป และสีที่กำหนดเอง