UserControl绑定与模型 - "属性未找到"

时间:2014-03-16 19:06:03

标签: c# wpf xaml user-controls

我制作了一个超级简单的用户控件来浏览文件

<UserControl x:Class="DrumMapConverter.FileBrowserTextBox"
             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" 
             DataContext="{Binding RelativeSource={RelativeSource Self}}"
             mc:Ignorable="d" Height="24" d:DesignWidth="500">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Column="0" Name="lblLabel" Text="{Binding Label, Mode=TwoWay}" MinWidth="150"/>
        <Button Grid.Column="1" Content=" ... " Click="BrowseButton_Click"/>
        <TextBox Grid.Column="2" Name="txtFilepath" Text="{Binding FilePath, Mode=TwoWay}"/>
    </Grid>

</UserControl>

具有2个依赖属性:

标签和文件路径:

// FilePath
public static readonly DependencyProperty FilePathProperty =
    DependencyProperty.Register("FilePath", typeof(string), typeof(FileBrowserTextBox), new UIPropertyMetadata(string.Empty, new PropertyChangedCallback(OnFilePathPropertyChanged)));

public string FilePath
{
    get { return (string)GetValue(FilePathProperty); }
    set { SetValue(FilePathProperty, value); }
}
static void OnFilePathPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
    var obj = o as FileBrowserTextBox;
    if (obj == null)
        return;
    FileBrowserTextBox fileBrowserTextBox = (FileBrowserTextBox)o;
    fileBrowserTextBox.txtFilepath.Text = (string)e.NewValue;
}
// Label
public static readonly DependencyProperty LabelProperty =
    DependencyProperty.Register("Label", typeof(string), typeof(FileBrowserTextBox), new UIPropertyMetadata(string.Empty, new PropertyChangedCallback(OnLabelPropertyChanged)));
public string Label
{
    get { return (string)GetValue(LabelProperty); }
    set { SetValue(LabelProperty, value); }
}
static void OnLabelPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
    var obj = o as FileBrowserTextBox;
    if (obj == null)
        return;
    FileBrowserTextBox fileBrowserTextBox = (FileBrowserTextBox)o;
    fileBrowserTextBox.lblLabel.Text = (string)e.NewValue;
}

然后在我的MainWindow ctor中我有这个

private DrumMapConverterDataModel model;
public MainWindow()
{
    InitializeComponent();
    model = new DrumMapConverterDataModel();
    DataContext = model;
}

模型有2个属性:

private string inputFile = "";
public string InputFile
{
    get { return inputFile; }
    set { 
        inputFile = value;
        OnPropertyChanged("InputFile");
         }
}

private string outputFile = "";
public string OutputFile
{
    get { return outputFile; }
    set
    {
        outputFile = value;
        OnPropertyChanged("OutputFile");
    }
}

我在MainWindow.XAML中像这样绑定

<cust:FileBrowserTextBox  Label="Input File" FilePath="{Binding InputFile}"/>
<cust:FileBrowserTextBox  Label="Output File" FilePath="{Binding OutputFile}"/>

运行它并获得此错误

System.Windows.Data错误:40:BindingExpression路径错误:'object'''FileBrowserTextBox'(Name ='')'上找不到'InputFile'属性。 BindingExpression:路径= INPUTFILE; DataItem ='FileBrowserTextBox'(Name =''); target元素是'FileBrowserTextBox'(Name =''); target属性是'FilePath'(类型'String') System.Windows.Data错误:40:BindingExpression路径错误:'object'''FileBrowserTextBox'(Name ='')'上找不到'OutputFile'属性。 BindingExpression:路径= OUTPUTFILE; DataItem ='FileBrowserTextBox'(Name =''); target元素是'FileBrowserTextBox'(Name =''); target属性是'FilePath'(类型'String')

这基本上意味着UserControl中没有InputFile和OutputFile,但是我试图将控件的FilePath属性与我的模型的InputFile和OutputFile绑定,为什么不起作用?

提前致谢。

2 个答案:

答案 0 :(得分:2)

您已在DataContext上将FileBrowserTextBox设置为自身,这使得所有默认绑定都会自行搜索属性。

避免为UserControl设置DataContext并使用ElementName 进行绑定:

<UserControl x:Name="fileBrowserControl">
  ...
  <TextBlock Text="{Binding Label, ElementName=fileBrowserControl}"/>
  <Button Grid.Column="1" Content=" ... " Click="BrowseButton_Click"/>
  <TextBox Text="{Binding FilePath, ElementName=fileBrowserControl}"/>
  ...
</UserControl>

如果想要保留DataContext for UserControl ,则必须更改Window中的代码以明确指向Window的DataContext ,如下所示:

<cust:FileBrowserTextBox Label="Input File"
                         FilePath="{Binding DataContext.InputFile, 
                                   RelativeSource={RelativeSource FindAncestor, 
                                       AncestorType=Window}}"/>

代替RelativeSource,您还可以在绑定中使用ElementName,方法是将x:Name赋予窗口并使用ElementName进行绑定,如UserControl中所述。

答案 1 :(得分:1)

当你这样做时

DataContext="{Binding RelativeSource={RelativeSource Self}}"

FileBrowserTextBox中,您覆盖了继承的DataContext更改绑定上下文。这意味着它会尝试在InputFile控件中查找OutputFileFileBrowserTextBox属性。删除该行并更改FileBrowserTextBox中的绑定,以便他们不会影响DataContext,例如使用RelativeSource,如下所示:

<TextBlock Grid.Column="0" Name="lblLabel" Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=Label}" MinWidth="150"/>
<TextBox Grid.Column="2" Name="txtFilepath" Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=FilePath}"/>

也在PropertyChangedCallback LabelFilePath中:

fileBrowserTextBox.txtFilepath.Text = (string)e.NewValue;
fileBrowserTextBox.lblLabel.Text = (string)e.NewValue;

如果您只想更改UI,则根本不需要处理属性更改的回调。您已经在XAML中使用了应该为您执行此操作的绑定,使用绑定上下文很好,上面的行只会用固定值覆盖这些绑定