Как установить ViewModel в окне в XAML с помощью свойства DataContext?

96

Вопрос в значительной степени говорит обо всем.

У меня есть окно, и я пытался установить DataContext, используя полное пространство имен для ViewModel, но, похоже, я что-то делаю не так.

<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">
Николай
источник
Вслед за Майком Накисом я пытался создать ViewModel вручную и подписаться на события в нем, но обнаружил, что фреймворк создает еще одну ViewModel. Следовательно, viewModel, на который я подписан, не был привязан к представлению.
jlady
Означает ли это, что вы не только сами создавали экземпляр модели представления, но и каким-то другим образом указывали тип модели представления? Второе преимущество моделей просмотра, требующих параметров конструктора, заключается в том, что инфраструктура либо не может создать их экземпляры, либо должна передавать значения по умолчанию для этих параметров, и в этом случае вы можете легко обнаружить создание экземпляра структурой.
Майк Накис
Конструктору XAML также может потребоваться возможность создавать экземпляры моделей представления, но этот конструктор никогда не был для меня полезен (он просто вызывает проблемы), поэтому я не использую его, поэтому меня лично не волнует этот случай использования.
Майк Накис

Ответы:

113

В дополнение к решению, которое предоставили другие люди (которое является хорошим и правильным), есть способ указать ViewModel в XAML, но при этом отделить конкретную ViewModel от View. Их разделение полезно, когда вы хотите написать отдельные тестовые примеры.

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

В 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}"
    />
Мерлин Морган-Грэм
источник
Ого ... спасибо. Я уже отмечал это как ответ, но мы очень признательны за ваше добавление. Буду его использовать.
Николас
@Nicholas: Другой ответ идеально подходит для вопроса, поэтому я согласен с вашим решением
Мерлин Морган-Грэм
8
Просто имейте в виду, что этот подход использует один и тот же экземпляр ViewModel для каждого экземпляра MainWindow. Это нормально, если окно является однократным, как предполагает этот случай, но не в том случае, если вы показываете несколько экземпляров окна, например, в случае MDI-приложения или приложения с вкладками.
Джош
1
На самом деле ответ Джоша лучше, поскольку он обеспечивает безопасность типов в DataContext. Таким образом, вы можете выполнить привязку напрямую к DataContext, не беспокоясь об опечатке имени / пути некоторого свойства.
Джош М.
149

Попробуйте вместо этого.

<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>
Джош
источник
3
Мне этот вариант больше всего нравится. Кажется более чистым, если виртуальная машина используется только для MainWindow.
Эндрю Гроте
13
Есть ли способ установить контекст данных с помощью атрибута Windowэлемента, например DataContext="VM:MainWindowViewModel"?
Оливер
Это правильный способ!
JavierIEH
Я не совсем понимаю, почему один способ лучше другого. Кроме того, я не вижу полностью разницы ни в одном из этих способов по сравнению с тем, как я видел, как некоторые люди используют «Динамический ресурс». Что это?
Трэвис Таббс
1
@Oliver, который вам нужно реализовать MarkupExtension, никогда не делал этого на виртуальных машинах, но вы могли бы сделать это с помощью конвертеров, чтобы убедиться, что присутствует только один экземпляр конвертера, и вызвать его непосредственно из xaml с ="{converters:SomethingConverter}", подразумевая xmlns:convertersточки в пространстве имен конвертера. 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

Вам нужно создать экземпляр MainViewModel и установить его как datacontext. В вашем заявлении он просто рассматривает его как строковое значение.

     <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>
Джоби Джой
источник
Спасибо, я так и думал.
Николас
3

Вы можете попробовать Catel . Он позволяет вам определять класс DataWindow (вместо Window), и этот класс автоматически создает для вас модель представления. Таким образом, вы можете использовать объявление ViewModel, как вы это делали в исходном посте, и модель представления все равно будет создана и установлена ​​как DataContext.

См. Пример в этой статье .

Герт ван Хоррик
источник
2

Также существует такой способ указания модели просмотра:

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>

Все ранее предложенные решения требуют MainViewModelналичия конструктора без параметров.

Microsoft считает, что системы могут быть построены с использованием конструкторов без параметров. Если вы тоже под этим впечатлением, воспользуйтесь другими решениями.

Для тех, кто знает, что конструкторы должны иметь параметры, и поэтому создание экземпляров объектов нельзя оставлять в руках волшебных фреймворков, правильный способ указания модели представления - это тот, который я показал выше.

</Rant>

Майк Накис
источник