DependencyProperty 绑定问题
Posted
技术标签:
【中文标题】DependencyProperty 绑定问题【英文标题】:Issue with DependencyProperty binding 【发布时间】:2015-05-12 23:05:04 【问题描述】:我创建了一个小的文件浏览器控件:
<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 是使用该控件的用户控件的 ViewModel 的属性)
目前的问题是,当我点击浏览时,用户控件中的文本框正在正确更新,但未设置 viewmodel 父控件的 SelectedFile 属性(未调用 set 属性)。
如果我将绑定的模式设置为双向,我得到了这个异常:
An unhandled exception of type 'System.***Exception' occurred in Unknown Module.
那么我做错了什么?
【问题讨论】:
【参考方案1】:主要问题是您在其构造函数中将 UserControl 的 DataContext 设置为自身:
DataContext = this;
您不应该这样做,因为它会破坏任何基于 DataContext 的绑定,即到由 DataContext 属性的属性值继承提供的视图模型实例
相反,您可以像这样更改 UserControl 的 XAML 中的绑定:
<TextBox Text="Binding SelectedFile,
RelativeSource=RelativeSource AncestorType=UserControl" />
现在,当您使用 UserControl 并编写类似的绑定时
<userControls:FileBrowserControl SelectedFile="Binding SelectedFile" />
SelectedFile 属性绑定到视图模型中的 SelectedFile 属性,该属性应位于从父控件继承的 DataContext 中。
【讨论】:
好像好多了,谢谢。您似乎非常了解这些良好做法。我还有一个其他相关的小问题:目前这个 userControl 的所有逻辑都在后面的代码中。这很常见吗?因为它并没有真正尊重我试图遵循的 MVVM 架构。 用户控件通常在其代码隐藏中包含其所有逻辑,至少是与视图相关的那些逻辑部分。然而,其背后的代码不应(大量)修改其视图模型。 好的,但是假设我有一个过滤器、InitialDirectory、SelectedFile,......我应该无法将它绑定到包含这些字段的 ViewModel 吗? 如答案所示,您可以将这些属性绑定到您使用控件的视图模型属性。然而,UserControl 的 XAML 也可以直接访问视图模型属性作为绑定源,但是您可能不需要在 UserControl 中声明其他属性。如果例如您的控件的 XAML 将直接访问视图模型的 SelectedFile 属性,控件本身不应有额外的 SelectedFile 属性。【参考方案2】:永远不要在 usercontrol 中设置 UserControl 的 DataContext:
这是错误的:
this.DataContext = someDataContext;
因为如果有人会使用你的用户控件,它的常见做法是设置它的数据上下文,它与你之前设置的有冲突
<my:SomeUserControls DataContext="Binding SomeDataContext" />
将使用哪一个?好吧,这取决于...
这同样适用于 Name 属性。您不应该像这样将名称设置为 UserControl:
<UserControl x:Class="WpfApplication1.SomeUserControl" Name="MyUserControl1" />
因为和
有冲突<my:SomeUserControls Name="SomeOtherName" />
解决方案: 在您的控件中,只需使用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/
【讨论】:
【参考方案3】:使用这个:
<userControls:FileBrowserControl SelectedFile="Binding SelectedFile" ...
FileBrowserControl 的 DataContext 已设置为自身,因此您实际上要求绑定到 DataContext 是 FileBrowserControl,而不是父 ViewModel 的 SelectedFile。
为您的视图命名并改用 ElementName 绑定。
SelectedFile="Binding DataContext.SelectedFile, ElementName=element"
【讨论】:
然后在我的脑海中我听到了 Unreal 的声音:God like
:) 非常感谢,我没想到父级中的绑定仍然引用了子级的 DataContext。但我有点失落。所有这些第三方库如何使他们的用户控件不需要这个ElementName
?
这是个好问题。我认为这是因为他们使用 CustomControls,而不是 UserControls,但我并不完全确定。您应该将其作为一个新问题发布。
没问题 :-) 如果问题解决了您的问题,您还应该将问题标记为已回答;-) 谢谢。以上是关于DependencyProperty 绑定问题的主要内容,如果未能解决你的问题,请参考以下文章
UWP DependencyProperty 绑定和 DataTemplate 绑定
只能在 DependencyObject 的 DependencyProperty 上设置“绑定”
设置绑定到 WPF 用户控件内的自定义 DependencyProperty
尝试绑定此 ValueConverter 时出现“只能在 DependencyObject 的 DependencyProperty 上设置绑定”错误