ตัวอย่างที่ดีของเทมเพลต MVVM


141

ฉันกำลังทำงานกับเทมเพลต Microsoft MVVM และค้นหาตัวอย่างที่ไม่มีรายละเอียดที่น่าผิดหวัง ตัวอย่าง ContactBook ที่รวมไว้แสดงการจัดการคำสั่งน้อยมากและตัวอย่างอื่น ๆ ที่ฉันพบคือจากบทความในนิตยสาร MSDN ที่มีแนวคิดคล้ายกัน แต่ใช้วิธีการที่แตกต่างกันเล็กน้อยและยังไม่มีความซับซ้อนใด ๆ มีตัวอย่าง MVVM ที่เหมาะสมที่อย่างน้อยแสดงการดำเนินการ CRUD ขั้นพื้นฐานและการเปลี่ยนโต้ตอบ / เนื้อหา


คำแนะนำของทุกคนมีประโยชน์จริง ๆ และฉันจะเริ่มรวบรวมรายการทรัพยากรที่ดี

กรอบ / แม่แบบ

บทความที่มีประโยชน์

screencasts

ห้องสมุดเพิ่มเติม


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

คำตอบ:


59

น่าเสียดายที่ไม่มีแอพตัวอย่าง MVVM ที่ยอดเยี่ยมที่ทำทุกอย่างและมีวิธีการต่าง ๆ มากมายในการทำสิ่งต่าง ๆ ก่อนอื่นคุณอาจต้องการทำความคุ้นเคยกับกรอบแอพหนึ่ง (Prism เป็นตัวเลือกที่เหมาะสม) เพราะพวกเขามีเครื่องมือที่สะดวกเช่นการฉีดพึ่งพาการสั่งการรวมเหตุการณ์และอื่น ๆ เพื่อลองรูปแบบที่แตกต่างกันที่เหมาะกับคุณ .

รุ่นปริซึม:
http://www.codeplex.com/CompositeWPF

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

ปริซึมไม่จำเป็นสำหรับทุกโครงการ แต่เป็นการดีที่จะทำความคุ้นเคย

CRUD: ส่วนนี้ค่อนข้างง่าย WPF การเชื่อมสองทางทำให้ง่ายต่อการแก้ไขข้อมูลส่วนใหญ่ เคล็ดลับที่แท้จริงคือการจัดทำแบบจำลองที่ทำให้ติดตั้ง UI ได้ง่าย อย่างน้อยที่สุดคุณต้องการให้แน่ใจว่า ViewModel (หรือวัตถุทางธุรกิจ) ของคุณใช้INotifyPropertyChangedเพื่อรองรับการเชื่อมโยงและคุณสามารถเชื่อมโยงคุณสมบัติกับตัวควบคุม UI ได้โดยตรง แต่คุณอาจต้องการนำไปใช้IDataErrorInfoสำหรับการตรวจสอบความถูกต้อง โดยทั่วไปหากคุณใช้ ORM บางประเภทในการตั้งค่า CRUD จะเป็นการถ่ายภาพ

บทความนี้แสดงให้เห็นถึงการดำเนินงานที่เรียบง่าย crud: http://dotnetslackers.com/articles/wpf/WPFDataBindingWithLINQ.aspx

มันถูกสร้างขึ้นบน LinqToSql แต่นั่นไม่เกี่ยวข้องกับตัวอย่าง - ทั้งหมดที่สำคัญคือวัตถุทางธุรกิจของคุณใช้INotifyPropertyChanged(ซึ่งคลาสที่สร้างโดย LinqToSql ทำ) MVVM ไม่ได้เป็นตัวอย่าง แต่ฉันไม่คิดว่ามันจะสำคัญในกรณีนี้

บทความนี้แสดงให้เห็นถึงการตรวจสอบข้อมูล
http://blogs.msdn.com/wpfsdk/archive/2007/10/02/data-validation-in-3-5.aspx

อีกครั้งโซลูชัน ORM ส่วนใหญ่จะสร้างคลาสที่ใช้งานอยู่แล้วIDataErrorInfoและโดยปกติจะมีกลไกเพื่อให้ง่ายต่อการเพิ่มกฎการตรวจสอบความถูกต้องที่กำหนดเอง

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

