12

I have a WPF window that takes a few parameters in it's constructor. I then use these constructors to setup the state of the window. Part of that constructor process is instantiating my view model class that is then set as the windows DataContext.

My question is when should I set my DataContext equal to my view model object-- before or after the call to InitializeComponent()?

I ask because if I set it beforehand, I then need to manually launch code that is executed after the window has been initialized, because certain events should fire when the DataContext is assigned, or re-assigned.

It is my assumption that there shouldn't be any binding issues if I set the DataContext after the call to InitializeComponent() but I wanted to ask for advice on the matter before making the final call to wire up my window this way. Could I be missing something the could come back to haunt me if I set my DataContext after the call to InitializeComponent()?

RLH
  • 15,230
  • 22
  • 98
  • 182
  • 2
    You answered your question, setting `DataContext` prior calling `InitializeComponents()` (which task is to load/parse/process xaml) will require refreshing (e.g. setting it to `null` and then back), so it has to be set after. – Sinatr Feb 21 '17 at 14:07
  • I always set DataContext after InitializeComponent, never had any problems that were strictly related to that fact. – Arie Feb 21 '17 at 14:10
  • My advice is to handle it differently: Do not set the window datacontext from within the window. Instead, set the datacontext on a child element within the window (eg. Grid) and do it in the Window.Loaded event, not in constructor. However, if I remember correctly this is more relevant to UserControl than to Window, and I just adapted the approach generally as a habit. – grek40 Feb 21 '17 at 14:14
  • @grek40 I understand what you are explaining, but please expound why that's the (likely) right approach. In fact, briefly address my question but explain your suggestion in an answer. If I see the merits, I'll give you the answer mark. Thanks. – RLH Feb 21 '17 at 14:15
  • Maybe I can write an answer later, not enough time now and need to re-check a few nitty details for the answer to be of any worth. – grek40 Feb 21 '17 at 14:19
  • @grek40 Ok, thanks. I'm not sure if this question will get much attention anyway. – RLH Feb 21 '17 at 14:22

3 Answers3

4

My question is when should I set my DataContext equal to my view model object-- before or after the call to InitializeComponent()?

It shouldn't matter unless you rely on some bindings that are established during the call to the InitializeComponent(), like ElementName bindings:

Cannot bind ItemsSource to ElementName

The InitializeComponent() method itself locates a URI to the compiled XAML file and passes it to a LoadComponent() method that parses the BAML, i.e. the compiled XAML, and creates instances of the elements that you have defined in your XAML markup:

What is the connection between .xaml and .xaml.cs files

Simply setting the DataContext property of the window to an instance of a view model class that elements in the view bind to might as well be done after the call to the InitializeComponent() method. These bindings aren't resolved before the constructor returns anyway.

Community
  • 1
  • 1
mm8
  • 163,881
  • 10
  • 57
  • 88
  • 1
    I've just made test where I set `DataContext` before `InitializeComponents`, and my Bindings were resolved before `InitializeComponents` returned – Liero Feb 22 '17 at 08:35
3

Here's my addition to @mm8's answer:

  1. Usually it does not matter, but set DataContext after InitializeComponents. When DataContextChanged event is called, you naturally expect, that components are already initialized.

    Also it's good to know whether the components can be initialized without DataContext and separate possible initialization issues from binding issues. If you set DataContext before InitializeComponents, the binding issues may result in an exception in InitializeComponents.

  2. Make you ViewModel constructor very fast. Don't do any DB calls or any I/O calls, etc. You want to display the UI as soon as possible.

  3. Make sure your ViewModel constructor never throws an exception. Parameter validations is OK, but just for debugging purposes. It should never happen in production.

  4. If you need to load data into viewmodel, create separate async method called e.g. Activate(), which you will call from View's Loaded or OnNavigatedTo event.

    Additionally, if you subscribe to some events in ViewModel, you should unsubscribe. Ideal place for subscription is Activate method, resp Deactivate to unsubscribe. If you subscribe in ViewModel's ctor, it may happen that Activate/Deactivate will never be called and you introduced memory leak.

  5. If you feel your bindings are slowing the UI, try to use {Binding IsAsync=True}, resp x:Bind, or try to use codebehind to set the properties in worst case.

Liero
  • 25,216
  • 29
  • 151
  • 297
1

Different from what you ask, I suggest two changes:

  1. Set the DataContext of an inner element and not on the Window / UserControl itself.
  2. Set the DataContext on Loaded instead of the constructor.

These points are more obvious when looking at a UserControl, which will probably be embedded at multiple points, but remember that a Window can be created by explicit startup code instead of some App.StartupUri.

Regarding the first point, consider the OOP design basics. Forget about WPF / XAML specifics and remember that you derive from a Window class and create a subclass of it. The contract of this class includes a public get/set property named DataContext which accepts any kind of object. So you should at least think about, how bad you will screw up, if someone is replacing your DataContext from the outside. When you instead set the DataContext on the next-inner FrameworkElement inside the window, it is hosted in an environment that is owned by the window.

Setting the DataContext on Loaded is working for me, while I ran into problems with constructor time setting. However, I can't actually recall the details of it, maybe it was related to the visual designer (that I'm not using anymore). For other controls it is easier to explain: constructor time initialization sucks when hosted in a virtualizing panel, also property initializers (new MyControl { Prop = Value }, XAML property assignments, ...) are not handled by time the constructor runs, so objects tend to be in a different state than how they are presented later.

grek40
  • 13,113
  • 1
  • 24
  • 50
  • If an exception occurs in usercontrols ctor, then you will get something like XamlInitializationExeption, which may be confusing. Other issue you may have experienced is slowing down ui, if the viemodel ctor takes too long. But I dont recommend setting viewmodel in loaded event. – Liero Feb 21 '17 at 18:49
  • @Liero most of the time I'm binding the viewmodel from an external source anyway, so I don't have to worry about timing and changes, but in case you want to establish an internal datacontext, where / how would you do that and why is the loaded event not a good place? – grek40 Feb 22 '17 at 07:06
  • You should not set DataContext in Loaded EventHandler, because at that point, the DataContext is already inherited from the parent. So Imagine you have `WindowViewModel` and `UserControlViewModel`. If you set datacontext in `UserControl.Loaded` event, you are changing it from `WindowViewModel` to `UserControlViewModel` – Liero Feb 22 '17 at 08:26