我有一个ViewModel
public class ViewModel:ViewModelObject
{
public ViewModel()
{
ProjectionDataElementList = new ObservableCollection<ProjectionDataElement>();
}
public ObservableCollection<ProjectionDataElement> ProjectionDataElementList { get; set; }
private ProjectionDataElement _currentSelectedProjectionDataElement;
public ProjectionDataElement CurrentSelectedProjectionDataElement
{
get
{
return _currentSelectedProjectionDataElement;
}
set
{
_currentSelectedProjectionDataElement = value;
OnPropertyChanged("CurrentSelectedProjectionDataElement");
}
}
}
名为ProjectorDisplayControl的控件
<UserControl x:Class="Fast_Project.ProjectorDisplayControl"
..................>
<Viewbox>
<StackPanel>
<TextBlock Text="{Binding Path=TextBody}"/>
</StackPanel>
</Viewbox>
public partial class ProjectorDisplayControl : UserControl
{
public ProjectorDisplayControl()
{
InitializeComponent();
}
public static readonly DependencyProperty ProjectedDataProperty = DependencyProperty.Register("ProjectedData", typeof(ProjectionDataElement), typeof(ProjectorDisplayControl),
new PropertyMetadata(new PropertyChangedCallback((objectInstance, arguments) =>
{
ProjectorDisplayControl projectorDisplayControl = (ProjectorDisplayControl)objectInstance;
projectorDisplayControl._projectedData = (ProjectionDataElement)arguments.NewValue;
})));
public ProjectionDataElement ProjectedData
{
get
{
return (ProjectionDataElement)GetValue(ProjectedDataProperty);
}
set
{
SetValue(ProjectedDataProperty, value);
}
}
private ProjectionDataElement _projectedData
{
get
{
return this.DataContext as ProjectionDataElement;
}
set
{
this.DataContext = value;
}
}
}
该控件在两个地方使用。
第一个位置是ListBox,它可以很好地运行:
<ListBox Grid.Column="0" ItemsSource="{Binding Path=ProjectionDataElementList}" Name="projectedDataListBox"
SelectedItem="{Binding Path=CurrentSelectedProjectionDataElement, UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Border BorderThickness="1" BorderBrush="Black">
<controls:ProjectorDisplayControl x:Name="_projectorDisplay" ProjectedData="{Binding}"/>
</Border>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
第二个位置在表单的顶部,我使用ViewModel对象设置表单的DataContext。要使ProjectorDisplayControl使用ViewModel中的CurrentSelectedProjectionDataElement,我希望必须这样做:
<Window x:Class="Fast_Project.DisplayWindow"
................>
<Viewbox>
<StackPanel>
<controls:ProjectorDisplayControl x:Name="_projectorDisplay" ProjectedData="{Binding CurrentSelectedProjectionDataElement}"/>
</StackPanel>
</Viewbox>
该代码给了我两个绑定错误:
System.Windows.Data错误:40: BindingExpression路径错误: 在'object'''ViewModel'(HashCode = 2512406)'上找不到'TextBody'属性。 BindingExpression:路径=的TextBody; DataItem ='ViewModel'(HashCode = 2512406); target元素是'TextBlock'(Name =''); target属性是'Text'(类型'String')
System.Windows.Data错误:40: BindingExpression路径错误: 在'object'''ProjectionDataElement'(HashCode = 37561097)'上找不到'CurrentSelectedProjectionDataElement'属性。 BindingExpression:路径= CurrentSelectedProjectionDataElement; DataItem ='ProjectionDataElement'(HashCode = 37561097); target元素是'ProjectorDisplayControl'(Name ='_ projectorDisplay'); target属性是'ProjectedData'(类型'ProjectionDataElement')
当我在ProjectorDisplayControl上查看设置DataContext的私有属性_projectedData的setter时,我首先看到它被设置为有效值,然后设置为null。
在包含单个ProjectorDisplayControl的DisplayWindow中,我可以删除对CurrentSelectedProjectionDataElement的绑定,然后我只得到第一个绑定错误消息:
<Viewbox>
<StackPanel>
<controls:ProjectorDisplayControl x:Name="_projectorDisplay" />
</StackPanel>
</Viewbox>
第一个绑定错误让我觉得当DisplayWindow的DataContext被设置时,ProjectorDisplayControl的DataContext将被设置为ViewModel对象。但是从我读过的内容中,控件不会与父窗口共享相同的数据上下文,除非你设置它。
我在DisplayWindow中处理ProjectorDisplayControl.ProjectedData的绑定路径,就像它是ProjectionDataElement对象一样,第二条错误消息说明了。
<Viewbox>
<StackPanel>
<controls:ProjectorDisplayControl x:Name="_projectorDisplay" ProjectedData="{Binding }"/>
</StackPanel>
</Viewbox>
然后告诉我:
无法创建默认转换器以在类型'Fast_Project.ViewModel'和'Fast_Project.ProjectionDataElement'之间执行“单向”转换
就像它真的是ViewModel对象一样,我认为它首先是......
无论如何我怀疑我的问题的根源在于我如何看到ProjectorDisplayControl将DataContext设置为ViewModel对象。有人看到我搞砸了吗?
谢谢大家的帮助!
解决方案是:
ProjectorDisplayControl
<StackPanel>
<TextBlock Text="{Binding Path=ProjectedData.TextBody, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type controls:ProjectorDisplayControl}}}"/>
</StackPanel>
DisplayWindow
<StackPanel>
<controls:ProjectorDisplayControl x:Name="_projectorDisplay" ProjectedData="{Binding Path=ViewModel.CurrentSelectedProjectionDataElement,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type controls:DisplayWindow}}}"/>
</StackPanel>
答案 0 :(得分:0)
子控件从其父级继承Dependency属性值(在本例中为DataContext)。 当您在itemTempalte中使用UserControl时,每个项目的DataContext已经是ProjectionDataElement,因此您的控件的DataContext设置为ProjectionDataElement。
当您在父级内部使用控件时,它将继承其DataContext。
问题是您在控件上设置ProjectedData属性而不在其中使用它。如果您希望每个控件都绑定到ProjectedData上设置的值,那么您应该更新绑定,如:
<UserControl x:Class="Fast_Project.ProjectorDisplayControl"
..................>
<Viewbox>
<StackPanel>
<TextBlock Text="{Binding Path=ProjectedData.TextBody, RelativeSource={RelativeSource Self}"/>
</StackPanel>
对于第二个错误,您必须将该窗口的DataContext设置为ProjectionDataElement,这就是为什么要在其中搜索它。