ความแตกต่างระหว่างคุณสมบัติการพึ่งพาและคุณสมบัติที่แนบมาใน WPF คืออะไร?


91

อะไรคือความแตกต่างระหว่างคุณสมบัติการพึ่งพา (กำหนดเอง) และคุณสมบัติที่แนบมาใน WPF? แต่ละอย่างใช้อะไรบ้าง? การใช้งานโดยทั่วไปแตกต่างกันอย่างไร?

คำตอบ:


22

บทคัดย่อ

เนื่องจากฉันพบเอกสารเกี่ยวกับเรื่องนี้เพียงเล็กน้อยหรือไม่มีเลยจึงต้องใช้ซอร์สโค้ดแต่นี่คือคำตอบ

มีความแตกต่างระหว่างการลงทะเบียนคุณสมบัติอ้างอิงเป็นคุณสมบัติปกติและเป็นคุณสมบัติที่แนบมานอกเหนือจากคุณสมบัติ "เชิงปรัชญา" ( คุณสมบัติปกติมีวัตถุประสงค์เพื่อใช้โดยประเภทการประกาศและประเภทที่ได้รับคุณสมบัติที่แนบมามีวัตถุประสงค์เพื่อใช้เป็น ส่วนขยายบน DependencyObject อินสแตนซ์โดยพลการ ) "Philosophical" เนื่องจากตามที่ @MarqueIV สังเกตเห็นในความคิดเห็นของเขาต่อคำตอบของ @ ReedCopsey คุณสมบัติทั่วไปยังสามารถใช้กับDependencyObjectอินสแตนซ์ตามอำเภอใจได้

ยิ่งไปกว่านั้นฉันต้องไม่เห็นด้วยกับคำตอบอื่น ๆ ที่ระบุว่าคุณสมบัติที่แนบมานั้นเป็น "ประเภทของคุณสมบัติการพึ่งพา" เพราะมันทำให้เข้าใจผิด - ไม่มีคุณสมบัติการพึ่งพา "ประเภท" ใด ๆ เฟรมเวิร์กไม่สนใจว่าคุณสมบัตินั้นได้รับการลงทะเบียนตามที่แนบมาหรือไม่ - ยังไม่สามารถระบุได้ด้วยซ้ำ (ในแง่ที่ว่าข้อมูลนี้ไม่ได้รับการบันทึกเนื่องจากไม่เกี่ยวข้อง) ในความเป็นจริงคุณสมบัติทั้งหมดได้รับการลงทะเบียนราวกับว่าเป็นคุณสมบัติที่แนบมา แต่ในกรณีปกติมีการดำเนินการเพิ่มเติมบางอย่างที่ปรับเปลี่ยนพฤติกรรมเล็กน้อย

รหัสที่ตัดตอนมา

เพื่อให้คุณไม่ต้องกังวลกับการใช้ซอร์สโค้ดด้วยตัวเองนี่คือสิ่งที่เกิดขึ้น

เมื่อลงทะเบียนคุณสมบัติโดยไม่ระบุข้อมูลเมตาให้เรียก

DependencyProperty.Register(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass))

ให้ผลลัพธ์เหมือนกับการโทร

DependencyProperty.RegisterAttached(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass))

อย่างไรก็ตามเมื่อระบุข้อมูลเมตาการโทร

DependencyProperty.Register(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass),
    typeMetadata: new FrameworkPropertyMetadata
    {
        CoerceValueCallback = CoerceCallback,
        DefaultValue = "default value",
        PropertyChangedCallback = ChangedCallback
    });

เทียบเท่ากับการโทร

var property = DependencyProperty.RegisterAttached(
    name: "MyProperty",
    propertyType: typeof(object),
    ownerType: typeof(MyClass),
    defaultMetadata: new PropertyMetadata
    {
        DefaultValue = "default value",
    });
