简体   繁体   English

WPF自定义控件拾取父窗口的DataContext

[英]WPF Custom Control Picking Up Parent Window's DataContext

I have a ViewModel 我有一个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");
        }
    }

}

A control called ProjectorDisplayControl 名为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;
        }
    }
}

That control is used in two places. 该控件在两个地方使用。

First place is in a ListBox where it works great: 第一名是在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>

Second place is on top of a form where I set the form's DataContext with a ViewModel object. 第二名是在窗体顶部,在该窗体中,我使用ViewModel对象设置了窗体的DataContext。 To make the ProjectorDisplayControl consume the CurrentSelectedProjectionDataElement in the ViewModel I would expect to have to do this: 为了使ProjectorDisplayControl在ViewModel中使用CurrentSelectedProjectionDataElement,我希望必须这样做:

<Window x:Class="Fast_Project.DisplayWindow"
    ................>
<Viewbox>
    <StackPanel>
        <controls:ProjectorDisplayControl x:Name="_projectorDisplay" ProjectedData="{Binding CurrentSelectedProjectionDataElement}"/>
    </StackPanel>
</Viewbox>

That code gives me two binding errors: 该代码给了我两个绑定错误:

System.Windows.Data Error: 40 : BindingExpression path error: 'TextBody' property not found on 'object' ''ViewModel' (HashCode=2512406)'. System.Windows.Data错误:40:BindingExpression路径错误:在“对象”“ ViewModel”(HashCode = 2512406)“上找不到“ TextBody”属性。 BindingExpression:Path=TextBody; BindingExpression:Path = TextBody; DataItem='ViewModel' (HashCode=2512406); DataItem ='ViewModel'(HashCode = 2512406); target element is 'TextBlock' (Name=''); 目标元素是'TextBlock'(Name =''); target property is 'Text' (type 'String') 目标属性为“文本”(类型为“字符串”)

System.Windows.Data Error: 40 : BindingExpression path error: 'CurrentSelectedProjectionDataElement' property not found on 'object' ''ProjectionDataElement' (HashCode=37561097)'. System.Windows.Data错误:40:BindingExpression路径错误:在“对象”“ ProjectionDataElement”(HashCode = 37561097)上找不到“ CurrentSelectedProjectionDataElement”属性。 BindingExpression:Path=CurrentSelectedProjectionDataElement; BindingExpression:Path = CurrentSelectedProjectionDataElement; DataItem='ProjectionDataElement' (HashCode=37561097); DataItem ='ProjectionDataElement'(HashCode = 37561097); target element is 'ProjectorDisplayControl' (Name='_projectorDisplay'); 目标元素是'ProjectorDisplayControl'(Name ='_ projectorDisplay'); target property is 'ProjectedData' (type 'ProjectionDataElement') 目标属性为“ ProjectedData”(类型为“ ProjectionDataElement”)

When I watch the setter of the private property _projectedData on ProjectorDisplayControl which sets the DataContext I first see it get set with a valid value and then set to null. 当我观看ProjectorDisplayControl上设置DataContext的私有属性_projectedData的设置器时,我首先看到它被设置为有效值,然后设置为null。

In the DisplayWindow that holds the single ProjectorDisplayControl I can remove the binding to the CurrentSelectedProjectionDataElement and then I only get the first binding error message: 在包含单个ProjectorDisplayControl的DisplayWindow中,我可以删除与CurrentSelectedProjectionDataElement的绑定,然后仅收到第一条绑定错误消息:

<Viewbox>
    <StackPanel>
        <controls:ProjectorDisplayControl x:Name="_projectorDisplay" />
    </StackPanel>
</Viewbox>

The first binding error makes me feel like the ProjectorDisplayControl's DataContext is getting set with a ViewModel object when the DisplayWindow's DataContext is getting set. 第一个绑定错误使我感到在设置DisplayWindow的DataContext时,已使用ViewModel对象设置了ProjectorDisplayControl的DataContext。 But from what I've read controls don't share the same data context with their parent window unless you set it so. 但是从我所读的内容来看,控件不会与其父窗口共享相同的数据上下文,除非您进行了设置。

I've treating the binding path for the ProjectorDisplayControl.ProjectedData in DisplayWindow like it was a ProjectionDataElement object as the second error message states. 我已经将DisplayWindow中ProjectorDisplayControl.ProjectedData的绑定路径视为第二个错误消息状态,就像它是一个ProjectionDataElement对象一样。

<Viewbox>
    <StackPanel>
        <controls:ProjectorDisplayControl x:Name="_projectorDisplay" ProjectedData="{Binding }"/>
    </StackPanel>
</Viewbox>

Then is tells me: 然后就是告诉我:

Cannot create default converter to perform 'one-way' conversions between types 'Fast_Project.ViewModel' and 'Fast_Project.ProjectionDataElement' 无法创建默认转换器以在类型“ Fast_Project.ViewModel”和“ Fast_Project.ProjectionDataElement”之间执行“单向”转换

Like it really was the ViewModel object like I thought it was in the first place... 就像我最初认为的那样,确实是ViewModel对象。

Anyways I suspect that the root of my problem lies in the how I see the ProjectorDisplayControl having a DataContext set to the ViewModel object. 无论如何,我怀疑问题的根源在于如何查看ProjectorDisplayControl并将DataContext设置为ViewModel对象。 Anyone see where I messed up? 有人看到我搞砸了吗?

Thank you all for your help! 谢谢大家的帮助!


The solution was: 解决方案是:


ProjectorDisplayControl 投影机DisplayControl

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

Child control inherits the Dependency property value (or DataContext in this case) from their Parents. 子控件从其父级继承Dependency属性值(在本例中为DataContext)。 When you are using the UserControl inside the itemTempalte, then the DataContext of each item is already ProjectionDataElement and hence the DataContext of your control is set to ProjectionDataElement. 当您在itemTempalte内部使用UserControl时,每个项目的DataContext已经是ProjectionDataElement,因此控件的DataContext被设置为ProjectionDataElement。

When you are using the control inside parent it will inherit its DataContext. 当您在父级内部使用控件时,它将继承其DataContext。

The problem is that you are setting your ProjectedData property on control and not using it inside it. 问题是您要在控件上设置ProjectedData属性,而不在控件内部使用它。 If you want that each of your control should be bind to the value set on ProjectedData then you should update the bindings like: 如果希望将每个控件都绑定到ProjectedData上设置的值,则应更新绑定,例如:

<UserControl x:Class="Fast_Project.ProjectorDisplayControl"
         ..................>
<Viewbox>
    <StackPanel>
        <TextBlock Text="{Binding Path=ProjectedData.TextBody, RelativeSource={RelativeSource Self}"/>
    </StackPanel>

For you second error, you must be setting the DataContext of that window to ProjectionDataElement somewhere that is why is searching it inside it. 对于第二个错误,您必须将该窗口的DataContext设置为ProjectionDataElement,这就是为什么要在其中搜索它的原因。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM