ใครควรควบคุมการนำทางในแอปพลิเคชัน MVVM


33

ตัวอย่าง # 1: ฉันมีมุมมองที่แสดงในแอปพลิเคชัน MVVM ของฉัน (ลองใช้ Silverlight เพื่อจุดประสงค์ในการอภิปราย) และฉันคลิกที่ปุ่มที่ควรพาฉันไปที่หน้าใหม่

ตัวอย่าง # 2: มุมมองเดียวกันนั้นมีปุ่มอื่นที่เมื่อคลิกแล้วควรเปิดมุมมองรายละเอียดในหน้าต่างลูก (กล่องโต้ตอบ)

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

เพื่อให้เฉพาะเจาะจงมากขึ้นในโมเดล View-first แบบดั้งเดิม (เช่นโครงร่างการนำทางตาม URL เช่นบนเว็บหรือเฟรมเวิร์กการนำทางในตัว SL) วัตถุคำสั่งจะต้องรู้ว่ามุมมองใดที่จะแสดงถัดไป ที่ดูเหมือนว่าจะข้ามเส้นเมื่อมันมาถึงการแยกความกังวลที่ส่งเสริมโดยรูปแบบ

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

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

ตามการประเมินนี้มันขึ้นอยู่กับกลไกอื่น ๆ ณ รันไทม์เพื่อ 'ผูก' สิ่งที่ควรดูการแสดงผลสำหรับแต่ละ ViewModel แต่ถ้าเราต้องการแชร์ View ด้วย ViewModels หลาย ๆ ตัวหรือในทางกลับกันล่ะ

เมื่อได้รับความต้องการในการจัดการความสัมพันธ์ View-ViewModel เพื่อให้เรารู้ว่าจะแสดงเมื่อใดพร้อมกับความต้องการนำทางระหว่างมุมมองรวมถึงการแสดงหน้าต่าง / กล่องโต้ตอบลูกเราจะทำสิ่งนี้ในรูปแบบ MVVM ได้อย่างไร?

คำตอบ:


21

การนำทางควรได้รับการจัดการใน ViewModel เสมอ

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

ฉันมักจะมีApplicationViewModelหรือShellViewModelที่จัดการสถานะโดยรวมของใบสมัครของฉัน ซึ่งรวมถึงCurrentPage(ซึ่งเป็น ViewModel) ChangePageEventsและรหัสสำหรับการจัดการ (มักใช้กับออบเจ็กต์ทั่วทั้งแอปพลิเคชันอื่นเช่น CurrentUser หรือ ErrorMessages ด้วย)

ดังนั้นหาก ViewModel ใด ๆ ทุกออกอากาศChangePageEvent(new SomePageViewModel)ที่ShellViewModelรถกระบะพระทัยว่าข้อความและเปลี่ยนCurrentPageเพื่อสิ่งที่หน้าถูกระบุในข้อความ

ฉันเขียนโพสต์บล็อกเกี่ยวกับการนำทางด้วย MVVMหากคุณสนใจ


2
แนวทางที่น่าสนใจ สี่ความคิดเห็น: 1) Silverlight ไม่สนับสนุนคุณสมบัติ DataType บน DataTemplate ดังนั้นการแมป DataTemplate กับ ViewModel นั้นไม่สามารถทำได้ใน SL 2) นี่ไม่ได้ระบุถึงความเป็นไปได้แบบหลายต่อหลายอย่างระหว่าง Views และ ViewModels 3) มันไม่ได้จัดการกับหน้าต่างลูก (หรืออย่างน้อยฉันก็ไม่เห็นวิธี) 4) มันต้องมีการเชื่อมต่ออย่างแน่นหนาระหว่าง Application / Shell ViewModel ของคุณกับมัน (ลูกหลาน ฯลฯ ) ถ้าฉันมี 40 หน้าในแอพของฉัน ViewModel นี้จะเป็นวิธีที่ยุ่งยากในการจัดการ
SonOfPirate

ที่กล่าวว่ามันเป็นสิ่งที่ต้องพิจารณาอย่างแน่นอน
SonOfPirate

@SonOfPirate 1) Silverlight ไม่รองรับการแมป DataTemplate โดยปริยาย (แต่) อย่างไรก็ตามมันรองรับ DataTemplateSelectorซึ่งเป็นสิ่งที่ฉันมักจะใช้กับแอป Silverlight 2) ฉันเคยใช้มันในหลาย ๆ สถานการณ์ก่อนหน้านี้ ตัวอย่างเช่นหนึ่ง ViewModel สามารถมีหลาย Views หรือหนึ่ง View สามารถเชื่อมโยงกับ ViewModels หลายรายการ ยกตัวอย่างเช่นในอดีตให้ดูrachel53461.wordpress.com/2011/05/28/... สำหรับในภายหลังคุณเพียงแค่ต้องระบุว่าหลาย ViewModels แมปไปยังมุมมองเดียวกันใน DataTemplateSelector ของคุณ
Rachel

