ViewModelLocator คืออะไรและข้อดี / ข้อเสียเมื่อเทียบกับ DataTemplates คืออะไร?


112

ใครช่วยสรุปให้หน่อยได้ไหมว่า ViewModelLocator คืออะไรทำงานอย่างไรและข้อดี / ข้อเสียสำหรับการใช้งานเปรียบเทียบกับ DataTemplates

ฉันได้ลองค้นหาข้อมูลใน Google แล้ว แต่ดูเหมือนว่าจะมีการใช้งานที่แตกต่างกันมากมายและไม่มีรายการที่โดดเด่นว่ามันคืออะไรและข้อดี / ข้อเสียของการใช้งาน

คำตอบ:


204

Intro

ใน MVVM วิธีปฏิบัติตามปกติคือการให้ Views ค้นหา ViewModels ของตนโดยการแก้ไขจากคอนเทนเนอร์Dependency injection (DI) สิ่งนี้จะเกิดขึ้นโดยอัตโนมัติเมื่อคอนเทนเนอร์ถูกขอให้ระบุ (แก้ไข) อินสแตนซ์ของคลาส View คอนเทนเนอร์ฉีด ViewModel ลงใน View โดยเรียกตัวสร้างของ View ซึ่งยอมรับพารามิเตอร์ ViewModel โครงร่างนี้เรียกว่าการกลับตัวควบคุม (IoC)

ประโยชน์ของ DI

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

ปัญหาที่เกิดจาก DI

จนถึงตอนนี้เราได้เห็นแล้วว่าแนวทาง DI ช่วยให้สามารถทดสอบแอปพลิเคชันได้ง่ายโดยการเพิ่มเลเยอร์นามธรรมเหนือการสร้างส่วนประกอบของแอปพลิเคชัน มีปัญหาอย่างหนึ่งในแนวทางนี้: ไม่สามารถใช้งานได้ดีกับนักออกแบบภาพเช่น Microsoft Expression Blend

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

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

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

เข้าสู่ ViewModelLocator

ViewModelLocator เป็นนามธรรมเพิ่มเติมที่ใช้ในลักษณะนี้:

  • View เองสร้างอินสแตนซ์ ViewModelLocator เป็นส่วนหนึ่งของรีซอร์สและฐานข้อมูล DataContext ของมันไปยังคุณสมบัติ ViewModel ของตัวระบุตำแหน่ง
  • ตัวระบุตำแหน่งจะตรวจพบว่าเราอยู่ในโหมดออกแบบหรือไม่
  • หากไม่ได้อยู่ในโหมดออกแบบตัวระบุตำแหน่งจะส่งกลับ ViewModel ที่แก้ไขจากคอนเทนเนอร์ DI ตามที่อธิบายไว้ข้างต้น
  • หากอยู่ในโหมดการออกแบบตัวระบุตำแหน่งจะส่งกลับ ViewModel "จำลอง" แบบคงที่โดยใช้ตรรกะของตัวมันเอง (จำไว้ว่าไม่มีคอนเทนเนอร์ในเวลาออกแบบ!); โดยทั่วไปแล้ว ViewModel นี้จะมาพร้อมกับข้อมูลจำลองล่วงหน้า

ซึ่งหมายความว่า View ต้องมีตัวสร้างแบบไม่มีพารามิเตอร์เพื่อเริ่มต้นด้วย (มิฉะนั้นผู้ออกแบบจะไม่สามารถสร้างอินสแตนซ์ได้)

สรุป

ViewModelLocator เป็นสำนวนที่ให้คุณเก็บประโยชน์ของ DI ไว้ในแอปพลิเคชัน MVVM ของคุณในขณะเดียวกันก็ช่วยให้โค้ดของคุณเล่นได้ดีกับนักออกแบบภาพ บางครั้งเรียกว่า "ความสามารถในการผสมผสาน" ของแอปพลิเคชันของคุณ (หมายถึง Expression Blend)

หลังจากการย่อยดังกล่าวข้างต้นให้ดูตัวอย่างการปฏิบัติที่นี่

สุดท้ายการใช้เทมเพลตข้อมูลไม่ใช่ทางเลือกในการใช้ ViewModelLocator แต่เป็นอีกทางเลือกหนึ่งในการใช้คู่ View / ViewModel ที่ชัดเจนสำหรับส่วนต่างๆของ UI ของคุณ บ่อยครั้งคุณอาจพบว่าไม่จำเป็นต้องกำหนด View สำหรับ ViewModel เนื่องจากคุณสามารถใช้เทมเพลตข้อมูลแทนได้


4
+1 สำหรับคำอธิบายที่ดี คุณสามารถขยายเพิ่มเติมในมุมมองและทรัพยากรได้หรือไม่ ตามทรัพยากรคุณหมายถึงคุณสมบัติของ View หรือไม่? หรือ? คุณมีลิงค์พร้อมตัวอย่างที่เป็นรูปธรรมสำหรับรูปแบบนี้หรือไม่?
Metro Smurf

@MetroSmurf: ลิงค์ของคุณอยู่ในส่วนสรุป
จอน

