Search code examples
c#wpfmvvmdatacontextitemscontrol

DataContext not set when using View as DataTemplate in ItemsControl


I have an ObservableCollection of ViewModels that I'd like to bind to an ItemsControl containing the associated child Views. When I add ViewModels to my collection, an appropriate number of child Views are generated in the ItemsControl. However, the DataContext for each of the generated views is null. If I inline my child view, it works correctly. So, what do I need to do to set the DataContext for my child views to my ViewModels?

Here's the relavent bits in my parent ViewModel:

    public ObservableCollection<ChildViewModel> Numbers { get; set; }

    public ParentViewModel()
    {
        Numbers = new ObservableCollection<ChildViewModel>();
    }

    private void ShowNumbers()
    {
        foreach (var num in Enumerable.Range(0, number))
        {
            var childView = new ChildViewModel(number.ToString());
            Numbers.Add(childView);
        }
    }

Relevant bit from the Parent View:

        <ItemsControl ItemsSource="{Binding Numbers, UpdateSourceTrigger=PropertyChanged}">
            <ItemsControl.ItemTemplate>
                <DataTemplate DataType="{x:Type vm:ChildViewModel}">
                    <v:ChildView />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

The Child View:

<UserControl x:Class="TestWpfApp.Views.ChildView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:prism="http://prismlibrary.com/"             
         prism:ViewModelLocator.AutoWireViewModel="True">
<Grid>
    <Label Content="{Binding NumberString}" Width="30" Height="30" BorderThickness="1" BorderBrush="Black" HorizontalAlignment="Center"/>
</Grid>
</UserControl>

The Child ViewModel:

public class ChildViewModel : BindableBase
{
    private string numberString;
    public string NumberString
    {
        get
        {
            return numberString;
        }
        set
        {
            SetProperty(ref numberString, value);
        }
    }

    public ChildViewModel() { }

    public ChildViewModel(string number)
    {
        NumberString = number;
    }
}

Obviously, I have something misconfigured, but I can't for the life of me figure out what.

FYI I am using the Prism Library


Solution

  • WPF automatically sets the DataContext of an item container element in an ItemsControl to the appropriate item instance, so that it can be inherited into the ItemTemplate. Apparently this mechanism is disabled when you set the prism:ViewModelLocator.AutoWireViewModel property.

    So, just remove it from your ChildView's XAML:

    <UserControl x:Class="TestWpfApp.Views.ChildView"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:prism="http://prismlibrary.com/">
        <Grid>
            <Label Content="{Binding NumberString}" Width="30" Height="30"
                   BorderThickness="1" BorderBrush="Black" HorizontalAlignment="Center" />
        </Grid>
    </UserControl>
    

    As a general rule, a UserControl should never explicitly set its own DataContext, neither directly nor by a mechanism like AutoWireViewModel, because that effectively prevents inheriting a DataContext from its parent control.