在 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
家长控制。
我有一个 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
家长控制。