Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

If setting a DataContext within a constructor, does it matter if I set it before or after the call to InitializeComponent()?

Tags:

c#

.net

mvvm

wpf

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()?

like image 319
RLH Avatar asked Feb 21 '17 14:02

RLH


3 Answers

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.

like image 60
mm8 Avatar answered Nov 12 '22 11:11

mm8


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.

like image 33
Liero Avatar answered Nov 12 '22 11:11

Liero


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.

like image 27
grek40 Avatar answered Nov 12 '22 12:11

grek40