Пользовательский элемент управления WPF: двусторонняя привязка DependencyProperty - PullRequest
3 голосов
/ 16 апреля 2011

У меня есть этот пользовательский пользовательский элемент управления, который имеет список и кнопку:

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

Код имеет свойство DependencyProperty типа IEnumerable и обработчик (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 также становится нулевым.Я что-то здесь не так делаю?Это ожидаемое поведение?Если да, что может быть по-другому: создайте пользовательский элемент управления, который будет иметь список и кнопку, а обработчик для кнопки будет добавлять объекты в список.

1 Ответ

5 голосов
/ 16 апреля 2011

Здесь много проблем, но главная причина, которая вызывает вашу проблему, состоит в том, что, вероятно, существует несоответствие типов между свойствами, для которых вы пытаетесь использовать TwoWay Binding. Вы не перечислили код для своего свойства Articles, но я предполагаю, что это, вероятно, что-то вроде IEnumerable<Article> или ObservableCollection<Article>, а не IEnumerable<object>, как ваше свойство ListSource.

Когда вы устанавливаете двустороннюю привязку и целевое значение не может быть преобразовано обратно в тип источника (т. Е. IEnumerable<object> -> ObservableCollection<Article>), свойство источника (статьи здесь) получит нулевое значение, которое будет затем возвращаемся через привязку к целевому свойству (здесь ListSource).

Вы можете изменить тип с любой стороны, но если вы используете их с двухсторонним связыванием, типы должны совпадать.

В целом, использование TwoWay Bindings с коллекциями является плохой идеей. Вместо того чтобы копировать и заменять экземпляр коллекции каждый раз, когда вы хотите внести изменение, просто добавьте и удалите элементы из одного экземпляра. Поскольку этот один экземпляр является одной и той же коллекцией с обеих сторон (OneWay) Binding, обновления будут отображаться с обеих сторон, и если вы используете ObservableCollection, вы также можете получать уведомления об изменениях с обеих сторон.

Вам также следует избавиться от кода OnValueChanged, поскольку он просто сбрасывает свойство в значение, которое было только что установлено в свойстве.

...