WPF数据绑定:更改绑定集合中的项目不会更新绑定源?

时间:2010-11-30 12:07:51

标签: c# wpf data-binding xaml

我有两个班级:

public class Person
{
    public string FirstName
    {
        get { return firstName; }
        set { firstName = value; }
    }
    public string LastName
    {
        get { return lastName; }
        set { lastName = value; }
    }
    public ObservableCollection<AccountDetail> Details
    {
        get { return details; }
        set { details = value; }
    }
    public ObservableCollection<AccountDetail> Phones
    {
        get
        {
            ObservableCollection<AccountDetail> phones;
            phones = new ObservableCollection<AccountDetail>();
            foreach (AccountDetail detail in Details)
            {
                if (detail.Type == DetailType.Phone)
                {
                    phones.Add(detail);
                }
            }
            return phones;
        }
        set
        {
            ObservableCollection<AccountDetail> phones;
            phones = value;
            foreach (AccountDetail detail in Details)
            {
                if (detail.Type == DetailType.Phone)
                {
                    Details.Remove(detail);
                }
            }
            foreach (AccountDetail detail in phones)
            {
                if (!string.IsNullOrEmpty(detail.Value))
                {
                    Details.Add(detail); 
                }
            }
        }
    }

    private string firstName;
    private string lastName;
    private ObservableCollection<AccountDetail> details;
}

public class AccountDetail
{
    public DetailType Type
    {
        get { return type; }
        set { type = value; }
    }
    public string Value
    {
        get { return this.value; }
        set { this.value = value; }
    }

    private DetailType type;
    private string value;
}

在我的XAML文件中,我有一个名为PhonesListBox的ListBox,它是绑定到电话列表的数据(Person对象的属性):

<Window.Resources>

    <!-- Window controller -->
    <contollers:PersonWindowController 
        x:Key="WindowController" />

</Window.Resources>

...

<ListBox 
    Name="PhonesListBox" 
    Margin="0,25,0,0" 
    HorizontalAlignment="Stretch" 
    VerticalAlignment="Top" 
    ItemsSource="{Binding Path=SelectedPerson.Phones, 
                  Source={StaticResource ResourceKey=WindowController}}"
    HorizontalContentAlignment="Stretch" />

...

在其后面的代码中,有一个按钮的处理程序,它为PhonesListBox添加一个新项:

private void AddPhoneButton_Click(object sender, RoutedEventArgs e)
{
    ObservableCollection<AccountDetail> phones;

    phones = (ObservableCollection<AccountDetail>)PhonesListBox.ItemsSource;
    phones.Add(new AccountDetail(DetailType.Phone));
}

问题是,新添加的列表框项目未添加到人员的详细信息可观察集合中,即未更新电话属性(永远不会调用set)。为什么?我哪里弄错了?

感谢所有帮助。

更新:我将AddPhoneButton_Click方法更改为:

private void AddPhoneButton_Click(object sender, RoutedEventArgs e)
{
    PersonWindowController windowController;
    ObservableCollection<AccountDetail> details;

    windowController = (PersonWindowController)this.FindResource("WindowController");
    details = windowController.SelectedPerson.Details;
    details.Add(new AccountDetail(DetailType.Phone));
}

这会更新相应的集合,details而非Phones(因为电话只是详细信息项子集的视图或getter )。此外,我意识到我甚至不需要电话设置器。我现在面临的问题是我的UI没有更新到对细节集合(以及随后的电话)所做的更改。由于细节和电话都没有变化,我不知道如何或在哪里要求改变房产;他们的收藏成员是。救命。请。

4 个答案:

答案 0 :(得分:2)

为什么每次检索ObservableCollection<AccountDetail>属性时都会创建一个新的Phones?通常,Person类将具有类型ObservableCollection<AccountDetail>的成员字段,该字段将仅在Phones属性的getter中返回,而不是每次都创建一个新的。例如,在构造Person的实例时,可以填充此集合。

我不知道这是否能解决您的问题,但似乎应该有所帮助。

答案 1 :(得分:1)

听起来你的ObservableCollection<AccountDetail>不仅仅包含手机了,所以看起来你确实需要添加CollectionViewSource的{​​{1}}:

Filter

答案 2 :(得分:0)

Person课改为以下内容应该有效:

public class Person
{
    public string FirstName
    {
        get { return firstName; }
        set { firstName = value; }
    }
    public string LastName
    {
        get { return lastName; }
        set { lastName = value; }
    }
    public ObservableCollection<AccountDetail> Details
    {
        get { return details; }
        set { details = value; }
    }

    public ObservableCollection<AccountDetail> Phones
    {
        get
        {
            if (phones == null)
            {
                phones = new ObservableCollection<AccountDetail>();
            }

            phones.Clear();
            foreach (AccountDetail detail in Details)
            {
                if (detail.Type == DetailType.Phone)
                {
                    phones.Add(detail);
                }
            }
            return phones;
        }
        set
        {
            phones.Clear();

            foreach (var item in value)
            {
                phones.Add(item);
            }

            foreach (AccountDetail detail in Details)
            {
                if (detail.Type == DetailType.Phone)
                {
                    Details.Remove(detail);
                }
            }

            foreach (AccountDetail detail in phones)
            {
                if (!string.IsNullOrEmpty(detail.Value))
                {
                    Details.Add(detail);
                }
            }
        }
    }

    private string firstName;
    private string lastName;
    private ObservableCollection<AccountDetail> details;
    public ObservableCollection<AccountDetail> phones;
}

代码未经过测试,可能需要您进行一些更改才能实际工作。

答案 3 :(得分:0)

尝试类似:

public class Person : INotifyPropertyChanged
{
    public string FirstName
    {
        get { return firstName; }
        set { firstName = value; }
    }
    public string LastName
    {
        get { return lastName; }
        set { lastName = value; }
    }
    public ObservableCollection<AccountDetail> Details
    {
        get { return details; }
        set { details = value; }
    }

    public void AddDetail(AccountDetail detail) {
        details.Add(detail);
        OnPropertyChanged("Phones");
    }

    public IEnumerable<AccountDetail> Phones
    {
        get
        {
            return details.Where(d => d.Type == DetailType.Phone);
        }
    }

    private string firstName;
    private string lastName;
    private ObservableCollection<AccountDetail> details;


    /// <summary>
    /// Called when a property changes.
    /// </summary>
    /// <param name="propertyName">Name of the property.</param>
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
        if (propertyChanged != null)
        {
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    #region INotifyPropertyChanged Members

    /// <summary>
    /// Occurs when a property value changes.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    #endregion

}

您可以从按钮事件处理程序调用AddDetail方法:

private void AddPhoneButton_Click(object sender, RoutedEventArgs e)
{
    PersonWindowController windowController;
    ObservableCollection<AccountDetail> details;

    windowController = (PersonWindowController)this.FindResource("WindowController");
    windowController.SelectedPerson.AddDetail(new AccountDetail(DetailType.Phone));
}

为Phones属性提升OnPropertyChanged事件只会导致WPF绑定框架重新查询属性,并在将详细信息列表中添加Phone详细信息后重新评估Linq查询。