That is one of the many reasons you should never set the DataContext
directly from the UserControl
itself.
When you do so, you can no longer use any other DataContext
with it because the UserControl’s DataContext
is hardcoded to an instance that only the UserControl
has access to, which kind of defeats one of WPF’s biggest advantages of having separate UI and data layers.
There are two main ways of using UserControls
in WPF
-
A standalone
UserControl
that can be used anywhere without a specificDataContext
being required.This type of
UserControl
normally exposesDependencyProperties
for any values it needs, and would be used like this:<v:InkStringView TextInControl="{Binding SomeValue}" />
Typical examples I can think of would be anything generic such as a Calendar control or Popup control.
-
A
UserControl
that is meant to be used with a specificModel
orViewModel
only.These
UserControls
are far more common for me, and is probably what you are looking for in your case. An example of how I would use such a UserControl would be this:<v:InkStringView DataContext="{Binding MyInkStringViewModelProperty}" />
Or more frequently, it would be used with an implicit
DataTemplate
. An implicitDataTemplate
is aDataTemplate
with aDataType
and noKey
, and WPF will automatically use this template anytime it wants to render an object of the specified type.<Window.Resources> <DataTemplate DataType="{x:Type m:InkStringViewModel}"> <v:InkStringView /> </DataTemplate> <Window.Resources> <!-- Binding to a single ViewModel --> <ContentPresenter Content="{Binding MyInkStringViewModelProperty}" /> <!-- Binding to a collection of ViewModels --> <ItemsControl ItemsSource="{Binding MyCollectionOfInkStringViewModels}" />
No
ContentPresenter.ItemTemplate
orItemsControl.ItemTemplate
is needed when using this method.
Don’t mix these two methods up, it doesn’t go well 🙂
But anyways, to explain your specific problem in a bit more detail
When you create your UserControl like this
<v:InkStringView TextInControl="{Binding text}" />
you are basically saying
var vw = new InkStringView()
vw.TextInControl = vw.DataContext.text;
vw.DataContext
is not specified anywhere in the XAML, so it gets inherited from the parent item, which results in
vw.DataContext = Strings[x];
so your binding that sets TextInControl = vw.DataContext.text
is valid and resolves just fine at runtime.
However when you run this in your UserControl constructor
this.DataContext = new InkStringViewModel();
the DataContext
is set to a value, so no longer gets automatically inherited from the parent.
So now the code that gets run looks like this:
var vw = new InkStringView()
vw.DataContext = new InkStringViewModel();
vw.TextInControl = vw.DataContext.text;
and naturally, InkStringViewModel
does not have a property called text
, so the binding fails at runtime.