property.OverrideMetadata(
    forType: typeof(MyClass),
    typeMetadata: new FrameworkPropertyMetadata
    {
        CoerceValueCallback = CoerceCallback,
        DefaultValue = "default value",
        PropertyChangedCallback = ChangedCallback
    });

ข้อสรุป

ความแตกต่างของคีย์ (และเท่านั้น) ระหว่างคุณสมบัติการพึ่งพาปกติและที่แนบมาคือข้อมูลเมตาเริ่มต้นที่พร้อมใช้งานผ่านคุณสมบัติDependencyProperty.DefaultMetadata สิ่งนี้ยังกล่าวถึงในส่วนหมายเหตุ :

สำหรับคุณสมบัติที่ไม่มีการระบุประเภทข้อมูลเมตาที่ส่งคืนโดยคุณสมบัตินี้ไม่สามารถแคสต์ไปยังชนิดPropertyMetadata ที่สืบทอดมาได้แม้ว่าคุณสมบัติจะถูกลงทะเบียนด้วยชนิดข้อมูลเมตาที่ได้รับ หากคุณต้องการให้ข้อมูลเมตาที่ลงทะเบียนเดิมรวมถึงประเภทข้อมูลเมตาที่ได้รับดั้งเดิมให้เรียกGetMetadata (Type)แทนโดยส่งประเภทการลงทะเบียนเดิมเป็นพารามิเตอร์

สำหรับคุณสมบัติที่แนบมาประเภทของข้อมูลเมตาที่ส่งคืนโดยคุณสมบัตินี้จะตรงกับประเภทที่ระบุในวิธีการลงทะเบียนRegisterAttachedดั้งเดิม

เห็นได้ชัดเจนในรหัสที่ให้มา คำแนะนำเล็ก ๆ น้อย ๆ จะถูกซ่อนไว้ยังอยู่ในวิธีการลงทะเบียนคือสำหรับRegisterAttachedพารามิเตอร์เมตาดาต้าที่มีชื่อdefaultMetadataในขณะที่สำหรับมันเป็นชื่อRegister typeMetadataสำหรับคุณสมบัติที่แนบมาข้อมูลเมตาที่ระบุจะกลายเป็นข้อมูลเมตาเริ่มต้น อย่างไรก็ตามในกรณีของคุณสมบัติทั่วไปข้อมูลเมตาเริ่มต้นจะเป็นอินสแตนซ์ใหม่ที่PropertyMetadataมีการDefaultValueตั้งค่าเท่านั้น(ไม่ว่าจะจากข้อมูลเมตาที่ให้มาหรือโดยอัตโนมัติ) เฉพาะการเรียกที่ตามมาเพื่อOverrideMetadataใช้ข้อมูลเมตาที่ให้มาจริงๆ

ผลที่ตามมา

ข้อแตกต่างในทางปฏิบัติที่สำคัญคือในกรณีของคุณสมบัติทั่วไปCoerceValueCallbackและPropertyChangedCallbackใช้ได้เฉพาะกับประเภทที่ได้รับจากประเภทที่ประกาศเป็นประเภทเจ้าของและสำหรับคุณสมบัติที่แนบมาคุณสมบัติเหล่านี้ใช้ได้กับทุกประเภท เช่นในสถานการณ์นี้:

var d = new DependencyObject();
d.SetValue(SomeClass.SomeProperty, "some value");

ผู้จดทะเบียนPropertyChangedCallback จะถูกเรียกหากทรัพย์สินนั้นได้รับการจดทะเบียนเป็นทรัพย์สินที่แนบมา แต่จะไม่ถูกเรียกหากได้รับการจดทะเบียนเป็นทรัพย์สินปกติ CoerceValueCallbackเดียวกันไป

