WPF 自定义控件:DependencyProperty 双向绑定

发布于 2024-11-02 04:16:06 字数 2747 浏览 1 评论 0原文

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

<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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

不及他 2024-11-09 04:16:06

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

ObservableCollection

而不是 IEnumerable; 与您的 ListSource 属性一样。

当您设置双向 Binding 并且目标值无法转换回源类型(即 IEnumerable-> ObservableCollection

)时,源属性(此处为 Articles)将接收一个 null 值,然后该值将通过 Binding 推回到目标属性(此处为 ListSource)。

您可以更改任一侧的类型,但如果您将它们与双向绑定一起使用,则类型应该匹配。

一般来说,将 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> or ObservableCollection<Article> and not IEnumerable<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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文