[英]WPF Custom Control Picking Up Parent Window's DataContext
我有一个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路径错误:在“对象”“ ViewModel”(HashCode = 2512406)“上找不到“ TextBody”属性。 BindingExpression:Path = TextBody; DataItem ='ViewModel'(HashCode = 2512406); 目标元素是'TextBlock'(Name =''); 目标属性为“文本”(类型为“字符串”)
System.Windows.Data错误:40:BindingExpression路径错误:在“对象”“ ProjectionDataElement”(HashCode = 37561097)上找不到“ CurrentSelectedProjectionDataElement”属性。 BindingExpression:Path = CurrentSelectedProjectionDataElement; DataItem ='ProjectionDataElement'(HashCode = 37561097); 目标元素是'ProjectorDisplayControl'(Name ='_ projectorDisplay'); 目标属性为“ ProjectedData”(类型为“ ProjectionDataElement”)
当我观看ProjectorDisplayControl上设置DataContext的私有属性_projectedData的设置器时,我首先看到它被设置为有效值,然后设置为null。
在包含单个ProjectorDisplayControl的DisplayWindow中,我可以删除与CurrentSelectedProjectionDataElement的绑定,然后仅收到第一条绑定错误消息:
<Viewbox>
<StackPanel>
<controls:ProjectorDisplayControl x:Name="_projectorDisplay" />
</StackPanel>
</Viewbox>
第一个绑定错误使我感到在设置DisplayWindow的DataContext时,已使用ViewModel对象设置了ProjectorDisplayControl的DataContext。 但是从我所读的内容来看,控件不会与其父窗口共享相同的数据上下文,除非您进行了设置。
我已经将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对象。 有人看到我搞砸了吗?
谢谢大家的帮助!
解决方案是:
投影机DisplayControl
<StackPanel>
<TextBlock Text="{Binding Path=ProjectedData.TextBody, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type controls:ProjectorDisplayControl}}}"/>
</StackPanel>
展示窗
<StackPanel>
<controls:ProjectorDisplayControl x:Name="_projectorDisplay" ProjectedData="{Binding Path=ViewModel.CurrentSelectedProjectionDataElement,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type controls:DisplayWindow}}}"/>
</StackPanel>
子控件从其父级继承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,这就是为什么要在其中搜索它的原因。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.