How to correctly bind to a dependency property of a usercontrol in a MVVM framework

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

  1. A standalone UserControl that can be used anywhere without a specific DataContext being required.

    This type of UserControl normally exposes DependencyProperties 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.

  2. A UserControl that is meant to be used with a specific Model or ViewModel 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 implicit DataTemplate is a DataTemplate with a DataType and no Key, 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 or ItemsControl.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.


Seems like you are mixing the model of the parent view with the model of the UC.

Here is a sample that matches your code:

The MainViewModel:

using System.Collections.Generic;

namespace UCItemsControl
{
    public class MyString
    {
        public string text { get; set; }
    }

    public class MainViewModel
    {
        public ObservableCollection<MyString> Strings { get; set; }

        public MainViewModel()
        {
            Strings = new ObservableCollection<MyString>
            {
                new MyString{ text = "First" },
                new MyString{ text = "Second" },
                new MyString{ text = "Third" }
            };
        }
    }
}

The MainWindow that uses it:

<Window x:Class="UCItemsControl.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:v="clr-namespace:UCItemsControl"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <v:MainViewModel></v:MainViewModel>
    </Window.DataContext>
    <Grid>
        <ItemsControl 
                ItemsSource="{Binding Strings}" x:Name="self" >

            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel HorizontalAlignment="Left" VerticalAlignment="Top" Orientation="Vertical" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>


            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <DataTemplate.Resources>
                        <Style TargetType="v:InkStringView">
                            <Setter Property="FontSize" Value="25"/>
                            <Setter Property="HorizontalAlignment" Value="Left"/>
                        </Style>
                    </DataTemplate.Resources>

                    <v:InkStringView TextInControl="{Binding text}"  />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>
</Window>

Your UC (no set of DataContext):

public partial class InkStringView : UserControl
{
    public InkStringView()
    {
        InitializeComponent();
    }

    public String TextInControl
    {
        get { return (String)GetValue(TextInControlProperty); }
        set { SetValue(TextInControlProperty, value); }
    }

    public static readonly DependencyProperty TextInControlProperty =
        DependencyProperty.Register("TextInControl", typeof(String), typeof(InkStringView));
}

(Your XAML is OK)

With that I can obtain what I guess is the expected result, a list of values:

First
I am row 1
Second
I am row 1
Third
I am row 1

You're almost there. The problem is that you're creating a ViewModel for your UserControl. This is a smell.

UserControls should look and behave just like any other control, as viewed from the outside. You correctly have exposed properties on the control, and are binding inner controls to these properties. That's all correct.

Where you fail is trying to create a ViewModel for everything. So ditch that stupid InkStringViewModel and let whoever is using the control to bind their view model to it.

If you are tempted to ask "what about the logic in the view model? If I get rid of it I'll have to put code in the codebehind!" I answer, "is it business logic? That shouldn't be embedded in your UserControl anyhow. And MVVM != no codebehind. Use codebehind for your UI logic. It's where it belongs."