Jak ustawić ViewModel w oknie w XAML przy użyciu właściwości DataContext?

96

To pytanie mówi wszystko.

Mam okno i próbowałem ustawić DataContext przy użyciu pełnej przestrzeni nazw na ViewModel, ale wydaje się, że robię coś nie tak.

<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">
Mikołaja
źródło
Idąc za Mike'iem Nakisem, próbowałem ręcznie stworzyć ViewModel i zasubskrybować w nim zdarzenia, tylko po to, aby stwierdzić, że framework tworzy inny ViewModel. Stąd viewModel, który subskrybowałem, nie był tym dołączonym do widoku.
jlady
Czy to oznacza, że ​​oprócz samodzielnego tworzenia wystąpienia modelu widoku, określasz również typ tego modelu w inny sposób? Dodatkową zaletą modeli widoków wymagających parametrów konstruktora jest to, że platforma albo nie może ich instancji, albo musi przekazywać wartości domyślne dla tych parametrów, w którym to przypadku można łatwo wykryć instancje przez platformę.
Mike Nakis
Projektant XAML może również potrzebować możliwości tworzenia wystąpień modeli widoku, ale ten projektant nigdy nie był dla mnie przydatny (tylko powoduje problemy), więc nie używam go, więc osobiście nie obchodzi mnie ten przypadek użycia.
Mike Nakis

Odpowiedzi:

112

Oprócz rozwiązania dostarczonego przez inne osoby (które są dobre i poprawne), istnieje sposób na określenie ViewModel w XAML, ale nadal oddzielenie określonego ViewModel od widoku. Rozdzielanie ich jest przydatne, gdy chcesz pisać pojedyncze przypadki testowe.

W załączniku 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>

W pliku 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
źródło
O wow ... dzięki. Oznaczyłem już to jako odpowiedź, ale Twój dodatek jest bardzo doceniany. Wykorzysta to.
Nicholas
@Nicholas: Druga odpowiedź jest idealna na to pytanie, więc zgadzam się z twoją decyzją
Merlyn Morgan-Graham
8
Należy tylko pamiętać, że to podejście wykorzystuje to samo wystąpienie ViewModel dla każdego wystąpienia MainWindow. To dobrze, jeśli okno jest pojedynczym wystąpieniem, jak sugeruje ten przypadek, ale nie, jeśli wyświetlasz wiele wystąpień okna, na przykład w przypadku aplikacji MDI lub aplikacji z kartami.
Josh
1
Właściwie odpowiedź Josha jest lepsza, ponieważ zapewnia bezpieczeństwo typów w DataContext. Możesz więc powiązać się bezpośrednio z DataContext, nie martwiąc się o wpisanie nazwy / ścieżki właściwości.
Josh M.
149

Spróbuj tego zamiast tego.

<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
źródło
3
Ta opcja podoba mi się najbardziej. Wydaje się czystszy, jeśli maszyna wirtualna jest używana tylko dla MainWindow.
Andrew Grothe,
13
Czy istnieje sposób na ustawienie kontekstu danych przy użyciu atrybutu Windowelementu, takiego jak DataContext="VM:MainWindowViewModel"?
Oliver
To jest właściwy sposób!
JavierIEH
Nie do końca rozumiem, dlaczego jeden sposób jest lepszy od drugiego. Nie widzę też całkowicie różnicy w obu tych aspektach w porównaniu z tym, jak widziałem, jak niektórzy ludzie używają „Zasobów Dynamicznych”. Co to jest?
Travis Tubbs
1
@Oliver musiałbyś zaimplementować MarkupExtension, nigdy nie robił tego na maszynach wirtualnych, ale możesz to zrobić za pomocą konwerterów, aby upewnić się, że występuje tylko jedna instancja konwertera i wywołać ją bezpośrednio z xaml z ="{converters:SomethingConverter}", co oznacza xmlns:converterspunkty w przestrzeni nazw konwertera. 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

Musisz utworzyć wystąpienie MainViewModel i ustawić go jako kontekst danych. W swoim oświadczeniu traktuje to po prostu jako wartość łańcuchową.

     <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>
Jobi Joy
źródło
Dzięki, pomyślałem, że to robi.
Nicholas
3

Możesz spróbować Catel . Pozwala zdefiniować klasę DataWindow (zamiast Window), która automatycznie tworzy model widoku za Ciebie. W ten sposób możesz użyć deklaracji ViewModel, tak jak w oryginalnym wpisie, a model widoku nadal zostanie utworzony i ustawiony jako DataContext.

Zobacz przykład w tym artykule .

Geert van Horrik
źródło
2

Istnieje również sposób określenia modelu widoku:

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>

Wszystkie wcześniej proponowane rozwiązania wymagają, MainViewModelaby mieć konstruktora bez parametrów.

Microsoft ma wrażenie, że systemy można budować za pomocą konstruktorów bez parametrów. Jeśli również jesteś pod takim wrażeniem, śmiało skorzystaj z innych rozwiązań.

Dla tych, którzy wiedzą, że konstruktorzy muszą mieć parametry, a zatem instancji obiektów nie można pozostawić w rękach magicznych frameworków, właściwym sposobem określenia viewmodelu jest ten, który pokazałem powyżej.

</Rant>

Mike Nakis
źródło