2013-05-10 34 views
5

在我的主窗口中,我尝试绑定到一个布尔值。但它正在查看我的自定义控件的数据上下文。如果我没有在用户控件中分配DataContext,那么主窗口的绑定工作,但(显然)这会在用户控件中制动绑定。这里的错误:我的WPF自定义控件的数据上下文并取代父母的

System.Windows.Data Error: 40 : BindingExpression path error: 'MyControlVisible' property not found on 'object' ''MyUserControlModel' (HashCode=1453241)'. BindingExpression:Path=MyControlVisible; DataItem='MyUserControlModel' (HashCode=1453241); target element is 'MyUserControl' (Name='_myUserControl'); target property is 'Visibility' (type 'Visibility') 

我需要结合双方的控制工作,但我不希望用户控件的DataContext,以取代窗口的。

下面的代码:

<Window x:Class="Sandbox.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:Controls="clr-namespace:Sandbox.Controls" Title="Sandbox"> 
    <DockPanel LastChildFill="True"> 
     <DockPanel.Resources> 
      <BooleanToVisibilityConverter x:Key="boolToVis" /> 
     </DockPanel.Resources> 
     <Grid> 
      <Controls:MyUserControl x:Name="_myUserControl" Visibility="{Binding MyControlVisible, Converter={StaticResource boolToVis}}"/> 
     </Grid> 
    </DockPanel> 
</Window> 

namespace Sandbox 
{ 
    public partial class MainWindow 
    { 
     private MainWindowModel model; 
     public MainWindow() 
     { 
      InitializeComponent(); 
      DataContext = model = new MainWindowModel(); 
      _myUserControl.Initialize(model.MyUControlModel); 
     } 
    } 
} 

using System.ComponentModel; 
using Sandbox.Controls; 

namespace Sandbox 
{ 
    public class MainWindowModel : BaseModel 
    { 
     public MyUserControlModel MyUControlModel { get; set; } 
     public bool MyControlVisible { get; set; } 
     public MainWindowModel() 
     { 
      MyUControlModel = new MyUserControlModel(); 
      MyControlVisible = false; 
      OnChange(""); 
     } 
    } 

    public class BaseModel : INotifyPropertyChanged 
    { 
     public event PropertyChangedEventHandler PropertyChanged; 
     protected void OnChange(string s) 
     { 
      var handler = PropertyChanged; 
      if (handler != null) 
      { 
       handler(this, new PropertyChangedEventArgs(s)); 
      } 
     } 
    } 
} 

<UserControl x:Class="Sandbox.Controls.MyUserControl" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      mc:Ignorable="d"> 
    <Grid> 
     <TextBlock Text="{Binding MyBoundText}"/> 
    </Grid> 
</UserControl> 

namespace Sandbox.Controls 
{ 
    public partial class MyUserControl 
    { 
     public MyUserControl() 
     { 
      InitializeComponent(); 
     } 

     public void Initialize(MyUserControlModel context) 
     { 
      DataContext = context; 
     } 
    } 

} 

namespace Sandbox.Controls 
{ 
    public class MyUserControlModel : BaseModel 
    { 
     public string MyBoundText { get; set; } 
     public MyUserControlModel() 
     { 
      MyBoundText = "Hello World!"; 
      OnChange(""); 
     } 
    } 
} 

回答

3

也就是说,你不应该从自身UserControl设置DataContext的直接原因之一。

当你这样做,你不能再使用任何其他DataContext用它,因为用户控件的DataContext被硬编码英寸

在你结合的情况下,通常是DataContext将被继承,因此Visibility结合能找到在当前DataContext上的属性MyControlVisible,但是因为您在UserControl的构造函数中对DataContext进行了硬编码,所以找不到该属性。

你可以在你的绑定指定一个不同的结合来源,如

<Controls:MyUserControl Visibility="{Binding 
    RelativeSource={RelativeSource AncestorType={x:Type Window}}, 
    Path=DataContext.MyControlVisible, 
    Converter={StaticResource boolToVis}}" ... /> 

然而,这只是一种变通方法针对此特定情况下的问题,在我看来不是一个永久性的解决方案。更好的方法是根本就没有硬编码DataContextUserControl

有几种不同的方式,你可以根据你的用户控件的用途和应用程序如何设计做。

  • 你可以在你的用户控件创建一个DependencyProperty在价值传递,并绑定到。

    <Controls:MyUserControl UcModel="{Binding MyUControlModelProperty}" ... /> 
    

    <UserControl x:Class="Sandbox.Controls.MyUserControl" 
          ElementName=MyUserControl...> 
        <Grid DataContext="{Binding UCModel, ElementName=MyUserControl}"> 
         <TextBlock Text="{Binding MyBoundText}"/> 
        </Grid> 
    </UserControl> 
    
  • 或者你可以建立自己的UserControl并期望一个特定的属性将得到传递给它的DataContext。这通常是我所做的,结合DataTemplates

    <Controls:MyUserControl DataContext="{Binding MyUControlModelProperty}" ... /> 
    

    <UserControl x:Class="Sandbox.Controls.MyUserControl"...> 
        <Grid> 
         <TextBlock Text="{Binding MyBoundText}"/> 
        </Grid> 
    </UserControl> 
    
  • 正如我前面所说,我喜欢用DataTemplates显示我的UserControls意想不到特定类型的Model他们DataContext,所以通常我主窗口XAML会看起来像这样:

    <DataTemplate DataType="{x:Type local:MyUControlModel}"> 
        <Controls:MyUserControl /> 
    </DataTemplate> 
    
    <ContentPresenter Content="{Binding MyUControlModelProperty}" ... />