WPF 自定义控件:DependencyProperty 双向绑定
我有这个自定义用户控件,它有一个列表和一个按钮:
<UserControl x:Class="WpfApplication1.CustomList"
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="300" d:DesignWidth="300">
<Grid>
<ListBox Name="listBox1" ItemsSource="{Binding ListSource}" HorizontalAlignment="Right" Width="174" />
<Button Name="ButtonAdd" Content="Add" HorizontalAlignment="Left" Width="101" />
</Grid>
</UserControl>
后面的代码有一个 IEnumerable 类型的 DependencyProperty 和按钮的处理程序(OnAdd):
public partial class CustomList : UserControl
{
public CustomList( )
{
InitializeComponent( );
ButtonAdd.Click += new RoutedEventHandler( OnAdd );
}
private void OnAdd( object sender, EventArgs e )
{
IList<object> tmpList = this.ListSource.ToList( );
Article tmpArticle = new Article( );
tmpArticle .Name = "g";
tmpList.Add(tmpArticle );
ListSource = (IEnumerable<object>) tmpList;
}
public IEnumerable<object> ListSource
{
get
{
return (IEnumerable<object>)GetValue( ListSourceProperty );
}
set
{
base.SetValue(CustomList.ListSourceProperty, value);
}
}
public static DependencyProperty ListSourceProperty = DependencyProperty.Register(
"ListSource",
typeof( IEnumerable<object> ),
typeof( CustomList ),
new PropertyMetadata( OnValueChanged ) );
private static void OnValueChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
{
( (CustomList)d ).ListSource = (IEnumerable<object>)e.NewValue;
}
}
在按钮处理程序中,我尝试将文章添加到 ListSource(已绑定 )至文章)。 这是我使用 UserControl(CustomList) 的窗口:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:WpfApplication1"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:CustomList ListSource="{Binding Articles, Mode=TwoWay}" Margin="80,0,0,0" />
</Grid>
</Window>
当我单击按钮时,文章变为空,而不是在文章集合中添加文章。并且 ListSource 属性也变为 null。我在这里做错了什么吗?这是预期的行为吗?如果是,那么有什么不同的方法可以做到这一点: 创建一个具有列表和按钮的自定义控件,按钮的处理程序将在列表中添加对象。
I have this Custom UserControl which has a List and a Button:
<UserControl x:Class="WpfApplication1.CustomList"
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="300" d:DesignWidth="300">
<Grid>
<ListBox Name="listBox1" ItemsSource="{Binding ListSource}" HorizontalAlignment="Right" Width="174" />
<Button Name="ButtonAdd" Content="Add" HorizontalAlignment="Left" Width="101" />
</Grid>
</UserControl>
The code behind has a DependencyProperty of Type IEnumerable and a handler(OnAdd) for the Button:
public partial class CustomList : UserControl
{
public CustomList( )
{
InitializeComponent( );
ButtonAdd.Click += new RoutedEventHandler( OnAdd );
}
private void OnAdd( object sender, EventArgs e )
{
IList<object> tmpList = this.ListSource.ToList( );
Article tmpArticle = new Article( );
tmpArticle .Name = "g";
tmpList.Add(tmpArticle );
ListSource = (IEnumerable<object>) tmpList;
}
public IEnumerable<object> ListSource
{
get
{
return (IEnumerable<object>)GetValue( ListSourceProperty );
}
set
{
base.SetValue(CustomList.ListSourceProperty, value);
}
}
public static DependencyProperty ListSourceProperty = DependencyProperty.Register(
"ListSource",
typeof( IEnumerable<object> ),
typeof( CustomList ),
new PropertyMetadata( OnValueChanged ) );
private static void OnValueChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
{
( (CustomList)d ).ListSource = (IEnumerable<object>)e.NewValue;
}
}
In the Button handler I am trying to add an Article to the ListSource(which is bound to the Articles).
This is the window where I use my UserControl(CustomList):
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:WpfApplication1"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:CustomList ListSource="{Binding Articles, Mode=TwoWay}" Margin="80,0,0,0" />
</Grid>
</Window>
When I click the Button, the Articles become null instead of adding an Article in the in the Articles collection. And the ListSource property also becomes null. Am I doing something wrong here? Is this an expected behavior? If yes, what would be a different way of doing this:
Create a Custom Control that will have a List and a Button and the handler for the button will add objects in the List.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这里有很多问题,但导致问题的主要原因是您尝试使用双向绑定的属性之间可能存在类型不匹配。您没有列出您的 Articles 属性的代码,但我认为它可能类似于
IEnumerable
或
ObservableCollection
而不是
IEnumerable
当您设置双向 Binding 并且目标值无法转换回源类型(即
IEnumerable
您可以更改任一侧的类型,但如果您将它们与双向绑定一起使用,则类型应该匹配。
一般来说,将 TwoWay Bindings 与集合一起使用是一个坏主意。每次想要进行更改时,无需复制和替换集合实例,只需在一个实例中添加和删除项目即可。由于该实例在(单向)绑定的两侧都是相同的集合,因此更新将显示在两侧,如果您使用 ObservableCollection,您还可以在任一侧获取更改通知。
您还应该删除 OnValueChanged 代码,因为它只是将属性重置为刚刚在属性上设置的值。
There are a lot of issues here but the main one that's causing your problem is that there is probably a type mismatch between the properties that you are trying to use TwoWay Binding on. You didn't list the code for your Articles property, but I assume it's probably something like
IEnumerable<Article>
orObservableCollection<Article>
and notIEnumerable<object>
as is your ListSource property.When you set up two way Binding and the target value can't be converted back to the source type (i.e.
IEnumerable<object>
->ObservableCollection<Article>
) the source property (Articles here) will receive a null value, which will then get pushed back through the Binding to the target property (ListSource here).You can change the type on either side but if you're using them with a TwoWay Binding the types should match.
In general it's a bad idea to use TwoWay Bindings with collections. Instead of copying and replacing the collection instance every time you want to make a change, just add and remove items from one instance. Since that one instance is the same collection on both sides of the (OneWay) Binding the updates will show up on both sides, and if you're using an ObservableCollection you can also get change notifications on either side.
You should also get rid of your OnValueChanged code since it is just resetting the property to the value that was just set on the property.