Como defino um ViewModel em uma janela em XAML usando a propriedade DataContext?

96

A pergunta diz quase tudo.

Eu tenho uma janela e tentei definir o DataContext usando o namespace completo para o ViewModel, mas parece que estou fazendo algo errado.

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

Respostas:

112

Além da solução que outras pessoas forneceram (que são boas e corretas), há uma maneira de especificar o ViewModel em XAML, mas ainda separar o ViewModel específico da View. Separá-los é útil quando você deseja escrever casos de teste isolados.

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

Em 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
fonte
Oh uau ... obrigado. Já marquei como respondido, mas sua adição é muito apreciada. Vou usar.
Nicholas
@ Nicholas: A outra resposta é perfeita para a pergunta, então concordo com sua decisão
Merlyn Morgan-Graham
8
Esteja ciente de que essa abordagem usa a mesma instância de ViewModel para todas as instâncias de MainWindow. Isso é bom se a janela for de instância única como este caso implica, mas não se você estiver mostrando várias instâncias da janela, como no caso de um MDI ou aplicativo com guias.
Josh
1
Na verdade, a resposta de Josh é melhor, pois oferece segurança de digitação no DataContext. Portanto, você pode vincular diretamente ao DataContext sem se preocupar em digitar algum nome / caminho de propriedade.
Josh M.
149

Em vez disso, tente isso.

<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
fonte
3
Eu gosto mais dessa opção. Parece mais limpo se a VM for usada apenas para a MainWindow.
Andrew Grothe
13
Existe uma maneira de definir o contexto de dados usando um atributo no Windowelemento, como DataContext="VM:MainWindowViewModel"?
Oliver
Esta é a maneira correta!
JavierIEH
Não estou entendendo totalmente porque uma maneira é melhor do que a outra. Além disso, não vejo totalmente a diferença em qualquer uma dessas maneiras em comparação a como vi algumas pessoas usarem "Recursos Dinâmicos". O que é isso?
Travis Tubbs
1
@Oliver você teria que implementar MarkupExtension, nunca fez em VMs, mas você poderia fazer isso com conversores para garantir que apenas uma instância do conversor estivesse presente e chamá-lo diretamente do xaml com ="{converters:SomethingConverter}", implicando em xmlns:converterspontos no namespace do conversor. 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

Você precisa instanciar o MainViewModel e configurá-lo como datacontext. Em sua declaração, considere-o apenas como um valor de 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>
Jobi Joy
fonte
Obrigado, imaginei que fosse isso.
Nicholas
3

Você pode querer experimentar Catel . Ele permite que você defina uma classe DataWindow (em vez de Window), e essa classe cria automaticamente o modelo de visualização para você. Dessa forma, você pode usar a declaração do ViewModel como fez em sua postagem original, e o modelo de visualização ainda será criado e definido como DataContext.

Veja este artigo para um exemplo.

Geert van Horrik
fonte
1

Também existe esta maneira de especificar o modelo de visualização:

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 as soluções propostas anteriormente requerem que MainViewModeltenha um construtor sem parâmetros.

A Microsoft tem a impressão de que os sistemas podem ser construídos usando construtores sem parâmetros. Se você também tiver essa impressão, vá em frente e use algumas das outras soluções.

Para quem sabe que construtores devem ter parâmetros, portanto a instanciação de objetos não pode ser deixada nas mãos de frameworks mágicos, a maneira adequada de especificar o modelo de visão que mostrei acima.

</Rant>

Mike Nakis
fonte