INotifyPropertyChanged กับ DependencyProperty ใน ViewModel


353

เมื่อติดตั้ง ViewModel ในแอพพลิเคชั่น WP-View-ViewModel สถาปัตยกรรม WPF ดูเหมือนจะมีสองตัวเลือกหลัก ๆ ในการทำให้ databindable ฉันได้เห็นการใช้งานที่ใช้DependencyPropertyสำหรับคุณสมบัติที่วิวจะเชื่อมโยงกับและฉันได้เห็นการใช้งาน ViewModel INotifyPropertyChangedแทน

คำถามของฉันคือเมื่อใดที่ฉันควรเลือกหนึ่งคำถาม มีความแตกต่างด้านประสิทธิภาพหรือไม่? เป็นความคิดที่ดีหรือไม่ที่จะมอบการพึ่งพา ViewModel ให้กับ WPF? ฉันต้องพิจารณาอะไรอีกเมื่อตัดสินใจออกแบบ


11
ดูstackoverflow.com/questions/1329138/…สำหรับวิธีการตรวจสอบคอมไพเลอร์ในการใช้ INotifyPropertyChanged หลีกเลี่ยงการมีชื่อคุณสมบัติเป็นสตริงมายากล
— Ian Ringrose

10
โดยทั่วไปมีความแตกต่างที่สำคัญระหว่างคุณสมบัติการพึ่งพาและคุณสมบัติปกติในชั้นเรียนซึ่งใช้ INotifyPropertyChanged คุณสมบัติการพึ่งพาอาจเป็นแหล่งที่มาหรือเป้าหมายในการเชื่อมโยงข้อมูล แต่คุณสมบัติปกติที่มีการสนับสนุน INotifyPropertyChanged สามารถใช้เป็นแหล่งข้อมูลได้เท่านั้น ดังนั้นโซลูชั่นเหล่านี้จึงไม่สามารถใช้แทนกันได้อย่างสมบูรณ์ โครงสร้างพื้นฐานการเชื่อมโยงข้อมูลต้องการ DP เป็นเป้าหมายในการทำงาน แต่แหล่งที่มาอาจเป็นคุณสมบัติปกติที่มีการสนับสนุน INotifyPropertyChanged หรือ DP ทั่วไป
— Mostafa Rezaei

4
ดูstackoverflow.com/a/10595688/200442สำหรับ .net INotifyPropertyChangedวิธีในการดำเนินการ
— แดเนียล Little

อธิบายได้ดีที่สุดที่นี่ stackoverflow.com/a/3552550/366064
— Bizhan

คำตอบ:


214

เคนท์เขียนบล็อกที่น่าสนใจเกี่ยวกับหัวข้อนี้: ดูโมเดล: POCOs เมื่อเทียบกับ DependencyObjects

สรุปสั้น ๆ:

  1. DependencyObjects ไม่ได้ถูกทำเครื่องหมายเป็นซีเรียลไลซ์
  2. คลาส DependencyObject จะแทนที่และผนึกเมธอด Equals () และ GetHashCode ()
  3. DependencyObject มีความสัมพันธ์ของเธรด - สามารถเข้าถึงได้บนเธรดที่สร้างขึ้นเท่านั้น

ฉันชอบวิธีการ POCO คลาสพื้นฐานสำหรับ PresentationModel (aka ViewModel) ซึ่งใช้อินเทอร์เฟซ INotifyPropertyChanged สามารถพบได้ที่นี่: http://compositeextensions.codeplex.com


24
DependencyObject ยังใช้การพึ่งพาไลบรารี WPF ในขณะที่ POCO ไม่อนุญาตให้มุมมองแบบจำลองของคุณขับ UI สแต็กอื่นที่ไม่มี WPF (Compact Framework, Mono)
— codekaizen

26
เห็นได้ชัดว่า Dependecy Properties นั้นสร้างขึ้นเพื่อ UI เท่านั้นและไม่ใช่สำหรับเลเยอร์ธุรกิจ
— Andrei Rînea

11
คุณสมบัติการพึ่งพายังต้องการพาเรนต์ DependancyObject ViewModel ของคุณไม่ควรรับช่วงต่อจาก DependencyObject
— Gusdor

38

ตามคู่มือประสิทธิภาพ WPF DependencyObjects ทำงานได้ดีกว่า POCO ที่ใช้ INotifyPropertyChanged แน่นอน:

http://msdn.microsoft.com/en-us/library/bb613546.aspx


