在 ItemsControl 中使用 View 作为 DataTemplate 时未设置 DataContext

DataContext not set when using View as DataTemplate in ItemsControl

我有一个 ViewModel 的 ObservableCollection,我想将其绑定到包含关联子视图的 ItemsControl。当我将 ViewModel 添加到我的集合时,会在 ItemsControl 中生成适当数量的子视图。但是,每个生成的视图的 DataContext 都是空的。如果我内联我的子视图,它会正常工作。那么,我需要做什么才能将我的子视图的 DataContext 设置为我的 ViewModels?

这是我父 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);
        }
    }

来自父视图的相关位:

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

子视图:

<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>

子视图模型:

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;
    }
}

显然,我有一些配置错误,但我终究无法弄清楚是什么。

仅供参考,我正在使用 Prism 库

在 ItemsControl 中生成了适当数量的子视图。
因此可以得出结论,DataContext正确设置为您的viewModel,但未应用DataTemplate's

我有同样的问题,我也在使用 Prism 库。我想分享一下如何做到这一点。也许对你有帮助。我用过 CompositeCollection:

ViewModel:

public class MainWindowVM:ViewModelBase
{
    public MainWindowVM()
    {
        LoadData();
    }
    private void LoadData()
    {            
        ObservableCollection<Human> coll = new ObservableCollection<Human>();
        for (int indexLoop = 0; indexLoop < 5; indexLoop++)
        {
            if (indexLoop % 2 == 0)
            {
                coll.Add(new Sportsman() {FirstName=indexLoop.ToString(), LastName=indexLoop.ToString()});
            }
            else
            {
                coll.Add(new Employee() { FirstName = indexLoop.ToString(), LastName = indexLoop.ToString()});
            }                
        }
        CompositeCollection compositeColl = new CompositeCollection();
        compositeColl.Add(new CollectionContainer() { Collection = coll });
        FooData = compositeColl;
    }

    private CompositeCollection fooData;

    public CompositeCollection FooData
    {
        get { return fooData; }
        set
        {
            fooData = value;
        }
    }

}

型号:

public class Human
{        
    private string firstName;
    public string FirstName
    {
        get { return firstName; }
        set { firstName = value; }
    }

    private string lastName;
    public string LastName
    {
        get { return lastName; }
        set { lastName = value; }
    }
}

public class Sportsman:Human
{    }

public class Employee:Human
{    }

查看:

<Window x:Class="ItemsControlWithDataTemplates.MainWindow"
    <!--The code is omitted for the brevity-->
    Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <vm:MainWindowVM/>
    </Window.DataContext>
    <Grid>
        <ItemsControl ItemsSource="{Binding FooData}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <VirtualizingStackPanel/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.Resources>
                <DataTemplate DataType="{x:Type model:Employee}">
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding FirstName}"/>
                        <TextBlock Text=" ( Employee"/>
                        <TextBlock Text="{Binding LastName}"/>
                        <TextBlock Text=")"/>
                    </StackPanel>
                </DataTemplate>
                <DataTemplate DataType="{x:Type model:Sportsman}">
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding FirstName}"/>
                        <TextBlock Text=" - Sportsman "/>
                        <TextBlock Text="{Binding LastName}"/>
                    </StackPanel>
                </DataTemplate>
                <DataTemplate DataType="{x:Type model:Human}">
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding FirstName}"/>
                        <TextBlock Text=" - "/>
                        <TextBlock Text="{Binding LastName}"/>
                    </StackPanel>
                </DataTemplate>
            </ItemsControl.Resources>
        </ItemsControl>    
    </Grid>
</Window>

实际上我在 Prism 应用程序中按以下方式设置 DataContext

public MyUserControl(IMyViewModel viewModel)
{
    InitializeComponent();
    DataContext = viewModel;
}

WPF 自动将 ItemsControl 中项目容器元素的 DataContext 设置为适当的项目实例,以便它可以继承到 ItemTemplate 中。显然,当您设置 prism:ViewModelLocator.AutoWireViewModel 属性.

时,此机制被禁用

所以,只需将它从您的 ChildView 中删除 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>

作为一般规则,UserControl 永远不应显式设置自己的 DataContext,既不直接也不通过 AutoWireViewModel 之类的机制,因为这有效地阻止了从其继承 DataContext家长控制。