Bagaimana cara menyetel ViewModel pada jendela di XAML menggunakan properti DataContext?

96

Pertanyaannya cukup banyak menjelaskan semuanya.

Saya memiliki jendela, dan telah mencoba untuk mengatur DataContext menggunakan namespace lengkap ke ViewModel, tapi sepertinya saya melakukan sesuatu yang salah.

<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">
Nicholas
sumber
Menindaklanjuti Mike Nakis, saya mencoba membuat ViewModel secara manual dan berlangganan acara di dalamnya, hanya untuk menemukan bahwa kerangka kerja sedang membuat ViewModel lain. Karenanya viewModel tempat saya berlangganan bukanlah yang dilampirkan ke view.
jlady
Apakah ini berarti bahwa selain membuat sendiri model tampilan, Anda juga menentukan jenis model tampilan dengan cara lain? Keuntungan kedua dari viewmodel yang membutuhkan parameter konstruktor adalah bahwa framework tidak dapat membuat instance-nya, atau harus meneruskan nilai default untuk parameter tersebut, dalam hal ini Anda dapat dengan mudah mendeteksi instantiation oleh framework.
Mike Nakis
Perancang XAML mungkin juga perlu dapat membuat model tampilan, tetapi perancang ini tidak pernah berguna bagi saya, (hanya menyebabkan masalah,) jadi saya tidak menggunakannya, jadi saya pribadi tidak peduli dengan kasus penggunaan itu.
Mike Nakis

Jawaban:

113

Selain solusi yang disediakan orang lain (yang baik dan benar), ada cara untuk menentukan ViewModel di XAML, namun masih memisahkan ViewModel tertentu dari View. Memisahkannya berguna saat Anda ingin menulis kasus uji yang terisolasi.

Di 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>

Di 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}"
    />
Merlyn Morgan-Graham
sumber
Oh wow ... terima kasih. Saya sudah menandai ini sebagai terjawab, tetapi penambahan Anda sangat kami hargai. Akan menggunakannya.
Nicholas
@Nicholas: Jawaban lain sempurna untuk pertanyaan itu, jadi saya setuju dengan keputusan Anda
Merlyn Morgan-Graham
8
Ketahuilah bahwa pendekatan ini menggunakan instance ViewModel yang sama untuk setiap instance MainWindow. Tidak apa-apa jika jendela adalah contoh tunggal seperti yang ditunjukkan dalam kasus ini, tetapi tidak jika Anda menampilkan beberapa contoh jendela seperti dalam kasus MDI atau aplikasi tab.
Josh
1
Sebenarnya jawaban Josh lebih baik karena memberi Anda keamanan tipe di DataContext. Jadi Anda bisa mengikat langsung ke DataContext tanpa khawatir salah mengetik nama / jalur properti.
Josh M.
149

Coba ini sebagai gantinya.

<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>
Josh
sumber
3
Saya paling suka opsi ini. Tampak lebih bersih jika VM hanya digunakan untuk MainWindow.
Andrew Grothe
13
Apakah ada cara untuk mengatur konteks data menggunakan atribut pada Windowelemen, seperti DataContext="VM:MainWindowViewModel"?
Oliver
Ini cara yang tepat!
JavierIEH
Saya tidak sepenuhnya memahami mengapa satu cara lebih baik dari yang lain. Juga, saya tidak sepenuhnya melihat perbedaan dalam salah satu cara ini dibandingkan dengan cara saya melihat beberapa orang menggunakan "Sumber Daya Dinamis". Apa ini?
Travis Tubbs
1
@Oliver Anda harus menerapkan MarkupExtension, tidak pernah melakukannya pada VM, tetapi Anda dapat melakukannya dengan konverter untuk memastikan hanya satu contoh konverter yang ada dan memanggilnya langsung dari xaml dengan ="{converters:SomethingConverter}", menyiratkan xmlns:converterstitik pada namespace konverter. 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

Anda perlu membuat instance MainViewModel dan menyetelnya sebagai konteks data. Dalam pernyataan Anda itu hanya menganggapnya sebagai nilai string.

     <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>
Joy Jobi
sumber
Terima kasih, saya pikir itu melakukan itu.
Nicholas
3

Anda mungkin ingin mencoba Catel . Ini memungkinkan Anda untuk mendefinisikan kelas DataWindow (bukan Window), dan kelas itu secara otomatis membuat model tampilan untuk Anda. Dengan cara ini, Anda bisa menggunakan deklarasi ViewModel seperti yang Anda lakukan di posting asli Anda, dan model tampilan akan tetap dibuat dan disetel sebagai DataContext.

Lihat artikel ini sebagai contoh.

Geert van Horrik
sumber
2

Ada juga cara ini untuk menentukan 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>

Semua solusi yang diusulkan sebelumnya memerlukan yang MainViewModelharus memiliki konstruktor tanpa parameter.

Microsoft mendapat kesan bahwa sistem dapat dibangun menggunakan konstruktor tanpa parameter. Jika Anda juga berada di bawah kesan itu, lanjutkan dan gunakan beberapa solusi lain.

Bagi mereka yang mengetahui bahwa konstruktor harus memiliki parameter, dan oleh karena itu pembuatan instance objek tidak dapat diserahkan di tangan kerangka kerja ajaib, cara yang tepat untuk menentukan model tampilan adalah yang saya tunjukkan di atas.

</Rant>

Mike Nakis
sumber