¿Cómo configuro un ViewModel en una ventana en XAML usando la propiedad DataContext?

96

La pregunta prácticamente lo dice todo.

Tengo una ventana y he intentado configurar el DataContext usando el espacio de nombres completo en ViewModel, pero parece que estoy haciendo algo 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">
Nicolás
fuente

Respuestas:

112

Además de la solución que proporcionaron otras personas (que es buena y correcta), hay una manera de especificar ViewModel en XAML, pero aún así separar el ViewModel específico de la View. Separarlos es útil cuando desea escribir casos de prueba aislados.

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

En 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
fuente
Oh wow ... gracias. Ya marqué esto como respondido, pero su adición es muy apreciada. Lo usaré.
Nicholas
@Nicholas: La otra respuesta es perfecta para la pregunta, así que estoy de acuerdo con su decisión
Merlyn Morgan-Graham
8
Solo tenga en cuenta que este enfoque usa la misma instancia de ViewModel para cada instancia de MainWindow. Eso está bien si la ventana es de una sola instancia, como implica este caso, pero no si está mostrando varias instancias de la ventana, como en el caso de una aplicación MDI o con pestañas.
Josh
1
En realidad, la respuesta de Josh es mejor, ya que le brinda seguridad de tipo en DataContext. Por lo tanto, puede vincularse directamente al DataContext sin preocuparse por escribir el nombre / ruta de alguna propiedad.
Josh M.
149

Prueba esto en su lugar.

<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
fuente
3
Me gusta más esta opción. Parece más limpio si la VM solo se usa para MainWindow.
Andrew Grothe
13
¿Hay alguna forma de establecer el contexto de datos usando un atributo en el Windowelemento, como DataContext="VM:MainWindowViewModel"?
Oliver
¡Esta es la forma correcta!
JavierIEH
No entiendo completamente por qué una forma es mejor que la otra. Además, no veo totalmente la diferencia en ninguna de estas formas en comparación con la forma en que he visto a algunas personas usar "Recursos dinámicos". ¿Que es esto?
Travis Tubbs
1
@Oliver tendría que implementar MarkupExtension, nunca lo hizo en máquinas virtuales, pero podría hacerlo con convertidores para asegurarse de que solo haya una instancia del convertidor y llamarlo directamente desde xaml con ="{converters:SomethingConverter}", lo que implica xmlns:converterspuntos en el espacio de nombres del convertidor. 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

Debe crear una instancia de MainViewModel y configurarlo como datacontext. En su declaración, simplemente considérelo como un valor de cadena.

     <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
fuente
Gracias, pensé que estaba haciendo eso.
Nicholas
3

Quizás quieras probar Catel . Le permite definir una clase DataWindow (en lugar de Window), y esa clase crea automáticamente el modelo de vista por usted. De esta manera, puede usar la declaración de ViewModel como lo hizo en su publicación original, y el modelo de vista aún se creará y establecerá como DataContext.

Consulte este artículo para ver un ejemplo.

Geert van Horrik
fuente
1

También existe esta forma de especificar el modelo de vista:

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>

Todas las soluciones propuestas anteriormente requieren que MainViewModeldeba tener un constructor sin parámetros.

Microsoft tiene la impresión de que los sistemas se pueden construir utilizando constructores sin parámetros. Si también tiene esa impresión, siga adelante y use algunas de las otras soluciones.

Para aquellos que saben que los constructores deben tener parámetros y, por lo tanto, la instanciación de objetos no puede dejarse en manos de marcos mágicos, la forma adecuada de especificar el modelo de vista del que mostré anteriormente.

</Rant>

Mike Nakis
fuente