Comment définir un ViewModel sur une fenêtre en XAML à l'aide de la propriété DataContext?

96

La question dit à peu près tout.

J'ai une fenêtre et j'ai essayé de définir le DataContext en utilisant l'espace de noms complet sur le ViewModel, mais je semble faire quelque chose de mal.

<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">
Nicolas
la source

Réponses:

112

En plus de la solution fournie par d'autres personnes (qui sont bonnes et correctes), il existe un moyen de spécifier le ViewModel en XAML, tout en séparant le ViewModel spécifique de la vue. Les séparer est utile lorsque vous souhaitez écrire des cas de test isolés.

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

Dans 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
la source
Oh wow ... merci. J'ai déjà marqué ceci comme réponse, mais votre ajout est très apprécié. Je vais l'utiliser.
Nicholas
@Nicholas: L'autre réponse est parfaite pour la question, donc je suis d'accord avec votre décision
Merlyn Morgan-Graham
8
Sachez simplement que cette approche utilise la même instance de ViewModel pour chaque instance de MainWindow. C'est bien si la fenêtre est à instance unique comme ce cas l'implique, mais pas si vous affichez plusieurs instances de la fenêtre, comme dans le cas d'une application MDI ou à onglets.
Josh le
1
En fait, la réponse de Josh est meilleure car elle vous donne une sécurité de type sur le DataContext. Vous pouvez donc vous lier directement au DataContext sans vous soucier de taper un nom / chemin de propriété.
Josh M.
149

Essayez plutôt ceci.

<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
la source
3
J'aime cette option le plus. Semble plus propre si la VM n'est utilisée que pour MainWindow.
Andrew Grothe
13
Existe-t-il un moyen de définir le contexte des données en utilisant un attribut sur l' Windowélément, comme DataContext="VM:MainWindowViewModel"?
Oliver
C'est la bonne manière!
JavierIEH
Je ne comprends pas totalement pourquoi un moyen est meilleur que l'autre. De plus, je ne vois pas totalement la différence de l'une ou l'autre de ces manières par rapport à la façon dont j'ai vu certaines personnes utiliser «Dynamic Resource». Qu'est-ce que c'est?
Travis Tubbs
1
@Oliver vous auriez à implémenter MarkupExtension, jamais fait sur les VM, mais vous pouvez le faire avec des convertisseurs pour vous assurer qu'une seule instance du convertisseur est présente et l'appeler directement depuis xaml avec ="{converters:SomethingConverter}", ce qui implique des xmlns:converterspoints sur l'espace de noms du convertisseur. 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

Vous devez instancier le MainViewModel et le définir comme datacontext. Dans votre déclaration, il suffit de le considérer comme une valeur de chaîne.

     <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>
Joie Jobi
la source
Merci, j'ai pensé qu'il faisait ça.
Nicholas le
3

Vous voudrez peut-être essayer Catel . Il vous permet de définir une classe DataWindow (au lieu de Window), et cette classe crée automatiquement le modèle de vue pour vous. De cette façon, vous pouvez utiliser la déclaration du ViewModel comme vous l'avez fait dans votre publication d'origine, et le modèle de vue sera toujours créé et défini comme DataContext.

Consultez cet article pour un exemple.

Geert van Horrik
la source
1

Il existe également cette façon de spécifier le 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>

Toutes les solutions proposées précédemment nécessitent de MainViewModeldisposer d'un constructeur sans paramètre.

Microsoft a l'impression que les systèmes peuvent être construits à l'aide de constructeurs sans paramètres. Si vous êtes également sous cette impression, allez-y et utilisez certaines des autres solutions.

Pour ceux qui savent que les constructeurs doivent avoir des paramètres, et donc l'instanciation des objets ne peut pas être laissée entre les mains de cadres magiques, la bonne façon de spécifier le modèle de vue de celui que j'ai montré ci-dessus.

</Rant>

Mike Nakis
la source