DependencyProperty绑定问题

时间:2015-03-11 08:40:26

标签: c# .net wpf mvvm-light

我创建了一个小文件浏览器控件:

<UserControl x:Class="Test.UserControls.FileBrowserControl"
             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" 
             d:DesignHeight="44" d:DesignWidth="461" Name="Control">
    <Grid Margin="3">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <TextBox  Margin="3" Text="{Binding SelectedFile}" IsReadOnly="True" TextWrapping="Wrap" />
        <Button HorizontalAlignment="Right" Margin="3" Width="100" Content="Browse" Grid.Column="1" Command="{Binding BrowseCommand}" />
    </Grid>
</UserControl>

后面的代码如下:

public partial class FileBrowserControl : UserControl
{
    public ICommand BrowseCommand { get; set; }
    //The dependency property
    public static DependencyProperty SelectedFileProperty = DependencyProperty.Register("SelectedFile",
        typeof(string),typeof(FileBrowserControl), new PropertyMetadata(String.Empty));
    public string SelectedFile { get{ return (string)GetValue(SelectedFileProperty);}  set{ SetValue(SelectedFileProperty, value);}}
    //For my first test, this is a static string
    public string Filter { get; set; }

    public FileBrowserControl()
    {
        InitializeComponent();
        BrowseCommand = new RelayCommand(Browse);
        Control.DataContext = this;
    }
    private void Browse()
    {
        SaveFileDialog dialog = new SaveFileDialog();
        if (Filter != null)
        {
            dialog.Filter = Filter;
        }
        if (dialog.ShowDialog() == true)
        {
            SelectedFile = dialog.FileName;
        }
    }
}

我这样使用它:

<userControls:FileBrowserControl SelectedFile="{Binding SelectedFile}" Filter="XSLT File (*.xsl)|*.xsl|All Files (*.*)|*.*"/>

(SelectedFile是使用此控件的usercontrol的ViewModel的属性)

目前的问题是当我点击Browse时,usercontrol中的文本框正在更新,但是未设置viewmodel父控件的SelectedFile属性(不调用set属性)。

如果我将绑定模式设置为TwoWay,我得到了这个例外:

An unhandled exception of type 'System.StackOverflowException' occurred in Unknown Module.

那我做错了什么?

3 个答案:

答案 0 :(得分:5)

主要问题是您在其构造函数中将UserControl的DataContext设置为自身:

DataContext = this;

您不应该这样做,因为它会破坏任何基于DataContext的Binding,例如到继承的DataContext值中的视图模型实例。

相反,你会改变UserControl的XAML中的绑定,如下所示:

<TextBox Text="{Binding SelectedFile,
                RelativeSource={RelativeSource AncestorType=UserControl}}" />

现在,当您使用UserControl并编写类似

的绑定时
<userControls:FileBrowserControl SelectedFile="{Binding SelectedFile}" />

SelectedFile属性绑定到视图模型中的SelectedFile属性,该属性应该位于从父控件继承的DataContext中。

答案 1 :(得分:1)

使用此:

<userControls:FileBrowserControl SelectedFile="{Binding SelectedFile}" ...

FileBrowserControl的DataContext已经设置为自身,因此您实际上要求绑定到DataFtext是FileBrowserControl的SelectedFile,而不是父ViewModel。

为视图命名并使用ElementName绑定。

SelectedFile="{Binding DataContext.SelectedFile, ElementName=element}"

答案 2 :(得分:1)

不要在usercontrol中设置UserControl的DataContext:

这是错的:

this.DataContext = someDataContext;

因为如果有人会使用你的用户控件,那么通常的做法是设置它的datacontext并且它与你之前设置的相冲突

<my:SomeUserControls DataContext="{Binding SomeDataContext}" />

将使用哪一个?嗯,这取决于......

同样适用于Name属性。你不应该像以下那样将名称设置为UserControl:

<UserControl x:Class="WpfApplication1.SomeUserControl" Name="MyUserControl1" />

因为它与

冲突
<my:SomeUserControls Name="SomeOtherName" />

<强> SOLUTION:
在您的控件中,只需使用RelativeSource Mode = FindAncestor:

<TextBox Text="{Binding SelectedFile, RelativeSource={RelativeSource AncestorType="userControls:FileBrowserControl"}" />

关于如何完成所有第三方控件的问题:他们使用TemplateBinding。但是TemplateBinding只能在ControlTemplate中使用。 http://www.codeproject.com/Tips/599954/WPF-TemplateBinding-with-ControlTemplate

在usercontrol中,xaml表示UserControl的内容,而不是ControlTemplate /