WPF自定义控件:依赖属性双向绑定

时间:2011-04-15 20:48:28

标签: wpf custom-controls dependency-properties

我有这个自定义用户控件,它有一个列表和一个按钮:

<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>

后面的代码有一个Type IEnumerable的DependencyProperty和一个Button的处理程序(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;
    }
}

在Button处理程序中,我试图将一个文章添加到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>

当我单击Button时,文章变为null,而不是在Articles集合中添加Article。 ListSource属性也变为null。我在这里做错了吗?这是预期的行为吗?如果是的话,这将是一种不同的方式: 创建一个具有List和Button的自定义控件,该按钮的处理程序将在List中添加对象。

1 个答案:

答案 0 :(得分:5)

这里存在很多问题,但导致问题的主要问题是,您尝试使用TwoWay绑定的属性之间可能存在类型不匹配。您没有列出您的Articles属性的代码,但我认为它可能类似于IEnumerable<Article>ObservableCollection<Article>而不是IEnumerable<object>,因为您的ListSource属性。

当你设置双向绑定并且目标值无法转换回源类型(即IEnumerable<object> - &gt; ObservableCollection<Article>)时,源属性(此处的文章)将收到null值,然后通过Binding返回到目标属性(此处为ListSource)。

您可以更改任何一方的类型,但如果您使用TwoWay Binding,则类型应该匹配。

一般来说,将TwoWay Bindings与集合一起使用是个坏主意。每次要进行更改时,只需添加和删除一个实例中的项目,而不是复制和替换集合实例。由于一个实例是(OneWay)绑定两侧的相同集合,因此更新将显示在双方,如果您正在使用ObservableCollection,您还可以获得任何一方的更改通知。

您还应该删除OnValueChanged代码,因为它只是将属性重置为刚刚在属性上设置的值。