【问题标题】:DataContext not set when using View as DataTemplate in ItemsControl在 ItemsControl 中使用 View as DataTemplate 时未设置 DataContext
【发布时间】:2016-09-24 00:05:34
【问题描述】:

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

这是我的父 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 库

【问题讨论】:

  • 我猜你不应该设置prism:ViewModelLocator.AutoWireViewModel,因为 WPF 已经负责设置 ItemsControl 中项目容器的属性 DataContext。另请参阅:stackoverflow.com/q/33043978/1136211
  • DataContext 是否设置为您的ViewModel?或者只是DataTemplate's 不能应用?只需创建一些string 属性并绑定你的View 来测试你的DataContext 是否已设置。
  • 好吧@Clemens,你说得对。删除那行代码使一切正常。如果您将其作为答案提交,我希望将其设为可接受的解决方案。

标签: c# wpf mvvm datacontext itemscontrol


【解决方案1】:

在 ItemsControl 中生成适当数量的子视图。
所以可以断定DataContext被正确设置为你的viewModel,但是DataTemplate's没有被应用。

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

视图模型:

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

【讨论】:

    【解决方案2】:

    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

    【讨论】:

      猜你喜欢
      • 2016-02-06
      • 2014-07-30
      • 2010-11-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-08
      • 1970-01-01
      相关资源
      最近更新 更多