1
ฉันต้องเห็นด้วยกับสิ่งนั้น ;-): blog.lexique-du-net.com/index.php?post/2010/02/24/…
— Jonatha ANTOINE

หากคุณเลือก. NET Framework เวอร์ชัน 4 ลิงก์จะยังใช้งานได้ มันใช้งานไม่ได้กับ "เวอร์ชันปัจจุบัน"
— doubleYou

ขอบคุณที่ชี้ให้เห็นว่ามีข้อมูลผิดพลาดอื้อฉาวมากมายที่ทำให้ devs อ้างว่าเป็นที่น่าสนใจว่า INotifyPropertyChanged เร็วกว่าหรือมีค่าใช้จ่ายน้อยกว่า DPs และไม่มีมูลความจริงเลย DPs เป็นวิธีที่รวดเร็วสวยงามและมีประสิทธิภาพในการกำหนดโครงสร้างเสมือน (ข้อมูล) ต้นไม้
— tpartee

มีสิ่งชั่วร้ายที่ซ่อนอยู่ใน DependencyObjects พวกเขาจะต้องสร้างขึ้นบนเธรดเดียวกันกับตัวควบคุมที่เชื่อมโยงกับพวกเขา นั่นหมายถึงเธรด GUI นั่นหมายความว่าคุณต้องส่งการสร้างไปยังเธรดนั้น คุณไม่สามารถโหลดและสร้างสิ่งเหล่านั้นบนเธรดพื้นหลังบางส่วนจาก DB ได้ หากคุณไม่ได้ส่งการสร้าง บ้า.
— ed22

28

ตัวเลือกขึ้นอยู่กับตรรกะทางธุรกิจของคุณและระดับนามธรรม UI หากคุณไม่ต้องการการแยกที่ดี DP จะทำงานให้คุณ

DependencyProperties จะมีผลบังคับใช้เป็นหลักในระดับ VisualElements ดังนั้นมันจะไม่เป็นความคิดที่ดีถ้าเราสร้าง DPs จำนวนมากสำหรับความต้องการทางธุรกิจแต่ละอย่างของเรา นอกจากนี้ยังมีค่าใช้จ่ายสำหรับ DP ที่สูงกว่า INotifyPropertyChanged เมื่อคุณออกแบบ WPF / Silverlight พยายามออกแบบ UI และ ViewModel แยกกันโดยสิ้นเชิงเพื่อให้ ณ เวลาใด ๆ เราสามารถเปลี่ยนการควบคุมเค้าโครงและ UI (ขึ้นอยู่กับธีมและสไตล์)

โปรดดูโพสต์นี้ - /programming/275098/what-applications-could-i-study-to-understand-datamodel-view-viewmodel ลิงก์มีการอ้างอิงถึงรูปแบบ Model-View-ViewModel ซึ่งมีความเกี่ยวข้องกับการสนทนานี้มาก


9
โพสต์โดย jbe ตอบความแตกต่างอย่างแม่นยำมากขึ้น เพียงเพราะ VM (หรือผู้นำเสนอ) สืบทอดมาจาก DependencyObject ไม่ได้หมายความว่ามันไม่สามารถจัดรูปแบบหรือไม่แยกจากเหตุผลจากมุมมองมันก็หมายความว่าที่เก็บข้อมูลสำหรับค่าคุณสมบัติแตกต่างจากเขตข้อมูลที่ประกาศอย่างชัดเจนใน สไตล์ POCO ที่ถูกกล่าวว่าเป็นอันดับความเท่าเทียมกันทางตรรกะและความสัมพันธ์ด้ายเป็นปัญหาจริงที่ DepedencyObject - based VMs ต้องจัดการกับ
— micahtan

"นอกจากนี้ยังมีค่าใช้จ่ายที่สูงขึ้นสำหรับ DP มากกว่า INotifyPropertyChanged" - แหล่งที่มาของการพิสูจน์ของคุณเกี่ยวกับเรื่องนี้คืออะไร? devs จำนวนมากทำการอ้างสิทธิ์นี้โดยไม่มีหลักฐานสนับสนุน ตาม MSDN มันไม่เป็นความจริง "ลองออกแบบ UI และ ViewModel แยกกันโดยสิ้นเชิงเพื่อให้ ณ เวลาใดก็ตามเราสามารถเปลี่ยนการควบคุมเลย์เอาต์และ UI" - อีกครั้งสิ่งนี้ไม่มีอะไรเกี่ยวข้องกับ POCO + PropChange กับ DO / DP หากมีสิ่งใดรีจิสทรีรีเฟลคชั่นและพา ธ ใน DO / DP จะช่วยเพิ่มความสามารถในการทำงานด้านภาพ
— tpartee

20

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

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


1
ตอนนี้ส่วนสตริงมีโซลูชันพร้อมตัวดำเนินการ nameof
— Newtopian

@ Newtopian: จริง [CallerMemberName]นอกจากนี้ยังมีสิ่งที่น่าสนใจบางอย่างเป็นไปได้ด้วย
— Bryan Watts

ไม่ต้องพูดถึงความมั่งคั่งของการจดทะเบียนทรัพย์สิน (Reflection) ประโยชน์ใน WPF และ CLR เมื่อใช้โมเดล DO / DP กับ POCO
— tpartee

16

INotifyPropertyChanged เมื่อใช้ยังช่วยให้คุณสามารถเพิ่มตรรกะเพิ่มเติมในรหัสของ getters และ setter ของคุณสมบัติของคุณ

DependencyProperty ตัวอย่าง:

public static DependencyProperty NameProperty = DependencyProperty.Register( "Name", typeof( String), typeof( Customer ) );

public String Name
{
    set { SetValue( NameProperty, value ); }
    get { return ( String ) GetValue( NameProperty ); }
}

ใน getter และ setter ของคุณ --- สิ่งที่คุณทำได้เพียงแค่เรียก SetValue และ GetValue ตามลำดับ b / c ในส่วนอื่น ๆ ของเฟรมเวิร์กที่ getter / setter ไม่ถูกเรียกใช้แทนมันจะเรียก SetValue, GetValue โดยตรงดังนั้นตรรกะคุณสมบัติของคุณจะไม่ ดำเนินการอย่างน่าเชื่อถือ

ด้วยINotifyPropertyChangedกำหนดกิจกรรม:

public event PropertyChangedEventHandler PropertyChanged;

และจากนั้นก็มีตรรกะใด ๆ ในรหัสของคุณแล้วโทร:

// ...
// Something cool...
// ...

if( this.PropertyChanged != null )
{
    PropertyChanged( this, new PropertyChangedEventArgs( "Name" ) );
}

// More cool stuff that will reliably happen...

สิ่งนี้อาจอยู่ใน getter / setter หรือที่อื่น ๆ


11
คุณสามารถรับการแจ้งเตือนการเปลี่ยนแปลงจาก DependencyProperties ได้เช่นกัน ดู PropertyMetadata.PropertyChangedCallback ตัวอย่างที่: msdn.microsoft.com/en-us/library/ms745795.aspx
— Joe White

2
นอกจากนี้คุณสามารถโทรไปที่ SetValue ได้จากทุกที่เช่นกันไม่ใช่แค่จากภายในที่พัก
— aL3891

นี่เป็นสิ่งที่ทำให้เข้าใจผิดและไม่จริง - มีหลายวิธีในการขอเข้าร่วมกิจกรรมการเปลี่ยนแปลงใน DP แม้ว่าจะเปลี่ยนเป็น 'ภายใน' หนึ่งในนั้นคือโจไวท์ได้ชี้ให้เห็นข้างต้น
— บุคคลที่

16

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

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

