DependencyProperty 绑定问题
Issue with DependencyProperty binding
我创建了一个小文件浏览器控件:
<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 的 属性)
目前的问题是,当我点击浏览时,用户控件中的文本框正确更新,但视图模型父控件的 SelectedFile 属性 未设置(未调用设置 属性).
如果我将绑定模式设置为 TwoWay,我得到了这个异常:
An unhandled exception of type 'System.WhosebugException' occurred in Unknown Module.
那我做错了什么?
使用这个:
<userControls:FileBrowserControl SelectedFile="{Binding SelectedFile}" ...
FileBrowserControl 的 DataContext 已设置为自身,因此您实际上是在要求绑定到 SelectedFile,其中 DataContext 是 FileBrowserControl,而不是父 ViewModel。
为您的视图命名并改用 ElementName 绑定。
SelectedFile="{Binding DataContext.SelectedFile, ElementName=element}"
永远不要在用户控件中设置 UserControl 的 DataContext:
这是错误的:
this.DataContext = someDataContext;
因为如果有人要使用你的用户控件,通常的做法是设置它的数据上下文,这与你之前的设置有冲突
<my:SomeUserControls DataContext="{Binding SomeDataContext}" />
会用到哪一个?好吧,这取决于...
名字属性也是如此。您不应该像这样将名称设置为 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
在用户控件中,xaml 表示用户控件的内容,而不是 ControlTemplate/
问题是您在其构造函数中将 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 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 的 属性)
目前的问题是,当我点击浏览时,用户控件中的文本框正确更新,但视图模型父控件的 SelectedFile 属性 未设置(未调用设置 属性).
如果我将绑定模式设置为 TwoWay,我得到了这个异常:
An unhandled exception of type 'System.WhosebugException' occurred in Unknown Module.
那我做错了什么?
使用这个:
<userControls:FileBrowserControl SelectedFile="{Binding SelectedFile}" ...
FileBrowserControl 的 DataContext 已设置为自身,因此您实际上是在要求绑定到 SelectedFile,其中 DataContext 是 FileBrowserControl,而不是父 ViewModel。
为您的视图命名并改用 ElementName 绑定。
SelectedFile="{Binding DataContext.SelectedFile, ElementName=element}"
永远不要在用户控件中设置 UserControl 的 DataContext:
这是错误的:
this.DataContext = someDataContext;
因为如果有人要使用你的用户控件,通常的做法是设置它的数据上下文,这与你之前的设置有冲突
<my:SomeUserControls DataContext="{Binding SomeDataContext}" />
会用到哪一个?好吧,这取决于...
名字属性也是如此。您不应该像这样将名称设置为 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
在用户控件中,xaml 表示用户控件的内容,而不是 ControlTemplate/
问题是您在其构造函数中将 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 中。