Как установить 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>

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.