มุมมองจะมีลักษณะเช่นนี้ (ViewModel มีคุณสมบัติItemที่เก็บแบบจำลองเช่นคลาสที่สร้างขึ้นใน ORM):

<StackPanel>
   <StackPanel DataContext=Item>
      <TextBox Text="{Binding FirstName, Mode=TwoWay, ValidatesOnDataErrors=True}" />
      <TextBox Text="{Binding LastName, Mode=TwoWay, ValidatesOnDataErrors=True}" />
   </StackPanel>
   <Button Command="{Binding SaveCommand}" />
   <Button Command="{Binding CancelCommand}" />
</StackPanel>

Dialogs: Dialogs และ MVVM นั้นค่อนข้างยุ่งยาก ฉันชอบที่จะใช้รสชาติของวิธีการไกล่เกลี่ยกับกล่องโต้ตอบคุณสามารถอ่านเพิ่มเติมเกี่ยวกับมันได้ในคำถาม StackOverflow:
ตัวอย่างกล่องโต้ตอบ WPF MVVM

วิธีปกติของฉันซึ่งไม่ใช่ MVVM แบบคลาสสิกสามารถสรุปได้ดังนี้:

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

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

ที่ใดที่หนึ่งที่คุณต้องการให้เทมเพลตข้อมูลสำหรับ ViewModels ของคุณสามารถทำได้ง่ายมากโดยเฉพาะอย่างยิ่งเนื่องจากคุณอาจมีมุมมองสำหรับแต่ละกล่องโต้ตอบที่ห่อหุ้มในตัวควบคุมแยกต่างหาก เทมเพลตข้อมูลเริ่มต้นสำหรับ ViewModel จะมีลักษณะดังนี้:

<DataTemplate DataType="{x:Type vmodels:AddressEditViewModel}">
   <views:AddressEditView DataContext="{Binding}" />
</DataTemplate>

มุมมองไดอะล็อกจำเป็นต้องเข้าถึงสิ่งเหล่านี้เนื่องจากไม่เช่นนั้นจะไม่รู้วิธีแสดง ViewModel นอกเหนือจาก UI ของไดอะล็อกที่แบ่งใช้แล้วเนื้อหาจะเป็นแบบนี้:

<ContentControl Content="{Binding}" />

เทมเพลตข้อมูลโดยนัยจะแมปมุมมองกับโมเดล แต่ใครเป็นผู้เปิดใช้

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

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

การตั้งค่าด้วยวิธีนี้ทำให้ ViewModels ขอให้แอปพลิเคชันเปิดกล่องโต้ตอบและตอบสนองต่อการกระทำของผู้ใช้ที่นั่นโดยไม่ทราบอะไรเกี่ยวกับ UI ดังนั้นส่วนใหญ่ MVVM-ness ยังคงสมบูรณ์

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

โค้ดหลอกของตัวจัดการปุ่มที่เพิ่มไดอะล็อกที่ต้องการข้อมูลตำแหน่งองค์ประกอบ:

ButtonClickHandler(sender, args){
    var vm = DataContext as ISomeDialogProvider; // check for null
    var ui_vm = new ViewModelContainer();
    // assign margin, width, or anything else that your custom dialog might require
    ...
    ui_vm.ViewModel = vm.SomeDialogViewModel; // or .GetSomeDialogViewModel()
    // raise the dialog show event
}

มุมมองโต้ตอบจะผูกกับข้อมูลตำแหน่งและผ่านการมี ViewModel ContentControlไปภายใน ViewModel เองยังไม่รู้อะไรเกี่ยวกับ UI

