WPF 自定义控件选取父窗口的数据上下文
本文关键字:数据 上下文 窗口 自定义控件 选取 WPF | 更新日期: 2023-09-27 18:32:53
我有一个视图模型
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 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>
该代码给了我两个绑定错误:
系统.视窗.数据错误: 40 : 绑定表达式路径错误: 在"对象"视图模型"(HashCode=2512406("上找不到"TextBody"属性。 BindingExpression:Path=TextBody; DataItem='ViewModel' (HashCode=2512406(; 目标元素是"文本块"(名称="(; 目标属性为"文本"(类型"字符串"(
系统.视窗.数据错误: 40 : 绑定表达式路径错误: 在"对象"投影数据元素"(HashCode=37561097("上找不到"当前选定投影数据元素"属性。 BindingExpression:Path=CurrentSelectedProjectionDataElement; DataItem='ProjectionDataElement' (HashCode=37561097(; 目标元素是"投影或显示控件"(名称="_projectorDisplay"(; 目标属性是"投影数据"(类型"投影数据元素"(
当我在 ProjectorDisplayControl 上观看私有属性_projectedData设置数据上下文的设置器时,我首先看到它被设置为有效值,然后设置为 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对象。有人看到我在哪里搞砸了吗?
谢谢大家的帮助!
解决方案是:
投影仪显示控制
<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>
子控件从其父控件继承依赖项属性值(在本例中为 DataContext(。当您在 itemTempalte 中使用 UserControl 时,每个项的 DataContext 已经是 ProjectionDataElement,因此控件的 DataContext 设置为 ProjectionDataElement。
当您使用父级中的控件时,它将继承其数据上下文。
问题是您在控件上设置 ProjectedData 属性,而不是在其中使用它。如果希望每个控件都应绑定到 ProjectedData 上设置的值,则应更新绑定,如下所示:
<UserControl x:Class="Fast_Project.ProjectorDisplayControl"
..................>
<Viewbox>
<StackPanel>
<TextBlock Text="{Binding Path=ProjectedData.TextBody, RelativeSource={RelativeSource Self}"/>
</StackPanel>
对于第二个错误,您必须将该窗口的DataContext设置为投影数据元素,这就是在其中搜索它的原因。