บทความนี้เน้นวิธีการใช้ตัวควบคุม TreeView ตัวควบคุม TreeView มีฟังก์ชันที่หลากหลายและสามารถใช้ได้ในหลายสถานการณ์ สรุป: อธิบายวิธีการเพิ่มฟังก์ชันการเชื่อมโยงข้อมูลให้กับตัวควบคุม TreeView ซึ่งเป็นหนึ่งในชุดตัวอย่างการพัฒนาตัวควบคุม Microsoft Windows คุณสามารถอ่านบทความนี้ร่วมกับบทความภาพรวมที่เกี่ยวข้องได้
การแนะนำ
หากเป็นไปได้ คุณควรเริ่มต้นด้วยการควบคุมที่มีจำหน่ายทั่วไป เนื่องจากการควบคุม Microsoft® Windows® Forms ที่มีให้นั้นมีการเขียนโค้ดและการทดสอบมากมายจนเป็นการสิ้นเปลืองที่จะละทิ้งการควบคุมเหล่านั้นและเริ่มต้นใหม่ทั้งหมด จากสิ่งนี้ ในตัวอย่างนี้ ฉันจะสืบทอดตัวควบคุม Windows Forms ที่มีอยู่ TreeView แล้วปรับแต่งเอง เมื่อคุณดาวน์โหลดโค้ดสำหรับตัวควบคุม TreeView คุณยังได้รับตัวอย่างการพัฒนาตัวควบคุมเพิ่มเติม ตลอดจนแอปพลิเคชันตัวอย่างที่สาธิตวิธีใช้ TreeView ที่ปรับปรุงแล้วกับตัวควบคุมที่ผูกกับข้อมูลอื่นๆ
การออกแบบมุมมองแผนผังการเชื่อมโยงข้อมูล
สำหรับนักพัฒนา Windows การเพิ่มการเชื่อมโยงข้อมูลเข้ากับการควบคุม TreeView เป็นปัญหาทั่วไป แต่เนื่องจากมีความแตกต่างที่สำคัญประการหนึ่งระหว่าง TreeView และการควบคุมอื่นๆ (เช่น ListBox หรือ DataGrid ) (เช่น TreeView จะแสดงข้อมูลแบบลำดับชั้น) การควบคุมพื้นฐาน คุณลักษณะนี้คือ ยังไม่รองรับ (คือเรายังต้องใช้มัน) เมื่อพิจารณาจากตารางข้อมูล จะชัดเจนว่าจะแสดงข้อมูลนั้นใน ListBox หรือ DataGrid ได้อย่างไร แต่การใช้ลักษณะแบบเลเยอร์ของ TreeView เพื่อแสดงข้อมูลเดียวกันนั้นตรงไปตรงมาน้อยกว่า โดยส่วนตัวแล้ว ฉันได้ใช้วิธีการต่างๆ มากมายในการแสดงข้อมูลโดยใช้ TreeView แต่มีวิธีหนึ่งที่ใช้บ่อยที่สุด คือ การจัดกลุ่มข้อมูลในตารางตามฟิลด์บางฟิลด์ ดังแสดงในรูปที่ 1
รูปที่ 1: การแสดงข้อมูลใน TreeView
ในตัวอย่างนี้ ฉันจะสร้างตัวควบคุม TreeView ซึ่งฉันสามารถส่งชุดข้อมูลแบบเรียบ (แสดงในรูปที่ 2) และสร้างผลลัพธ์ที่แสดงในรูปที่ 1 ได้อย่างง่ายดาย
รูปที่ 2: ชุดผลลัพธ์แบบเรียบที่มีข้อมูลทั้งหมดที่จำเป็นในการสร้างแผนผังดังแสดงในรูปที่ 1
ก่อนที่ฉันจะเริ่มเขียนโค้ด ฉันคิดการออกแบบตัวควบคุมใหม่ที่จะจัดการกับชุดข้อมูลเฉพาะนี้ และหวังว่ามันจะใช้ได้กับสถานการณ์อื่นๆ ที่คล้ายคลึงกันอีกมากมาย เพิ่มคอลเลกชันกลุ่มที่มีขนาดใหญ่พอที่จะสร้างโครงสร้างแบบลำดับชั้นโดยใช้ข้อมูลแบบเรียบส่วนใหญ่ ซึ่งคุณระบุฟิลด์การจัดกลุ่ม ฟิลด์การแสดงผล และฟิลด์ค่าสำหรับลำดับชั้นแต่ละระดับ (ฟิลด์ใด ๆ หรือทั้งหมดควรเหมือนกัน) ในการแปลงข้อมูลที่แสดงในรูปที่ 2 เป็น TreeView ที่แสดงในรูปที่ 1 การควบคุมใหม่ของฉันต้องการให้คุณกำหนดระดับการจัดกลุ่มสองระดับ ผู้เผยแพร่และชื่อเรื่อง และกำหนด pub_id เป็นฟิลด์การจัดกลุ่มของกลุ่มผู้เผยแพร่ และ title_id เป็นฟิลด์การจัดกลุ่ม ของฟิลด์การจัดกลุ่ม นอกจากฟิลด์การจัดกลุ่มแล้ว คุณยังต้องระบุฟิลด์การแสดงผลและค่าสำหรับแต่ละกลุ่มเพื่อกำหนดข้อความที่แสดงบนโหนดกลุ่มที่เกี่ยวข้องและค่าที่ระบุกลุ่มเฉพาะโดยไม่ซ้ำกัน เมื่อพบข้อมูลประเภทนี้ ให้ใช้ pub_name/pub_id และ title/title_id เป็นช่องแสดง/ค่าสำหรับทั้งสองกลุ่มนี้ ข้อมูลผู้เขียนจะกลายเป็นโหนดปลายสุดของแผนผัง (โหนดที่ส่วนท้ายของลำดับชั้นการจัดกลุ่ม) และคุณยังต้องระบุฟิลด์ ID ( au_id ) และจอแสดงผล ( au_lname ) สำหรับโหนดเหล่านี้
เมื่อสร้างการควบคุมแบบกำหนดเอง การกำหนดวิธีที่โปรแกรมเมอร์จะใช้การควบคุมก่อนเริ่มการเขียนโค้ดจะช่วยทำให้การควบคุมมีประสิทธิภาพมากขึ้น ในกรณีนี้ ฉันต้องการให้โปรแกรมเมอร์ (ตามข้อมูลที่แสดงไว้ก่อนหน้านี้และผลลัพธ์ที่ต้องการ) สามารถจัดกลุ่มให้สำเร็จด้วยโค้ดสองสามบรรทัดดังนี้:
ด้วย DbTreeControl .ValueMember = au_id .DisplayMember = au_lname .DataSource = myDataTable.DefaultView .AddGroup(ผู้เผยแพร่, pub_id, pub_name, pub_id) .AddGroup(ชื่อเรื่อง, title_id, หัวเรื่อง, title_id) จบด้วย |
หมายเหตุ: นี่ไม่ใช่บรรทัดสุดท้ายของโค้ดที่ฉันเขียน แต่คล้ายกัน ในขณะที่พัฒนาส่วนควบคุม ฉันพบว่าจำเป็นต้องเชื่อมโยงดัชนีรูปภาพใน ImageList ที่เกี่ยวข้องกับ TreeView กับแต่ละระดับการจัดกลุ่ม ดังนั้นฉันจึงต้องเพิ่มพารามิเตอร์เพิ่มเติมให้กับวิธี AddGroup
หากต้องการสร้างแผนผังจริง ฉันจะดูข้อมูลและค้นหาการเปลี่ยนแปลงในฟิลด์ (ระบุเป็นค่าการจัดกลุ่มสำหรับแต่ละการจัดกลุ่ม) ในขณะที่สร้างโหนดการจัดกลุ่มใหม่หากจำเป็น และโหนดลีฟสำหรับแต่ละรายการข้อมูล เนื่องจากโหนดการจัดกลุ่ม จำนวนโหนดทั้งหมดจะมากกว่าจำนวนรายการในแหล่งข้อมูล แต่จะมีโหนดปลายสุดหนึ่งรายการสำหรับแต่ละรายการในข้อมูลพื้นฐาน
รูปที่ 3: โหนดกลุ่มและโหนดลีฟ
ความแตกต่างระหว่างโหนดใบและโหนดกลุ่ม (แสดงในรูปที่ 3) จะมีความสำคัญสำหรับส่วนที่เหลือของบทความนี้ ฉันตัดสินใจที่จะปฏิบัติต่อโหนดทั้งสองประเภทนี้แตกต่างกัน สร้างโหนดแบบกำหนดเองสำหรับโหนดแต่ละประเภท และเพิ่มเหตุการณ์ที่แตกต่างกันตามประเภทโหนดที่เลือก
ดำเนินการผูกข้อมูล
ขั้นตอนแรกในการเขียนโค้ดสำหรับการควบคุมนี้คือการสร้างโปรเจ็กต์และคลาสเริ่มต้นที่สอดคล้องกัน ในตัวอย่างนี้ ฉันสร้าง Windows Control Library ใหม่ก่อน จากนั้นจึงลบคลาส UserControl เริ่มต้นและแทนที่ด้วยคลาสใหม่ที่สืบทอดมาจากคอนโทรล TreeView :
dbTreeControl คลาสสาธารณะ
สืบทอด System.Windows.Forms.TreeView
จากจุดนี้ไป ฉันจะออกแบบตัวควบคุมที่สามารถวางบนแบบฟอร์มและมีรูปลักษณ์และฟังก์ชันการทำงานของ TreeView ทั่วไป ขั้นตอนต่อไปคือการเริ่มเพิ่มโค้ดที่จำเป็นเพื่อจัดการกับฟังก์ชันการทำงานใหม่ที่เพิ่มลงใน TreeView ได้แก่ การผูกข้อมูลและการจัดกลุ่มข้อมูล
เพิ่มคุณสมบัติแหล่งข้อมูล
ฟังก์ชันการทำงานทั้งหมดของตัวควบคุมใหม่ของฉันมีความสำคัญ แต่ประเด็นสำคัญสองประการในการสร้างตัวควบคุมที่ผูกกับข้อมูลที่ซับซ้อนคือการจัดการคุณสมบัติ แหล่งข้อมูล และการดึงข้อมูลแต่ละรายการจากแต่ละออบเจ็กต์ในแหล่งข้อมูล
สร้างรูทีนแอตทริบิวต์
ขั้นแรก การควบคุมใดๆ ที่ใช้ในการผูกข้อมูลที่ซับซ้อนจำเป็นต้องใช้รูทีนคุณสมบัติ DataSource และรักษาตัวแปรสมาชิกที่เหมาะสม:
m_DataSource ส่วนตัวเป็นวัตถุ - แหล่งข้อมูลทรัพย์สินสาธารณะ () เป็นวัตถุ รับ ส่งกลับ m_DataSource จบรับ ตั้งค่า (ค่า ByVal เป็นวัตถุ) ถ้ามูลค่าไม่มีอะไรแล้ว ซม. = ไม่มีอะไร การจัดกลุ่มเปลี่ยนแปลง() อื่น ถ้าไม่ใช่ (ค่า TypeOf คือ IList หรือ _ TypeOf Value คือ IListSource) จากนั้น ' ไม่ใช่แหล่งข้อมูลที่ถูกต้องสำหรับจุดประสงค์นี้ โยน System.Exception ใหม่ (แหล่งข้อมูลไม่ถูกต้อง) อื่น ถ้าค่า TypeOf เป็น IListSource แล้ว หรี่ myListSource เป็น IListSource myListSource = CType (ค่า, IListSource) ถ้า myListSource.ContainsListCollection = True แล้ว โยน System.Exception ใหม่ (แหล่งข้อมูลไม่ถูกต้อง) อื่น 'ใช่ใช่. เป็นแหล่งข้อมูลที่ถูกต้อง m_DataSource = ค่า cm = CType (Me.BindingContext (ค่า), _ ผู้จัดการสกุลเงิน) การจัดกลุ่มเปลี่ยนแปลง() สิ้นสุดถ้า อื่น m_DataSource = ค่า cm = CType (Me.BindingContext (ค่า), _ ผู้จัดการสกุลเงิน) การจัดกลุ่มเปลี่ยนแปลง() สิ้นสุดถ้า สิ้นสุดถ้า สิ้นสุดถ้า จบเซต สิ้นสุดคุณสมบัติ |
โดยทั่วไปออบเจ็กต์ที่สามารถใช้เป็นแหล่งข้อมูลสำหรับการผูกข้อมูลที่ซับซ้อนได้รับการสนับสนุน อินเทอร์เฟซนี้จะแสดงข้อมูลเป็นชุดของออบเจ็กต์และมีคุณสมบัติที่มีประโยชน์หลายประการ เช่น Count การควบคุม TreeView ใหม่ของฉันต้องใช้อ็อบเจ็กต์ที่ได้รับการสนับสนุนจาก IList ในการโยง แต่การใช้อินเทอร์เฟซอื่นทำงานได้ดีเพราะมันให้วิธีที่สะดวกในการรับอ็อบเจ็กต์ IList ( GetList ) เมื่อตั้งค่าคุณสมบัติ DataSource ฉันจะพิจารณาก่อนว่ามีการระบุออบเจ็กต์ที่ถูกต้องหรือไม่ นั่นคือออบเจ็กต์ที่รองรับ IList หรือ IListSource สิ่งที่ฉันต้องการจริงๆ คือ IList ดังนั้นหากวัตถุรองรับเฉพาะ IListSource (เช่น DataTable ) ฉันจะใช้เมธอด GetList() ของอินเทอร์เฟซนั้นเพื่อรับวัตถุที่ถูกต้อง
ออบเจ็กต์บางตัวที่ใช้ IListSource (เช่น DataSet ) จริงๆ แล้วมีหลายรายการที่แสดงโดย คุณสมบัติ หากคุณสมบัตินี้เป็น True GetList จะส่งกลับวัตถุ IList ที่แสดงรายการ (ประกอบด้วยหลายรายการ) ในตัวอย่างของฉัน ฉันตัดสินใจที่จะสนับสนุนการเชื่อมต่อโดยตรงกับออบเจ็กต์ IList หรือออบเจ็กต์ IListSource ที่มีออบเจ็กต์ IList เพียงรายการเดียว และละเว้นออบเจ็กต์ที่ต้องมีการทำงานเพิ่มเติมเพื่อระบุแหล่งข้อมูล เช่น ชุดข้อมูล
หมายเหตุ: หากคุณต้องการสนับสนุนออบเจ็กต์ดังกล่าว ( DataSet หรือที่คล้ายกัน) คุณสามารถเพิ่มคุณสมบัติเพิ่มเติม (เช่น DataMember ) เพื่อระบุรายการย่อยเฉพาะสำหรับการโยง
หากแหล่งข้อมูลที่ให้มาถูกต้อง ผลลัพธ์สุดท้ายคืออินสแตนซ์ที่สร้างขึ้น ( cm = Me.BindingContext(Value) ) เนื่องจากอินสแตนซ์นี้จะถูกใช้เพื่อเข้าถึงแหล่งข้อมูลที่สำคัญ คุณสมบัติออบเจ็กต์ และข้อมูลตำแหน่ง จึงถูกจัดเก็บไว้ในตัวแปรภายในเครื่อง
เพิ่มคุณสมบัติการแสดงผลและค่าสมาชิก
การมี แหล่งข้อมูล เป็นขั้นตอนแรกในการใช้งานการเชื่อมโยงข้อมูลที่ซับซ้อน แต่ตัวควบคุมจำเป็นต้องทราบว่าเขตข้อมูลหรือคุณสมบัติเฉพาะใดของข้อมูลที่จะใช้เป็นสมาชิกการแสดงผลและค่า สมาชิก Display จะถูกใช้เป็นชื่อเรื่องของโหนดแผนผัง ในขณะที่สมาชิก Value สามารถเข้าถึงได้ผ่านคุณสมบัติ Value ของโหนด คุณสมบัติเหล่านี้เป็นสตริงทั้งหมดที่แสดงชื่อเขตข้อมูลหรือคุณสมบัติ และสามารถเพิ่มลงในตัวควบคุมได้อย่างง่ายดาย:
m_ValueMember ส่วนตัวเป็นสตริง m_DisplayMember ส่วนตัวเป็นสตริง - ทรัพย์สินสาธารณะ ValueMember() เป็นสตริง รับ ส่งกลับ m_ValueMember จบรับ ชุด (ค่า ByVal เป็นสตริง) m_ValueMember = ค่า จบเซต สิ้นสุดคุณสมบัติ - ทรัพย์สินสาธารณะ DisplayMember() เป็นสตริง รับ ส่งคืน m_DisplayMember จบรับ ชุด (ค่า ByVal เป็นสตริง) m_DisplayMember = ค่า จบเซต สิ้นสุดคุณสมบัติ |
ใน TreeView นี้ คุณสมบัติเหล่านี้จะเป็นตัวแทนเฉพาะสมาชิก Display และ Value ของ leaf nodes เท่านั้น และข้อมูลที่เกี่ยวข้องสำหรับแต่ละระดับการจัดกลุ่มจะถูกระบุในเมธอด AddGroup
การใช้วัตถุCurrencyManager
ในคุณสมบัติ DataSource ที่กล่าวถึงก่อนหน้านี้ อินสแตนซ์ของคลาส CurrencyManager จะถูกสร้างขึ้นและจัดเก็บไว้ในตัวแปรระดับคลาส คลาส CurrencyManager ที่เข้าถึงผ่านออบเจ็กต์นี้เป็นส่วนสำคัญของการนำข้อมูลไปใช้เนื่องจากมีคุณสมบัติ วิธีการ และเหตุการณ์ที่เปิดใช้งานฟังก์ชันต่อไปนี้:
ดึงค่าแอตทริบิวต์/ฟิลด์
ออบเจ็กต์ CurrencyManager ช่วยให้คุณสามารถดึงข้อมูลคุณสมบัติหรือค่าฟิลด์ เช่น ค่าของฟิลด์ DisplayMember หรือ ValueMember จากแต่ละรายการในแหล่งข้อมูลผ่านเมธอด GetItemProperties จากนั้นใช้วัตถุ PropertyDescriptor เพื่อรับค่าของเขตข้อมูลหรือคุณสมบัติเฉพาะในรายการเฉพาะ ส่วนย่อยโค้ดต่อไปนี้แสดงวิธีการสร้างออบเจ็กต์ PropertyDescriptor เหล่านี้ และวิธีการใช้ฟังก์ชัน GetValue เพื่อรับค่าคุณสมบัติของรายการในแหล่งข้อมูลพื้นฐาน หมายเหตุคุณสมบัติ รายการ ของออบเจ็กต์ CurrencyManager : ให้การเข้าถึงอินสแตนซ์ IList ที่เชื่อมโยงกับการควบคุม:
หรี่ myNewLeafNode เป็น TreeLeafNode หรี่ currObject เป็นวัตถุ currObject = cm.List (currentListIndex) ถ้า Me.DisplayMember <> AndAlso Me.ValueMember <> จากนั้น 'เพิ่มโหนดลีฟ? Dim pdValue เป็น System.ComponentModel.PropertyDescriptor Dim pdDisplay As System.ComponentModel.PropertyDescriptor pdValue = cm.GetItemProperties()(Me.ValueMember) pdDisplay = cm.GetItemProperties()(Me.DisplayMember) myNewLeafNode = _ ใหม่ TreeLeafNode(CStr(pdDisplay.GetValue(currObject)), _ วัตถุเคอร์เซอร์, _ pdValue.GetValue (currObject), _ currentListIndex) |
GetValue จะละเว้นชนิดข้อมูลพื้นฐานของคุณสมบัติเมื่อส่งคืนออบเจ็กต์ ดังนั้นจึงจำเป็นต้องแปลงค่าที่ส่งคืนก่อนใช้งาน
รักษาการควบคุมที่ผูกกับข้อมูลให้ตรงกัน
มี คุณลักษณะ ที่สำคัญอีกอย่างหนึ่ง: นอกเหนือจากการให้การเข้าถึงแหล่งข้อมูลที่ถูกผูกไว้และคุณสมบัติของรายการแล้ว ยังอนุญาตให้ใช้ แหล่งข้อมูล เดียวกันเพื่อประสานงานการผูกข้อมูลระหว่างตัวควบคุมนี้และตัวควบคุมอื่น ๆ การสนับสนุนนี้สามารถใช้เพื่อให้แน่ใจว่าการควบคุมหลายรายการที่ถูกผูกไว้กับแหล่งข้อมูลเดียวกันในเวลาเดียวกันยังคงอยู่ในรายการเดียวกันของแหล่งข้อมูล สำหรับการควบคุมของฉัน ฉันต้องการตรวจสอบให้แน่ใจว่าเมื่อมีการเลือกรายการในแผนภูมิ การควบคุมอื่นๆ ทั้งหมดที่เชื่อมโยงกับแหล่งข้อมูลเดียวกันจะชี้ไปที่รายการเดียวกัน (ระเบียน แถว หรืออาร์เรย์เดียวกัน หากคุณต้องการ ให้คิดในแง่ของฐานข้อมูล) . เมื่อต้องการทำเช่นนี้ ฉันจะแทนที่วิธี OnAfterSelect ใน TreeView ฐาน ในวิธีการนั้น (ซึ่งถูกเรียกหลังจากเลือกโหนดต้นไม้) ฉันตั้งค่าคุณสมบัติ ตำแหน่ง ของอ อบเจ็กต์ CurryManager เป็นดัชนีของรายการที่เลือกในปัจจุบัน แอปพลิเคชันตัวอย่างที่มาพร้อมกับการควบคุม TreeView แสดงให้เห็นว่าการควบคุมการซิงโครไนซ์ช่วยให้สร้างอินเทอร์เฟซผู้ใช้ที่เชื่อมโยงกับข้อมูลได้ง่ายขึ้นได้อย่างไร เพื่อให้ระบุตำแหน่งรายการของรายการที่เลือกในปัจจุบันได้ง่ายขึ้น ฉันใช้คลาส TreeNode แบบกำหนดเอง ( TreeLeafNode หรือ TreeGroupNode ) และจัดเก็บดัชนีรายการของแต่ละโหนดไว้ในคุณสมบัติ Position ที่ฉันสร้างขึ้น:
ป้องกันแทนที่การแทนที่ย่อย OnAfterSelect _ (ByVal และ System.Windows.Forms.TreeViewEventArgs) หรี่แสงเป็น TreeLeafNode ถ้า TypeOf e.Node เป็น TreeGroupNode แล้ว tln = FindFirstLeafNode (e.Node) Dim groupArgs As ใหม่ groupTreeViewEventArgs (e) RaiseEvent AfterGroupSelect (groupArgs) อย่างอื่นถ้า TypeOf e.Node เป็น TreeLeafNode แล้ว Dim leafArgs เหมือนใบไม้ใหม่ TreeViewEventArgs (e) RaiseEvent AfterLeafSelect (leafArgs) tln = CType (e.Node, TreeLeafNode) สิ้นสุดถ้า ถ้าไม่ใช่ก็ไม่มีอะไรแล้ว ถ้า cm.Position <> tln.Position แล้ว cm.Position = tln.Position สิ้นสุดถ้า สิ้นสุดถ้า MyBase.OnAfterSelect(e) จบหมวดย่อย |
ในโค้ดก่อนหน้านี้ คุณอาจสังเกตเห็นฟังก์ชันที่เรียกว่า FindFirstLeafNode ซึ่งฉันอยากจะแนะนำสั้นๆ ที่นี่ ใน TreeView ของฉัน เฉพาะโหนดปลายสุด (โหนดสุดท้ายในลำดับชั้น) ที่สอดคล้องกับรายการใน DataSource โหนดอื่นๆ ทั้งหมดจะใช้เพื่อสร้างโครงสร้างการจัดกลุ่มเท่านั้น ถ้าฉันต้องการสร้างตัวควบคุมการเชื่อมโยงข้อมูลที่มีประสิทธิภาพดี ฉันจะต้องเลือกรายการที่สอดคล้องกับ DataSource เสมอ ดังนั้นเมื่อใดก็ตามที่ฉันเลือกโหนดกลุ่ม ฉันจะพบโหนดปลายสุดแรกภายใต้กลุ่ม ราวกับว่า โหนดนี้เป็น การเลือกปัจจุบัน คุณสามารถดูตัวอย่างการใช้งานจริงได้ แต่ตอนนี้คุณสามารถใช้มันได้ตามใจชอบ
ฟังก์ชั่นส่วนตัว FindFirstLeafNode (ByVal currNode As TreeNode) _ เป็น TreeLeafNode ถ้า TypeOf currNode เป็น TreeLeafNode แล้ว กลับ CType (currNode, TreeLeafNode) อื่น ถ้า currNode.Nodes.Count > 0 แล้ว กลับ FindFirstLeafNode (currNode.Nodes (0)) อื่น กลับไม่มีอะไร สิ้นสุดถ้า สิ้นสุดถ้า ฟังก์ชันสิ้นสุด |
การตั้งค่าคุณสมบัติ Position ของออบเจ็ก ต์ CurryManager จะซิงโครไนซ์การควบคุมอื่นๆ กับการเลือกปัจจุบัน แต่เมื่อตำแหน่งของการควบคุมอื่นๆ เปลี่ยนแปลง CurrencyManager ยังสร้างเหตุการณ์เพื่อให้การเลือกเปลี่ยนแปลงตามไปด้วย เพื่อให้เป็นส่วนประกอบที่เชื่อมโยงกับข้อมูลที่ดี สิ่งที่เลือกควรย้ายเมื่อตำแหน่งของแหล่งข้อมูลเปลี่ยนแปลง และจอแสดงผลควรอัปเดตเมื่อข้อมูลสำหรับรายการได้รับการแก้ไข มีสามเหตุการณ์ที่เกิดขึ้นโดย CurrencyManager : CurrentChanged , ItemChanged และ PositionChanged เหตุการณ์สุดท้ายนั้นค่อนข้างง่าย หนึ่งในวัตถุประสงค์ของ CurrencyManager คือการรักษาตัวบ่งชี้ตำแหน่งปัจจุบันสำหรับแหล่งข้อมูล เพื่อให้การควบคุมที่ถูกผูกไว้หลายรายการสามารถแสดงเรกคอร์ดหรือรายการเดียวกันได้ และเหตุการณ์นี้จะเกิดขึ้นทุกครั้งที่ตำแหน่งเปลี่ยนแปลง อีกสองเหตุการณ์บางครั้งทับซ้อนกันและความแตกต่างก็ไม่ค่อยชัดเจน ข้อมูลต่อไปนี้จะอธิบายวิธีใช้เหตุการณ์เหล่านี้ในการควบคุมแบบกำหนดเอง: PositionChanged เป็นเหตุการณ์ที่ค่อนข้างง่ายและจะไม่มีการอธิบายไว้ที่นี่ เมื่อคุณต้องการปรับรายการที่เลือกในปัจจุบันในการควบคุมที่ผูกกับข้อมูลที่ซับซ้อน (เช่น Tree) โปรดใช้ The เหตุการณ์. เหตุการณ์ ItemChanged จะเกิดขึ้นเมื่อมีการแก้ไขรายการในแหล่งข้อมูล ในขณะที่ CurrentChanged จะเกิดขึ้นเมื่อมีการแก้ไขรายการปัจจุบันเท่านั้น
ใน TreeView ของฉัน ฉันพบว่าเมื่อใดก็ตามที่ฉันเลือกรายการใหม่ เหตุการณ์ทั้งสามเหตุการณ์จะถูกยกขึ้น ดังนั้นฉันจึงตัดสินใจจัดการกับเหตุการณ์ PositionChanged โดยการเปลี่ยนรายการที่เลือกในปัจจุบัน และไม่ทำอะไรกับอีกสองเหตุการณ์ ขอแนะนำให้ส่งแหล่งข้อมูลไปยัง IBindingList (หากแหล่งข้อมูลรองรับ IBindingList ) และใช้เหตุการณ์ ListChanged แทน แต่ฉันไม่ได้ใช้ฟังก์ชันนี้
ส่วนตัวย่อย cm_PositionChanged (ผู้ส่ง ByVal As Object, _ ByVal e As System.EventArgs) จัดการ cm.PositionChanged หรี่แสงเป็น TreeLeafNode ถ้า TypeOf Me.SelectedNode เป็น TreeLeafNode แล้ว tln = CType (Me.SelectedNode, TreeLeafNode) อื่น tln = FindFirstLeafNode (Me.SelectedNode) สิ้นสุดถ้า ถ้า tln.Position <> cm.Position แล้ว Me.SelectedNode = FindNodeByPosition(cm.Position) สิ้นสุดถ้า จบหมวดย่อย ฟังก์ชันโอเวอร์โหลดส่วนตัว FindNodeByPosition (ดัชนี ByVal As Integer) _ เป็น TreeNode กลับ FindNodeByPosition (ดัชนี Me.Nodes) ฟังก์ชันสิ้นสุด ฟังก์ชันโอเวอร์โหลดส่วนตัว FindNodeByPosition (ดัชนี ByVal เป็นจำนวนเต็ม, _ ByVal NodesToSearch As TreeNodeCollection) เป็น TreeNode หรี่ i As Integer = 0 หรี่ currNode เป็น TreeNode หรี่แสงเป็น TreeLeafNode ทำในขณะที่ฉัน < NodesToSearch.Count currNode = NodesToSearch(i) ฉัน += 1 ถ้า TypeOf currNode เป็น TreeLeafNode แล้ว tln = CType (currNode, TreeLeafNode) ถ้า tln.Position = ดัชนี จากนั้น กลับ currNode สิ้นสุดถ้า อื่น currNode = FindNodeByPosition (ดัชนี, currNode.Nodes) ถ้าไม่ใช่ currNode ก็ไม่มีอะไรแล้ว กลับ currNode สิ้นสุดถ้า สิ้นสุดถ้า วนซ้ำ กลับไม่มีอะไร ฟังก์ชันสิ้นสุด |
แปลงแหล่งข้อมูลเป็นแผนผัง
หลังจากเขียนโค้ดการเชื่อมโยงข้อมูลแล้ว ฉันสามารถดำเนินการต่อและเพิ่มโค้ดเพื่อจัดการระดับการจัดกลุ่ม สร้างแผนผังตามลำดับ จากนั้นเพิ่มเหตุการณ์ วิธีการ และคุณสมบัติที่กำหนดเอง
กลุ่มผู้บริหาร
ในการกำหนดค่าคอลเลกชันของกลุ่ม โปรแกรมเมอร์จะต้องสร้างฟังก์ชัน AddGroup , RemoveGroup และ ClearGroups เมื่อใดก็ตามที่มีการแก้ไขคอลเลกชันกลุ่ม ต้นไม้จะต้องถูกวาดใหม่ (เพื่อสะท้อนถึงการกำหนดค่าใหม่) ดังนั้นฉันจึงสร้างขั้นตอนทั่วไป GroupingChanged ซึ่งสามารถเรียกได้ด้วยโค้ดต่างๆ ในตัวควบคุมเมื่อสถานการณ์เปลี่ยนแปลงและต้นไม้จำเป็นต้องถูกบังคับให้บังคับ ถูกสร้างขึ้นใหม่:
treeGroups ส่วนตัวเป็น ArrayList ใหม่ () Public Sub RemoveGroup (กลุ่ม ByVal As Group) ถ้าไม่ใช่ treeGroups.Contains(group) จากนั้น treeGroups.Remove (กลุ่ม) การจัดกลุ่มเปลี่ยนแปลง() สิ้นสุดถ้า จบหมวดย่อย AddGroup ย่อยสาธารณะโอเวอร์โหลด (กลุ่ม ByVal As Group) พยายาม treeGroups เพิ่ม (กลุ่ม) การจัดกลุ่มเปลี่ยนแปลง() จับ สิ้นสุดการลอง จบหมวดย่อย AddGroup ย่อยโอเวอร์โหลดสาธารณะ (ชื่อ ByVal As String, _ ByVal groupBy As สตริง, _ ByVal displayMember เป็นสตริง _ ByVal valueMember เป็นสตริง _ ByVal imageIndex เป็นจำนวนเต็ม, _ ByVal เลือก ImageIndex เป็นจำนวนเต็ม) หรี่ myNewGroup เป็นกลุ่มใหม่ (ชื่อ, groupBy, _ สมาชิกจอแสดงผล, สมาชิกค่า, _ imageIndex เลือก ImageIndex) Me.AddGroup(myNewGroup) จบหมวดย่อย ฟังก์ชั่นสาธารณะ GetGroups() เป็นกลุ่ม () กลับ CType (treeGroups.ToArray (GetType (กลุ่ม)), กลุ่ม ()) ฟังก์ชันสิ้นสุด |
ทอดต้นไม้
การสร้างแผนผังใหม่จริงทำได้โดยกระบวนการคู่หนึ่ง: BuildTree และ AddNodes เนื่องจากโค้ดสำหรับทั้งสองกระบวนการยาวเกินไป บทความนี้จึงไม่ได้แสดงรายการทั้งหมด แต่พยายามสรุปการทำงาน (แน่นอน คุณสามารถดาวน์โหลดโค้ดแบบเต็มได้หากต้องการ) ตามที่กล่าวไว้ข้างต้น โปรแกรมเมอร์สามารถโต้ตอบกับการควบคุมนี้โดยการตั้งค่ากลุ่มต่างๆ จากนั้นใช้กลุ่มเหล่านี้ใน BuildTree เพื่อกำหนดวิธีการตั้งค่าโหนดต้นไม้ BuildTree ล้างการรวบรวมโหนดปัจจุบัน จากนั้นวนซ้ำแหล่งข้อมูลทั้งหมดเพื่อประมวลผลการจัดกลุ่มระดับแรก (ผู้เผยแพร่ที่กล่าวถึงในตัวอย่างและภาพประกอบก่อนหน้าในบทความนี้) เพิ่มโหนดสำหรับค่าการจัดกลุ่มที่แตกต่างกันแต่ละค่า (โดยใช้ข้อมูลในตัวอย่าง สำหรับแต่ละเพิ่มโหนดที่มีค่า pub_id ) จากนั้นเรียก AddNodes เพื่อเติมโหนดทั้งหมดภายใต้การจัดกลุ่มระดับแรก AddNodes เรียกตัวเองซ้ำๆ เพื่อจัดการระดับต่างๆ เท่าที่จำเป็น โดยเพิ่มโหนดกลุ่มและโหนดปลายสุดตามความจำเป็น ใช้คลาสแบบกำหนดเองสองคลาสที่ใช้ TreeNode เพื่อแยกความแตกต่างของโหนดกลุ่มและโหนดปลายสุด และจัดเตรียมคุณลักษณะที่สอดคล้องกันสำหรับโหนดทั้งสองประเภท
เหตุการณ์ TreeView แบบกำหนดเอง
เมื่อใดก็ตามที่มีการเลือกโหนด TreeView จะสร้างเหตุการณ์สองเหตุการณ์: BeforeSelect และ AfterSelect แต่ในการควบคุมของฉัน ฉันต้องการทำให้เหตุการณ์ของโหนดกลุ่มและโหนดปลายสุดแตกต่างกัน ดังนั้นฉันจึงเพิ่มเหตุการณ์ของตัวเอง BeforeGroupSelect/AfterGroupSelect และ BeforeLeafSelect/AfterLeafSelect นอกเหนือจากเหตุการณ์พื้นฐานแล้ว ฉันยังทริกเกอร์คลาสพารามิเตอร์เหตุการณ์ที่กำหนดเองด้วย:
กิจกรรมสาธารณะ BeforeGroupSelect _ (ผู้ส่ง ByVal As Object, ByVal e As groupTreeViewCancelEventArgs) กิจกรรมสาธารณะ AfterGroupSelect _ (ผู้ส่ง ByVal As Object, ByVal e As groupTreeViewEventArgs) กิจกรรมสาธารณะBeforeLeafSelect _ (ผู้ส่ง ByVal As Object, ByVal e As leafTreeViewCancelEventArgs) กิจกรรมสาธารณะ AfterLeafSelect _ (ผู้ส่ง ByVal As Object, ByVal e As leafTreeViewEventArgs) ป้องกันแทนที่การแทนที่ย่อย OnBeforeSelect _ (ByVal และ System.Windows.Forms.TreeViewCancelEventArgs) ถ้า TypeOf e.Node เป็น TreeGroupNode แล้ว Dim groupArgs As ใหม่ groupTreeViewCancelEventArgs (e) RaiseEvent BeforeGroupSelect (CObj (Me), groupArgs) อย่างอื่นถ้า TypeOf e.Node เป็น TreeLeafNode แล้ว Dim leafArgs เหมือนใบไม้ใหม่ TreeViewCancelEventArgs (e) RaiseEvent BeforeLeafSelect (CObj (ฉัน), leafArgs) สิ้นสุดถ้า MyBase.OnBeforeSelect(e) จบหมวดย่อย ป้องกันแทนที่การแทนที่ย่อย OnAfterSelect _ (ByVal และ System.Windows.Forms.TreeViewEventArgs) หรี่แสงเป็น TreeLeafNode ถ้า TypeOf e.Node เป็น TreeGroupNode แล้ว tln = FindFirstLeafNode (e.Node) Dim groupArgs As ใหม่ groupTreeViewEventArgs (e) RaiseEvent AfterGroupSelect (CObj (ฉัน), groupArgs) อย่างอื่นถ้า TypeOf e.Node เป็น TreeLeafNode แล้ว Dim leafArgs เหมือนใบไม้ใหม่ TreeViewEventArgs (e) RaiseEvent AfterLeafSelect (CObj (ฉัน), leafArgs) tln = CType (e.Node, TreeLeafNode) สิ้นสุดถ้า ถ้าไม่ใช่ก็ไม่มีอะไรแล้ว ถ้า cm.Position <> tln.Position แล้ว cm.Position = tln.Position สิ้นสุดถ้า สิ้นสุดถ้า MyBase.OnAfterSelect(e) จบหมวดย่อย |
คลาสโหนดที่กำหนดเอง ( TreeLeafNode และ TreeGroupNode ) และคลาสพารามิเตอร์เหตุการณ์ที่กำหนดเองจะรวมอยู่ในโค้ดที่ดาวน์โหลดได้
แอปพลิเคชันตัวอย่าง
เพื่อให้เข้าใจโค้ดทั้งหมดในการควบคุมตัวอย่างนี้อย่างถ่องแท้ คุณควรเข้าใจวิธีการทำงานในแอปพลิเคชันของคุณ แอปพลิเคชันตัวอย่างที่รวมไว้ใช้ฐานข้อมูล pubs.mdb Access และแสดงให้เห็นว่าตัวควบคุม แบบทรี สามารถใช้กับตัวควบคุมที่ผูกกับข้อมูลอื่น ๆ เพื่อสร้างแอปพลิเคชัน Windows ได้อย่างไร คุณลักษณะสำคัญของหมายเหตุเฉพาะในกรณีนี้ ได้แก่ การซิงโครไนซ์แผนภูมิกับการควบคุมที่ถูกผูกไว้อื่นๆ และการเลือกโหนดแผนผังโดยอัตโนมัติเมื่อทำการค้นหาแหล่งข้อมูล
หมายเหตุ: แอปพลิเคชันตัวอย่างนี้ (ชื่อ TheSample) รวมอยู่ในการดาวน์โหลดสำหรับบทความนี้
รูปที่ 4: แอปพลิเคชันสาธิตสำหรับ TreeView ที่ผูกกับข้อมูล
สรุป
ตัวควบคุม Tree ที่ผูกกับข้อมูลซึ่งอธิบายไว้ในบทความนี้ไม่เหมาะสำหรับทุกโครงการที่ต้องใช้ตัวควบคุม Tree เพื่อแสดงข้อมูลฐานข้อมูล แต่จะแนะนำวิธีการในการปรับแต่งตัวควบคุมเพื่อวัตถุประสงค์ส่วนบุคคล โปรดจำไว้ว่าการควบคุมที่ผูกกับข้อมูลที่ซับซ้อนใดๆ ที่คุณต้องการสร้างจะมีโค้ดเดียวกันกับตัวควบคุม Tree เป็นจำนวนมาก และคุณสามารถทำให้การพัฒนาการควบคุมในอนาคตง่ายขึ้นโดยการแก้ไขโค้ดที่มีอยู่