3) นั่นเป็นเพียงตัวอย่างพื้นฐาน หากคุณรู้ว่าแอปพลิเคชั่นของคุณจะเป็นหน้าต่างหลายบานคุณจะต้องเปลี่ยน ShellViewModel เป็นหลาย ๆ หน้าต่างCurrentPages 4) อีกครั้งนั่นเป็นเพียงตัวอย่างพื้นฐาน ในความเป็นจริง PageViewModels ของฉันทุกคนตามออกชั้นฐานบางอย่างเพื่อให้ ShellViewModel IPageViewModelของฉันเท่านั้นทำงานร่วมกับชั้นฐานหรืออินเตอร์เฟซเช่น จริงๆแล้วชิ้นส่วนที่ยุ่งที่สุดของการทำแผนที่คือ DataTemplateSelector ซึ่งจะต้องทำแผนที่ 40 Views to 40 ViewModels
Rachel

@SonOfPirate ฉันหวังว่าจะตอบคำถามของคุณ รู้สึกอิสระที่จะมองฉันขึ้นในการพูดคุยถ้าคุณมีคนอื่น ๆ :)
ราเชล

6

เพื่อประโยชน์ในการปิดฉันคิดว่าฉันจะโพสต์ทิศทางที่ฉันเลือกที่จะแก้ปัญหานี้ในที่สุด

การตัดสินใจครั้งแรกคือการใช้ประโยชน์จากเฟรมเวิร์กการนำทางของ Silverlight ที่จัดหาให้นอกกรอบ การตัดสินใจครั้งนี้ขึ้นอยู่กับปัจจัยหลายประการรวมถึงความรู้ที่ว่าการนำทางประเภทนี้กำลังถูกส่งต่อโดย Microsoft ในแอพ Windows 8 Metro และสอดคล้องกับการนำทางในแอพ Phone 7

เพื่อให้มันทำงานได้ฉันไปดูงานที่ ASP.NET MVC ทำกับการนำทางด้วยการประชุม ตัวควบคุมเฟรมใช้ URI เพื่อค้นหา 'หน้า' เพื่อแสดง ความคล้ายคลึงกันเปิดโอกาสให้ใช้วิธีการประชุมที่คล้ายกันในแอป Silverlight เคล็ดลับคือทำให้ทุกอย่างทำงานร่วมกันในรูปแบบ MVVM

การแก้ปัญหาคือ NavigationService บริการนี้แสดงหลายวิธีเช่น NavigateTo และ Back ที่ ViewModels สามารถใช้เพื่อเริ่มต้นการเปลี่ยนหน้า เมื่อมีการร้องขอหน้าใหม่ NavigationService จะส่ง CurrentPageChangedMessage โดยใช้คุณสมบัติ MVVMLight Messenger

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

กลับไปที่ NavigationService กระบวนการ NavigateTo วิธียอมรับชื่อของหน้าเป้าหมาย เพื่อให้แน่ใจว่า ViewModels ไม่มีส่วนเกี่ยวข้องกับ UI ชื่อที่ใช้คือชื่อของ ViewModel ที่จะแสดง ฉันสร้างการแจงนับที่มีฟิลด์สำหรับแต่ละ ViewModel ที่ใช้งานได้ในฐานะผู้ช่วยและกำจัดสายเวททั่วแอพ ฟังก์ชั่นการทำแผนที่ที่ฉันกล่าวถึงข้างต้นจะตัดส่วนต่อท้าย "ViewModel" จากชื่อผนวก "หน้า" กับชื่อและตั้งชื่อเต็มเป็น "Views {Name} Page.xaml"

ตัวอย่างเช่นเพื่อไปยังมุมมองรายละเอียดลูกค้าฉันสามารถโทร:

NavigationService.NavigateTo(ViewModels.CustomerDetails);

ค่าของ CustomerDetails คือ "CustomerDetailsViewModel" ซึ่งถูกแมปกับ "Views \ CustomerDetailsPage.xaml"

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

หวังว่าคำอธิบายจะช่วยได้


2

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

ในใบสมัครของฉันของฉันเริ่มต้นด้วยPresenter ดักสื่อสารทั้งหมดระหว่างและ หนึ่งในสิ่งที่สามารถทำได้ในระหว่างการโต้ตอบคือการคืนค่าใหม่ซึ่งหมายความว่าหน้าใหม่หรือใหม่ควรจะแสดง ดูแลการสร้างหรือเขียนทับใหม่และการตั้งค่าCurrentViewModelPresenterViewViewModelViewModelViewModelWindowPresenterViewViewModelDataContext

ผลลัพธ์ของการดำเนินการอาจเป็นได้ว่าViewModel"เสร็จสมบูรณ์" ซึ่งในกรณีPresenterนี้จะตรวจจับและปิดหน้าต่างหรือViewModelดึงสิ่งนี้ออกจากสแต็ก VM และกลับไปแสดงหน้าก่อนหน้า


ผู้นำเสนอรู้ได้อย่างไรว่ามุมมองใดที่จะแสดง
SonOfPirate

1
@SonOfPirate - โดยปกติจะทำผ่านกลไก WPF Presenterเพียงแท่งกลับViewModelในภาพต้นไม้และ WPF คว้าเหมาะสมViewตะขอขึ้นDataContextและทำให้เห็นว่าในภาพต้นไม้แทน คุณสามารถทำได้โดยใช้DataTemplates ที่ประกาศViewModelประเภทที่พวกเขาแสดงผลหรือคุณสามารถสร้างตัวเลือกแม่แบบข้อมูลที่กำหนดเอง ยังคงอยู่ในขอบเขตของคุณสมบัติ WPF
Scott Whitlock
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.