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

时间:2013-09-26 01:21:30

标签: c# wpf xaml

我有一个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>

1 个答案:

答案 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,这就是为什么要在其中搜索它。