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.