ความแตกต่างที่รองเกิดจากความจริงที่ว่าต้องการให้บุคลากรประเภทผลิตภัณฑ์จากOverrideMetadata DependencyObjectในทางปฏิบัติหมายความว่าประเภทเจ้าของสำหรับคุณสมบัติทั่วไปต้องได้มาจากDependencyObjectในขณะที่คุณสมบัติที่แนบมาในสามารถเป็นประเภทใดก็ได้ (รวมถึงคลาสแบบคงที่โครงสร้าง enums ตัวแทน ฯลฯ )

เสริม

นอกจากคำแนะนำของ @ MarqueIV แล้วในหลาย ๆ ครั้งฉันพบความคิดเห็นว่าคุณสมบัติปกติและคุณสมบัติที่แนบมานั้นแตกต่างกันไปตามวิธีที่สามารถใช้ในXAMLได้ กล่าวคือคุณสมบัติทั่วไปนั้นต้องการไวยากรณ์ของชื่อโดยนัยซึ่งตรงข้ามกับไวยากรณ์ของชื่อที่ชัดเจนซึ่งต้องการโดยคุณสมบัติที่แนบมา นี่ไม่เป็นความจริงในทางเทคนิคแม้ว่าในทางปฏิบัติมักจะเป็นเช่นนั้นก็ตาม เพื่อความชัดเจน:

<!-- Implicit property name -->
<ns:SomeClass SomeProperty="some value" /> 

<!-- Explicit property name -->
<DependencyObject ns:SomeClass.SomeProperty="some value" />

ในXAML แท้กฎเดียวที่ควบคุมการใช้ไวยากรณ์เหล่านี้มีดังต่อไปนี้:

  • ไวยากรณ์ของชื่อโดยนัยสามารถใช้กับองค์ประกอบได้ก็ต่อเมื่อคลาสที่องค์ประกอบนี้แสดงถึงมีคุณสมบัติCLRของชื่อนั้น
  • ไวยากรณ์ของชื่อที่ชัดเจนสามารถใช้กับองค์ประกอบได้ก็ต่อเมื่อคลาสที่ระบุโดยส่วนแรกของชื่อเต็มเปิดเผยเมธอดget / setแบบคงที่ที่เหมาะสม(เรียกว่าaccessors ) โดยมีชื่อตรงกับส่วนที่สองของชื่อเต็ม

การปฏิบัติตามเงื่อนไขเหล่านี้ช่วยให้คุณสามารถใช้ไวยากรณ์ที่เกี่ยวข้องได้ไม่ว่าคุณสมบัติการสำรองข้อมูลจะถูกลงทะเบียนเป็นปกติหรือแนบ

ตอนนี้ความเข้าใจผิดดังกล่าวเกิดจากความจริงที่ว่าบทช่วยสอนส่วนใหญ่ (พร้อมกับตัวอย่างโค้ดVisual Studio ในคลัง) สั่งให้คุณใช้คุณสมบัติCLRสำหรับคุณสมบัติการพึ่งพาปกติและรับ / ตั้งค่าตัวเข้าถึงสำหรับสิ่งที่แนบมา แต่ไม่มีอะไรหยุดคุณจากการใช้ทั้งสองอย่างในเวลาเดียวกันทำให้คุณสามารถใช้ไวยากรณ์แบบใดก็ได้ที่คุณต้องการ


71

คุณสมบัติที่แนบมาเป็นคุณสมบัติการพึ่งพาชนิดหนึ่ง ความแตกต่างอยู่ที่วิธีใช้

ด้วยคุณสมบัติที่แนบมาคุณสมบัติจะถูกกำหนดบนคลาสที่ไม่ใช่คลาสเดียวกันกับที่ใช้ โดยปกติจะใช้สำหรับการจัดวาง ตัวอย่างที่ดี ได้แก่ Panel.ZIndex หรือ Grid.Row - คุณใช้สิ่งนี้กับตัวควบคุม (เช่นปุ่ม) แต่จริงๆแล้วถูกกำหนดไว้ในแผงหรือตาราง คุณสมบัติ "แนบ" กับอินสแตนซ์ของปุ่ม