โดยทั่วไปฉันไม่ได้ใช้DialogResultคุณสมบัติส่งคืนของShowDialog()เมธอดหรือคาดว่าเธรดจะบล็อกจนกว่าไดอะล็อกจะปิด กล่องโต้ตอบโมดอลที่ไม่ได้มาตรฐานจะไม่ทำงานเช่นนั้นเสมอไปและในสภาพแวดล้อมแบบคอมโพสิตคุณมักไม่ต้องการให้ตัวจัดการเหตุการณ์บล็อคเช่นนั้น แต่อย่างใด ฉันต้องการให้ ViewModels จัดการกับสิ่งนี้ - ผู้สร้าง ViewModel สามารถสมัครสมาชิกกับกิจกรรมที่เกี่ยวข้องกำหนดวิธีการยอมรับ / ยกเลิกเป็นต้นดังนั้นจึงไม่จำเป็นต้องพึ่งพากลไก UI นี้

ดังนั้นแทนที่จะไหลนี้:

// in code behind
var result = somedialog.ShowDialog();
if (result == ...

ฉันใช้:

// in view model
var vm = new SomeDialogViewModel(); // child view model
vm.CommitAction = delegate { this.DoSomething(vm); } // what happens on commit 
vm.CancelAction = delegate { this.DoNothing(vm); } // what happens on cancel/close (optional)
// raise dialog request event on the container

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


ขอบคุณสำหรับคำตอบอย่างละเอียด! ฉันเพิ่งพบว่าปัญหาที่ใหญ่ที่สุดของฉันคือเมื่อฉันต้องการให้ MainViewModel สื่อสารกับโมเดลมุมมองอื่น ๆ เพื่อจัดการการไหลของแอปพลิเคชัน อย่างไรก็ตามดูเหมือนว่า MVVM + Mediator น่าจะเป็นวิธีที่ได้รับความนิยม
jwarzech

2
คนกลางช่วยได้อย่างแน่นอนรูปแบบตัวรวบรวมเหตุการณ์ (ปริซึมมีการใช้งานที่ดี) ก็มีประโยชน์เช่นกันเมื่อการมีเพศสัมพันธ์ต่ำเป็นเป้าหมาย นอกจากนี้โมเดลมุมมองหลักของคุณมักมีมุมมองแบบลูกของตัวเองและไม่ควรมีปัญหาในการสื่อสารกับพวกเขา คุณต้องใช้ผู้ไกล่เกลี่ยหรือ / และผู้รวบรวมเหตุการณ์เมื่อมุมมองลูกของคุณจำเป็นต้องมีปฏิสัมพันธ์กับโมดูลอื่นในแอปของคุณซึ่งพวกเขาไม่จำเป็นต้องรู้ - รวมถึง UI (ตัวอย่างไดอะล็อกของฉันเกี่ยวกับกรณีนี้โดยเฉพาะ)
Egor

1
แนวทางการทำงานกับกล่องโต้ตอบและหน้าต่างมีประโยชน์จริง ๆ อย่างไรก็ตามฉันติดปัญหาบางอย่าง: 1. คุณตั้งชื่อหน้าต่างจากมุมมองได้อย่างไร? 2. คุณจัดการกับการตั้งค่าหน้าต่างเจ้าของได้อย่างไร?
djskinner

@ แดเนียลสกินเนอร์: ฉันกำลังสมมติว่าคุณกำลังพูดถึงการสนทนาที่นี่แก้ไขฉันถ้าฉันผิด หัวเรื่องของกล่องโต้ตอบเป็นอีกคุณสมบัติหนึ่งและคุณสามารถผูกกับสิ่งที่คุณต้องการ ถ้าคุณทำตามวิธีการของฉันด้วยคลาส viewmodel โต้ตอบ (สมมติว่ามันมีคุณสมบัติชื่อเรื่อง) จากนั้นในหน้าต่างโต้ตอบทั่วไปทั้งหมดของคุณคุณสามารถใช้ UI เป็น UI ผูกพันเพื่อตั้งชื่อเป็น {Binding Path = DataContext.Title, ElementName = NameOfContentPresenter} หน้าต่างของเจ้าของเป็นเล่ห์เหลี่ยมเล็กน้อย - หมายความว่าผู้ไกล่เกลี่ยที่ปรากฏขึ้นจริงโต้ตอบจำเป็นต้องรู้เกี่ยวกับมุมมองแอปรูท
Egor

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

6

เจสัน Dolinger ทำดีscreencastของ MVVM เช่นเดียวกับ Egor ที่กล่าวถึงไม่มีตัวอย่างที่ดี พวกเขาทั้งหมด ส่วนใหญ่เป็นตัวอย่าง MVVM ที่ดี แต่ไม่ใช่เมื่อคุณมีปัญหาที่ซับซ้อน ทุกคนมีวิธีการของตนเอง Laurent Bugnion มีวิธีที่ดีในการสื่อสารระหว่าง viewmodels เช่นกัน http://blog.galasoft.ch/archive/2009/09/27/mvvm-light-toolkit-messenger-v2-beta.aspx Cinch ก็เป็นตัวอย่างที่ดีเช่นกัน พอล Stovel มีดีโพสต์ที่อธิบายมากเกินไปกับกรอบเจลลันของเขา


3

มีคุณดูCaliburn ? ตัวอย่าง ContactManager มีสิ่งดีๆมากมายอยู่ในนั้น ตัวอย่าง WPF ทั่วไปยังให้ภาพรวมที่ดีของคำสั่ง เอกสารประกอบค่อนข้างดีและมีการใช้งานฟอรัมอยู่ แนะนำ!




2

ตัวอย่างโครงการในกรอบงาน Cinchแสดงเครื่องมือ CRUD และเครื่องมือนำทางขั้นพื้นฐาน มันเป็นตัวอย่างที่ดีในการใช้ MVVM และรวมถึงบทความหลายส่วนที่อธิบายการใช้งานและแรงจูงใจ


2

ฉันยังแบ่งปันในความคับข้องใจของคุณ ฉันกำลังเขียนแอปพลิเคชันและฉันมีข้อกำหนด 3 ข้อต่อไปนี้:

  • Extensible
  • WPF พร้อม MVVM
  • ตัวอย่างที่เข้ากันได้กับ GPL

ทั้งหมดที่ฉันพบคือบิตและชิ้นส่วนดังนั้นฉันเพิ่งเริ่มเขียนมันให้ดีที่สุดเท่าที่จะทำได้ หลังจากที่ฉันได้รับมันฉันรู้ว่าอาจมีคนอื่น (เช่นตัวคุณ) ที่สามารถใช้แอปพลิเคชันอ้างอิงได้ดังนั้นฉันจึงปรับโครงสร้างสิ่งทั่วไปออกเป็นเฟรมเวิร์กแอปพลิเคชัน WPF / MVVM และเผยแพร่ภายใต้ LGPL ผมตั้งชื่อมันSoapbox หลัก หากคุณไปที่หน้าดาวน์โหลดคุณจะเห็นว่ามันมาพร้อมกับแอปพลิเคชันสาธิตขนาดเล็กและซอร์สโค้ดสำหรับแอปพลิเคชันสาธิตนั้นยังสามารถดาวน์โหลดได้ หวังว่าคุณจะพบว่ามีประโยชน์ และส่งอีเมลถึงฉันที่สกอตต์ {at} soapboxautomation.com หากคุณต้องการข้อมูลเพิ่มเติม

แก้ไข : โพสต์บทความ CodeProject ยังอธิบายถึงวิธีการทำงาน


2

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

ป้อนคำอธิบายรูปภาพที่นี่


1

แม้ฉันจะแบ่งปันความหงุดหงิดจนกระทั่งฉันหยิบเรื่องนี้ไว้ในมือของฉัน ฉันเริ่ม IncEditor

IncEditor ( http://inceditor.codeplex.com ) เป็นตัวแก้ไขที่พยายามแนะนำนักพัฒนาให้รู้จักกับ WPF, MVVM & MEF ฉันเริ่มและจัดการเพื่อรับฟังก์ชั่นบางอย่างเช่นการสนับสนุน 'ธีม' ฉันไม่มีความเชี่ยวชาญใน WPF หรือ MVVM หรือ MEF ดังนั้นฉันจึงไม่สามารถใช้ฟังก์ชั่นได้มากมาย ฉันขอให้พวกคุณอย่างจริงใจเพื่อทำให้ดีขึ้นเพื่อให้ nutters เช่นฉันสามารถเข้าใจได้ดีขึ้น

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