ฉันจะตั้งค่า ViewModel บนหน้าต่างใน XAML โดยใช้คุณสมบัติ DataContext ได้อย่างไร


96

คำถามพูดได้ทั้งหมด

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

<Window x:Class="BuildAssistantUI.BuildAssistantWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    DataContext="BuildAssistantUI.ViewModels.MainViewModel">

ติดตาม Mike Nakis ฉันพยายามสร้าง ViewModel ด้วยตนเองและสมัครรับเหตุการณ์ในนั้นเพียงเพื่อจะพบว่าเฟรมเวิร์กกำลังสร้าง ViewModel อื่น ดังนั้น viewModel ที่ฉันสมัครไม่ใช่รุ่นที่แนบมากับมุมมอง
jlady

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

นักออกแบบ XAML อาจต้องสามารถสร้างอินสแตนซ์แบบจำลองมุมมองได้ แต่นักออกแบบรายนี้ไม่เคยมีประโยชน์ใด ๆ สำหรับฉันเลย (มันทำให้เกิดปัญหา) ดังนั้นฉันจึงไม่ใช้มันดังนั้นโดยส่วนตัวแล้วฉันไม่สนใจเกี่ยวกับกรณีการใช้งานนั้น
Mike Nakis

คำตอบ:


113

นอกจากวิธีแก้ปัญหาที่คนอื่นให้มา (ซึ่งดีและถูกต้อง) แล้วยังมีวิธีระบุ ViewModel ใน XAML แต่ยังแยก ViewModel เฉพาะออกจาก View การแยกสิ่งเหล่านี้มีประโยชน์เมื่อคุณต้องการเขียนกรณีทดสอบแยกต่างหาก

ใน App.xaml:

<Application
    x:Class="BuildAssistantUI.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:BuildAssistantUI.ViewModels"
    StartupUri="MainWindow.xaml"
    >
    <Application.Resources>
        <local:MainViewModel x:Key="MainViewModel" />
    </Application.Resources>
</Application>

ใน MainWindow.xaml:

<Window x:Class="BuildAssistantUI.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    DataContext="{StaticResource MainViewModel}"
    />

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

@Nicholas: คำตอบอื่นเหมาะสำหรับคำถามดังนั้นฉันเห็นด้วยกับการตัดสินใจของคุณ
Merlyn Morgan-Graham

8
โปรดทราบว่าแนวทางนี้ใช้อินสแตนซ์ ViewModel เดียวกันสำหรับทุกอินสแตนซ์ของ MainWindow ไม่เป็นไรถ้าหน้าต่างเป็นอินสแตนซ์เดียวตามที่กล่าวถึงในกรณีนี้ แต่ไม่ใช่ถ้าคุณแสดงหลายอินสแตนซ์ของหน้าต่างเช่นในกรณีของ MDI หรือแอปพลิเคชันแบบแท็บ
Josh

1
คำตอบของ Josh นั้นดีกว่าเนื่องจากให้ความปลอดภัยในการพิมพ์บน DataContext ดังนั้นคุณสามารถเชื่อมโยงโดยตรงกับ DataContext โดยไม่ต้องกังวลเกี่ยวกับการพิมพ์ชื่อ / เส้นทางคุณสมบัติบางอย่างผิดพลาด
Josh M.

149

ลองใช้วิธีนี้แทน

<Window x:Class="BuildAssistantUI.BuildAssistantWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:VM="clr-namespace:BuildAssistantUI.ViewModels">
    <Window.DataContext>
        <VM:MainViewModel />
    </Window.DataContext>
</Window>

3
ฉันชอบตัวเลือกนี้ที่สุด ดูเหมือนจะสะอาดกว่าถ้าใช้ VM สำหรับ MainWindow เท่านั้น
Andrew Grothe

13
มีวิธีตั้งค่าบริบทข้อมูลโดยใช้แอตทริบิวต์ในWindowองค์ประกอบDataContext="VM:MainWindowViewModel"หรือไม่?
Oliver

นี่คือวิธีที่เหมาะสม!
JavierIEH

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

1
@Oliver คุณจะมีการดำเนินการMarkupExtensionที่ไม่เคยทำมันใน VMs แต่คุณสามารถทำมันด้วยตัวแปลงเพื่อให้มั่นใจเพียงหนึ่งตัวอย่างของแปลงเป็นปัจจุบันและเรียกมันว่า direcly จาก XAML กับ="{converters:SomethingConverter}"หมายความxmlns:convertersจุด namespace แปลง public abstract class BaseValueConverter<T> : MarkupExtension, IValueConverter where T : class, new() { private static T _converter; public override object ProvideValue(IServiceProvider serviceProvider) { return _converter ?? (_converter = new T()); } }
Whazz

11

คุณต้องสร้างอินสแตนซ์ MainViewModel และตั้งเป็น datacontext ในคำสั่งของคุณจะถือว่าเป็นค่าสตริง

     <Window x:Class="BuildAssistantUI.BuildAssistantWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:BuildAssistantUI.ViewModels">
      <Window.DataContext>
        <local:MainViewModel/>
      </Window.DataContext>

ขอบคุณฉันคิดว่ามันทำอย่างนั้น
Nicholas

3

คุณอาจอยากลองCatel Catelช่วยให้คุณสามารถกำหนดคลาส DataWindow (แทนหน้าต่าง) และคลาสนั้นจะสร้างโมเดลมุมมองให้คุณโดยอัตโนมัติ ด้วยวิธีนี้คุณสามารถใช้การประกาศ ViewModel ได้เช่นเดียวกับที่คุณทำในโพสต์เดิมของคุณและโมเดลมุมมองจะยังคงถูกสร้างและตั้งค่าเป็น DataContext

ดูบทความนี้เป็นตัวอย่าง


3

นอกจากนี้ยังมีวิธีการระบุ viewmodel:

using Wpf = System.Windows;

public partial class App : Wpf.Application //your skeleton app already has this.
{
    protected override void OnStartup( Wpf.StartupEventArgs e ) //you need to add this.
    {
        base.OnStartup( e );
        MainWindow = new MainView();
        MainWindow.DataContext = new MainViewModel( e.Args );
        MainWindow.Show();
    }
}

<Rant>

โซลูชันทั้งหมดที่เสนอไว้ก่อนหน้านี้จำเป็นMainViewModelต้องมีตัวสร้างแบบไม่มีพารามิเตอร์

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

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

</Rant>

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