วัตถุการอ้างอิงทั้งหมดไม่สามารถต่อเนื่องกันได้ (ซึ่งอาจขัดขวางการใช้ ViewModels และ DTO (POCO)

มีความแตกต่างระหว่าง DP ภายใน Silverlight เมื่อเปรียบเทียบกับ WPF

http://msdn.microsoft.com/en-us/library/cc221408(v=VS.95).aspx

http://msdn.microsoft.com/en-us/library/cc903933(VS.95).aspx


ฉันใช้วัตถุอ้างอิงต่อเนื่องกันมาตั้งแต่ปี 2009 โดยไม่มีปัญหาดังนั้นไม่แน่ใจว่าคุณกำลังพูดถึงอะไรเมื่อคุณพูดว่า "วัตถุอ้างอิงทั้งหมดไม่สามารถต่อเนื่องกันได้" - ใช่พวกเขาทำได้ ในความเป็นจริงมีตัวเลือกมากมาย: codeproject.com/Articles/61440/… focusess.net/2008/11/25/dependencyproperty-serialization และเป็นหนึ่งในรายการโปรดส่วนตัวของฉัน: เพียงแค่ให้ร้านค้าสำรองสำหรับ DPs ทั้งหมดของคุณและทำให้เป็นอนุกรม ( ไม่มีตัวอย่างง่ายๆที่ดีที่มีอยู่ใน 2 นาทีของการค้นหาบน Google แต่ฉันรับรองว่างานนี้)
— tpartee

7

ฉันก็ต้องพิจารณาการตัดสินใจนี้เมื่อเร็ว ๆ นี้

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

ด้วย DP คุณไม่สามารถกำหนดแบ็กเอนด์ที่จัดเก็บสถานะของคุณเองได้ ฉันจะต้องให้. net แคชสำเนาของทุกรายการของรัฐที่ฉันผูกพัน ดูเหมือนว่าค่าใช้จ่ายที่ไม่จำเป็น - รัฐของฉันใหญ่และซับซ้อน

ดังนั้นที่นี่ฉันพบ INotifyPropertyChanged ดีกว่าสำหรับการเปิดเผยคุณสมบัติจากตรรกะทางธุรกิจไปยัง GUI

อย่างที่บอกไปแล้วว่าฉันต้องการวิดเจ็ต GUI แบบกำหนดเองเพื่อเปิดเผยคุณสมบัติและสำหรับการเปลี่ยนแปลงคุณสมบัตินั้นเพื่อส่งผลกระทบต่อวิดเจ็ต GUI อื่น ๆ DP พิสูจน์วิธีแก้ปัญหาง่ายๆ

ดังนั้นฉันจึงพบว่า DP มีประโยชน์สำหรับการแจ้งเตือน GUI เป็น GUI


6

เป็นความคิดที่ดีหรือไม่ที่จะมอบการพึ่งพา ViewModel ให้กับ WPF?

.NET 4.0 จะมี System.Xaml.dll ดังนั้นคุณไม่จำเป็นต้องพึ่งพาเฟรมเวิร์กใด ๆ เพื่อใช้งาน ดูโพสต์ของ Rob Relyeaเกี่ยวกับเซสชัน PDC ของเขา

ของฉัน

XAML เป็นภาษาสำหรับอธิบายวัตถุและ WPF เป็นกรอบงานซึ่งวัตถุที่อธิบายเป็นองค์ประกอบ UI

ความสัมพันธ์ของพวกเขาคล้ายกับ C # ซึ่งเป็นภาษาสำหรับอธิบายตรรกะและ. NET ซึ่งเป็นกรอบที่ใช้ตรรกะชนิดใดชนิดหนึ่งโดยเฉพาะ

วัตถุประสงค์ของ XAML คือกราฟวัตถุที่เปิดเผย เทคโนโลยี W * F เป็นตัวเลือกที่ยอดเยี่ยมสำหรับกระบวนทัศน์นี้ แต่ XAML มีอยู่เป็นอิสระจากพวกเขา

XAML และระบบการพึ่งพาทั้งหมดถูกนำมาใช้เป็นสแต็คแยกต่างหากสำหรับ WF และ WPF ซึ่งอาจใช้ประโยชน์จากประสบการณ์ของทีมที่แตกต่างกันโดยไม่ต้องสร้างการพึ่งพา (ไม่มีการเล่นสำนวนเจตนา) ระหว่างพวกเขา


โดยการตอบกลับดูเหมือนว่าคุณจะทำการสันนิษฐานว่า bitbonk ถือว่า XAML และ WPF เหมือนกัน ViewModels ควรมีการพึ่งพา WPF น้อยที่สุดเท่าที่จะทำได้เพื่อไม่ให้เพิ่มการแบ่งแยกแบบโลจิคัล แต่เพื่อลดความซับซ้อนของโค้ดและหลีกเลี่ยงปัญหาทั้งหมดที่เกี่ยวข้องกับการเขียนลอจิกในโค้ดของการควบคุมผู้ใช้ คุณจะต้องใช้แนวคิด WPF อย่างหลีกเลี่ยงไม่ได้เช่น ICommand และพฤติกรรมปัจจุบันที่มีเพียง WPF / Silverlight เท่านั้นที่จะสามารถห่อได้อย่างง่ายดาย - งานนำเสนอที่เกี่ยวข้องกับเธรดในมุมมองเท่านั้นคือ CollectionViews และ ObservableCollection
— Gusdor

6

คุณสมบัติการพึ่งพาเป็นกาวของการสร้างการควบคุมที่กำหนดเอง หากคุณสนใจที่จะใช้ Intelli-sense เพื่อแสดงคุณสมบัติของคุณในหน้าต่างคุณสมบัติในเวลาออกแบบ XAML คุณต้องใช้คุณสมบัติการพึ่งพา INPC จะไม่แสดงคุณสมบัติใด ๆ ในหน้าต่างคุณสมบัติขณะออกแบบ


4

ดูเหมือนว่าควรใช้คุณสมบัติการพึ่งพาในการควบคุมที่คุณสร้างเช่นปุ่ม ในการใช้คุณสมบัติใน XAML และใช้คุณสมบัติ WPF ทั้งหมดคุณสมบัติเหล่านั้นจะต้องมีคุณสมบัติการพึ่งพา

อย่างไรก็ตาม ViewModel ของคุณดีกว่าการใช้ INotifyPropertyChanged การใช้ INotifyPropertyChanged จะทำให้คุณมีความสามารถในการมี getter / setter logic หากคุณต้องการ

ฉันขอแนะนำให้ตรวจสอบรุ่นพื้นฐานของ Josh Smith สำหรับ ViewModel ที่ใช้ INotifyPropertyChanged แล้ว:

http://joshsmithonwpf.wordpress.com/2007/08/29/a-base-class-which-implements-inotifypropertychanged/

ฉันคิดว่านี่เป็นตัวอย่างที่ยอดเยี่ยมของวิธีการทำ ViewModel


4

ฉันคิดว่า DependencyProperty และ INotifyPropertyChanged ใช้สำหรับสองสิ่งใน Binding: สิ่งแรกที่ทำให้คุณสมบัติเป็นเป้าหมายของการเชื่อมโยงและรับอินพุตจากคุณสมบัติอื่น (ใช้ {Binding ... } เพื่อตั้งค่าคุณสมบัติ) สุดท้าย เมื่อคุณต้องการให้ค่าของคุณสมบัติถูกใช้เป็นแหล่งที่มาของการรวม (ชื่อใน Binding Path Expression) ดังนั้นทางเลือกจึงเป็นเพียงทางเทคนิค


2
INotifyPropertyChanged สามารถใช้ได้ทั้งสองกรณี คุณสามารถผูก TwoWay กับมันได้ DependencyProperty จำเป็นสำหรับเหตุผลทางเทคนิคสำหรับการกระทำบางอย่างที่ดำเนินการกับวัตถุมุมมองเท่านั้น (การตั้งค่าคุณสมบัติบางอย่างเมื่อสร้างอินสแตนซ์ของวัตถุมุมมองใน XAML เป็นต้น) DependencyProperty ไม่จำเป็นสำหรับ ViewModel
— oillio

3

ฉันชอบวิธีการโดยตรงมากขึ้นซึ่งผม blogged เกี่ยวกับในการนำเสนอรูปแบบโดยไม่ต้อง INotifyPropertyChanged คุณสามารถผูกกับคุณสมบัติ CLR ได้โดยตรงโดยไม่ต้องใช้รหัสอื่นใด ๆ คุณเพิ่งเขียนรหัส. NET แบบธรรมดาใน View Model ของคุณและมันจะได้รับการอัพเดทเมื่อ Data Model ของคุณเปลี่ยนไป


โดยไม่ต้องINotifyPropertyChanged, PropertyDescriptorมีการใช้ซึ่งเป็นสาเหตุของการรั่วไหลของหน่วยความจำ
— ลัก

ไลบรารีการควบคุมการอัปเดตที่ฉันนำเสนอในโพสต์บล็อกนั้นใช้การอ้างอิงที่อ่อนแอไม่ใช่ตัวให้คำอธิบายคุณสมบัติ มันไม่รั่วไหลของหน่วยความจำ
— Michael L Perry

1
Michael ห้องสมุดของคุณสร้างรหัสจำนวนมาก ฉันไม่เห็นประโยชน์ ฉันสามารถทำสิ่งเดียวกันโดยการสร้างโมเดล wrapper ด้วยการสร้างการเรียกเหตุการณ์ PropertyChanged
— Der_Meister

3

มีเพียงสิ่งเดียวที่ต้องเลือกDependencyObject- การผูกจะทำงานได้ดีขึ้น เพียงลองตัวอย่างด้วยListBoxและTextBoxเติมรายการที่มีข้อมูลจากINotifyPropertyChangedคุณสมบัติเทียบกับDependencyPropertyและแก้ไขรายการปัจจุบันจากTextBox...


1
ตัวอย่างโค้ดโปรด
— Hassan Tareq

1

หากคุณต้องการแสดงคุณสมบัติให้กับตัวควบคุมอื่นคุณต้องใช้คุณสมบัติการพึ่งพา ... แต่โชคดีเพราะพวกเขาใช้เวลาสักครู่ในการคิดออก ...

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