1
ขอบคุณ มีข้อ จำกัด ในการใช้ ViewModelLocator หรือไม่? ฉันมีข้อกังวลเกี่ยวกับข้อเท็จจริงที่อ้างถึงทรัพยากรแบบคงที่ - ViewModels สามารถสร้างแบบไดนามิกที่รันไทม์ได้หรือไม่ และมีรหัสพิเศษมากมายที่เกี่ยวข้องกับการเชื่อมต่อกันหรือไม่?
Rachel

@ Rachel: ลิงก์เดียวกันในสรุปควรตอบคำถามเหล่านี้พร้อมตัวอย่างที่ใช้ได้จริง
จอน

2
คำตอบที่ทำให้เข้าใจผิดอย่างยิ่ง จุดประสงค์หลักของ View Model Locator ไม่ใช่เพื่อให้ข้อมูลจำลองแก่ผู้ออกแบบ d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}"คุณสามารถทำเช่นนี้โดยการระบุ วัตถุประสงค์ของ Locator คือการเปิดใช้งาน DI บน Views เนื่องจาก WPF ไม่ดีในการให้บริการ ตัวอย่าง: คุณมีหน้าต่างหลักซึ่งเปิดหน้าต่างโต้ตอบบางหน้าต่าง ในการแก้ปัญหา DI บน Dialog Window ตามปกติคุณจะต้องส่งผ่านไปโดยขึ้นอยู่กับหน้าต่างหลัก! สิ่งนี้หลีกเลี่ยงได้ด้วย View Locator
hyankov

10

ตัวอย่างการใช้งานคำตอบของ@ Jon

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

public class ViewModelLocator
{
    private DependencyObject dummy = new DependencyObject();

    public IMainViewModel MainViewModel
    {
        get
        {
            if (IsInDesignMode())
            {
                return new MockMainViewModel();
            }

            return MyIoC.Container.GetExportedValue<IMainViewModel>();
        }
    }

    // returns true if editing .xaml file in VS for example
    private bool IsInDesignMode()
    {
        return DesignerProperties.GetIsInDesignMode(dummy);
    }
}

และเพื่อใช้งานฉันสามารถเพิ่มตัวระบุตำแหน่งของฉันลงในApp.xamlทรัพยากร:

xmlns:core="clr-namespace:MyViewModelLocatorNamespace"

<Application.Resources>
    <core:ViewModelLocator x:Key="ViewModelLocator" />
</Application.Resources>

จากนั้นเชื่อมต่อมุมมองของคุณ (เช่น MainView.xaml) ไปยัง viewmodel ของคุณ:

<Window ...
  DataContext="{Binding Path=MainViewModel, Source={StaticResource ViewModelLocator}}">

มีความแตกต่างในการใช้thisแทนdummyหรือไม่?
Sebastian Xawery Wiśniowiecki

5

ฉันไม่เข้าใจว่าทำไมคำตอบอื่น ๆ ของคำถามนี้จึงเกี่ยวกับตัวออกแบบ

จุดประสงค์ของ View Model Locator คือเพื่อให้ View ของคุณสร้างอินสแตนซ์ได้ (ใช่ View Model Locator = View First):

public void MyWindowViewModel(IService someService)
{
}

แทนที่จะเป็นเพียงสิ่งนี้:

public void MyWindowViewModel()
{
}

โดยประกาศสิ่งนี้:

DataContext="{Binding MainWindowModel, Source={StaticResource ViewModelLocator}}"

ViewModelLocatorคลาสอยู่ที่ไหนซึ่งอ้างอิงถึง IoC และนั่นคือวิธีแก้ปัญหาMainWindowModelคุณสมบัติที่เปิดเผย

ไม่มีอะไรเกี่ยวข้องกับการจัดหาโมเดลมุมมองจำลองให้กับมุมมองของคุณ ถ้าคุณต้องการเพียงแค่ทำ

d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}"

View Model Locator เป็นสิ่งห่อหุ้มรอบ ๆ คอนเทนเนอร์ Inversion of Control บางตัว (ใด ๆ ) เช่น Unity เป็นต้น

อ้างถึง:


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

คุณพูดถูกเมื่อพูดว่า " ฉันไม่เข้าใจว่าทำไมคำตอบอื่น ๆ [... ] ล้อมรอบตัวออกแบบ " +1 แต่ประเด็นของตัวระบุตำแหน่งคือการลบความรู้ใด ๆ เกี่ยวกับโมเดลมุมมองออกจากมุมมอง สร้างขึ้นทำให้มุมมองเป็นอิสระจากการสร้างอินสแตนซ์นี้ทิ้งไว้ให้กับตัวระบุตำแหน่ง ตัวระบุตำแหน่งจะสามารถให้รสชาติที่แตกต่างกันของรูปแบบมุมมองอาจจะมีบางอย่างที่กำหนดเองที่เพิ่มผ่านปลั๊กอินที่ตัวระบุตำแหน่งจะจัดการ (และเฉพาะสำหรับเวลาออกแบบ) มุมมองจะสะอาดในกระบวนการใด ๆ ในการค้นหารุ่นมุมมองที่ถูกต้องซึ่งเป็นสิ่งที่ดีสำหรับ SoC
นาทีที่
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.