112

The question pretty much says it all.

I have a window, and have tried to set the DataContext using the full namespace to the ViewModel, but I seem to be doing something wrong.

<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">
Binil
  • 6,445
  • 3
  • 30
  • 40
Nicholas
  • 3,286
  • 5
  • 27
  • 35
  • Following up on Mike Nakis, I was trying to create the ViewModel manually and subscribe to events in it, only to find that the framework was creating another ViewModel. Hence the viewModel I was subscribed to was not the one attached to the view. – jlady Aug 27 '20 at 21:21
  • Does this mean that besides instantiating the viewmodel yourself, you were ***also*** specifying the type of the viewmodel in some other way ? A secondary advantage of viewmodels requiring constructor parameters is that the framework either cannot instantiate them, or it has to pass default values for those parameters, in which case you can easily detect instantiation by the framework. – Mike Nakis Aug 28 '20 at 08:13
  • The XAML designer might also need to be able to instantiate viewmodels, but this designer was never of any usefulness to me, (it just causes problems,) so I don't use it, so I personally do not care about that usage case. – Mike Nakis Aug 28 '20 at 08:13
  • `DataContext="{Binding Source={x:Type BuildAssistantUI.ViewModels.MainViewModel}}"` can we use like this {x:Type}?.. but, it is not working. – Vintage Coder Dec 22 '21 at 08:52

6 Answers6

162

Try this instead.

<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
  • 68,005
  • 14
  • 144
  • 156
  • 3
    I like this option the best. Seems cleaner if the VM is only used for the MainWindow. – Andrew Grothe Jan 29 '13 at 00:36
  • 15
    Is there a way to set the data context using an attribute on the `Window` element, like `DataContext="VM:MainWindowViewModel"`? – Oliver Mar 18 '14 at 15:21
  • This is the proper way! – JavierIEH Jul 26 '15 at 22:13
  • I am not completely understanding totally why one way is better than the other. Also, I do not totally see the difference in either of these ways in comparison to how I have seen some people use "Dynamic Resource". What is this? – Travis Tubbs Jun 05 '17 at 14:32
  • 2
    @Oliver you would have to implement `MarkupExtension`, never done it on VMs, but you could do it with converters to ensure only one instance of converter is present and call it direcly from xaml with `="{converters:SomethingConverter}"`, implying `xmlns:converters` points at converter namespace. `public abstract class BaseValueConverter : MarkupExtension, IValueConverter where T : class, new() { private static T _converter; public override object ProvideValue(IServiceProvider serviceProvider) { return _converter ?? (_converter = new T()); } }` – Whazz May 19 '18 at 22:30
  • Be aware that this mechanism only specifies **the type** of `MainViewModel`, not the instance of `MainViewModel`. This means that by using this construct you are giving WPF license to instantiate your `MainViewModel` by itself, which further requires that your `MainViewModel` must have a parameterless constructor. Furthermore, if you are also instantiating your `MainViewModel` somewhere, (say, in App.xaml.cs,) then there will be two instances of it, and your view will be interacting with only one of them. – Mike Nakis Aug 05 '20 at 13:50
123

In addition to the solution that other people provided (which are good, and correct), there is a way to specify the ViewModel in XAML, yet still separate the specific ViewModel from the View. Separating them is useful for when you want to write isolated test cases.

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

In 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}"
    />
Scott Weaver
  • 7,192
  • 2
  • 31
  • 43
Merlyn Morgan-Graham
  • 58,163
  • 16
  • 128
  • 183
  • Oh wow... thanks. I already marked this as answered, but your addition is much appreciated. Will use it. – Nicholas Jan 04 '11 at 04:10
  • @Nicholas: The other answer is perfect for the question, so I agree with your decision – Merlyn Morgan-Graham Jan 04 '11 at 04:15
  • 9
    Just be aware that this approach uses the same ViewModel instance for every instance of MainWindow. That's fine if the window is single-instance as this case implies, but not if you are showing multiple instances of the window such as in the case of a MDI or tabbed application. – Josh Jan 04 '11 at 04:27
  • 1
    Actually Josh's answer is better as it gives you type-safety on the DataContext. So you can bind directly to the DataContext without worrying about typo-ing some property name/path. – Josh M. Nov 04 '15 at 13:08
  • ` ` This works fine . but, i want like this inline `` – Vintage Coder Dec 22 '21 at 08:57
  • @VintageCoder I think you're trying to ask a new question? If so, go ahead and author a new separate question. I can't answer it though, cause I haven't done any XAML stuff for a decade now. – Merlyn Morgan-Graham Jan 13 '22 at 23:18
12

You need to instantiate the MainViewModel and set it as datacontext. In your statement it just consider it as string value.

     <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
  • 49,102
  • 20
  • 108
  • 119
7

There is also this much better way of specifying the viewmodel:

    using Wpf = System.Windows;

    public partial class App : Wpf.Application //your skeleton app already has this.
    {
        protected override void OnStartup( Wpf.StartupEventArgs e ) //add this.
        {
            base.OnStartup( e );
            MainWindow = new MainView();
            MainWindow.DataContext = new MainViewModel( e.Args );
            MainWindow.Show();
        }
    }

The above mechanism applies to the main viewmodel only. To address a comment below which is asking what about user controls, the mechanism translates as follows for child viewmodels:

    public class ParentViewModel
    {
        public MyChildViewModel ChildViewModel { get; }
    
        public ParentViewModel()
        {
            ChildViewModel = new MyChildViewModel( ... );
        }
    }

ParentView.xaml:

    [...]
        xmlns:local="clr-namespace:the-namespace-of-my-wpf-stuff"
    [...]
        <local:MyChildView DataContext="{Binding ChildViewModel}" />
    [...]

<Rant>All of the solutions previously proposed require viewmodels to have parameterless constructors. Microsoft is under the impression that systems can be built using parameterless constructors. If you are also under that impression, the please, by all means, do go ahead and use some of the other solutions. For those who understand that constructors must have parameters, and therefore the instantiation of objects cannot be left in the hands of magic frameworks, the proper way of specifying the viewmodel is the one I showed above.</Rant>

IAbstract
  • 19,551
  • 15
  • 98
  • 146
Mike Nakis
  • 56,297
  • 11
  • 110
  • 142
3

You might want to try Catel. It allows you to define a DataWindow class (instead of Window), and that class automatically creates the view model for you. This way, you can use the declaration of the ViewModel as you did in your original post, and the view model will still be created and set as DataContext.

See this article for an example.

Geert van Horrik
  • 5,689
  • 1
  • 18
  • 32
0

Setting it in the xaml did not work for me. I reviewed my code over and over and everthing looked fine. What solved my problem was setting the DataContext in the WindowIsLoaded event handler:

PrintDrsAnywayViewModel TheWindowsViewModel = new PrintDrsAnywayViewModel();

private void MyWindowIsLoaded(object sender, RoutedEventArgs e)
{
    DataContext = TheWindowsViewModel;
    TheWindowsViewModel.WindowHeaderMessage = "snot";
}

The xaml, which was the same before and after it worked is this:

<TextBlock Text="{Binding WindowHeaderMessage, Mode=OneWay}"
          Margin="20,20,0,20" FontSize="20" />

WindowHeaderMessage is a string property in the viewmodel file which implements INotifyPropertyChanged

hth

pdschuller
  • 584
  • 6
  • 26