สิ่งนี้ช่วยให้คอนเทนเนอร์สามารถสร้างคุณสมบัติที่สามารถใช้กับ UIelement ใด ๆ

สำหรับความแตกต่างในการนำไปใช้ - โดยพื้นฐานแล้วเป็นเพียงเรื่องของการใช้ Register กับ RegisterAttached เมื่อคุณกำหนดคุณสมบัติ


10
แต่อะไรคือความแตกต่าง?! จากสิ่งที่ฉันเห็นคุณสามารถแนบคุณสมบัติที่ไม่สามารถเข้าถึงได้กับอีกคุณสมบัติหนึ่งผ่านทางรหัส (ฉันคิดว่าสิ่งนี้ถูกบล็อกใน XAML) บางทีนั่นอาจเป็นความแตกต่าง?
Mark A. Donohoe

5

คุณสมบัติที่แนบมาโดยทั่วไปมีไว้สำหรับองค์ประกอบคอนเทนเนอร์เช่นถ้าคุณมีกริดและคุณมี grid.row ตอนนี้ถือว่าเป็นคุณสมบัติที่แนบมาขององค์ประกอบกริดนอกจากนี้คุณสามารถใช้คุณสมบัตินี้ใน texbox ปุ่ม ฯลฯ เพื่อตั้งค่า วางในตาราง

คุณสมบัติการพึ่งพาเป็นเหมือนคุณสมบัติโดยทั่วไปเป็นของคลาสอื่นและใช้ในคลาสอื่น เช่นคุณมีรูปสี่เหลี่ยมผืนผ้าที่นี่ความสูงและความกว้างเป็นคุณสมบัติปกติของสี่เหลี่ยมผืนผ้า แต่ด้านซ้ายและด้านบนเป็นคุณสมบัติอ้างอิงเนื่องจากเป็นของคลาส Canvass


-1

คุณสมบัติที่แนบมาเป็น DependencyProperties ชนิดพิเศษ ช่วยให้คุณสามารถแนบค่ากับวัตถุที่ไม่รู้อะไรเกี่ยวกับค่านี้ ตัวอย่างที่ดีสำหรับแนวคิดนี้คือแผงเค้าโครง แผงเค้าโครงแต่ละแผงต้องการข้อมูลที่แตกต่างกันเพื่อจัดแนวองค์ประกอบลูก Canvas ต้องการด้านบนและด้านซ้าย DockPanel ต้องใช้ Dock เป็นต้นเนื่องจากคุณสามารถเขียนแผงเลย์เอาต์ของคุณเองรายการจึงไม่มีที่สิ้นสุด คุณจะเห็นว่าเป็นไปไม่ได้ที่จะมีคุณสมบัติทั้งหมดบนคอนโทรล WPF ทั้งหมด สารละลายมีคุณสมบัติที่แนบมา ซึ่งกำหนดโดยตัวควบคุมที่ต้องการข้อมูลจากตัวควบคุมอื่นในบริบทเฉพาะ ตัวอย่างเช่นองค์ประกอบที่จัดแนวโดยแผงโครงร่างหลัก


-1

ฉันคิดว่าคุณสามารถกำหนดคุณสมบัติที่แนบมาในคลาสได้เองหรือจะกำหนดในคลาสอื่นก็ได้ เราสามารถใช้คุณสมบัติที่แนบมาเพื่อขยายการควบคุมมาตรฐานของ Microsoft ได้เสมอ แต่คุณสมบัติการพึ่งพาคุณกำหนดไว้ในตัวควบคุมของคุณเอง เช่นคุณสามารถสืบทอดการควบคุมของคุณจากการควบคุมมาตรฐานและกำหนดคุณสมบัติการพึ่งพาในการควบคุมของคุณเองและใช้มัน สิ่งนี้เทียบเท่ากับการกำหนดคุณสมบัติที่แนบมาและใช้คุณสมบัติที่แนบนี้ในการควบคุมมาตรฐาน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.