如何在另一个 ObservableCollection 中使用 ObservableCollection?

How to use ObservableCollection in another ObservableCollection?

在我的 WPF 应用程序中,我想使用 ObservableCollection 来包含一些 Wave classes。每个 Wave 都有一个 ObservableCollection 来包含一些 Couple class。看起来像:

ObservableCollection<Wave> Waves { get; set; }
- int StartYear { get; set; }
- ObservableCollection<Couple> Couples { get; set; }
  - int A { get; set; }
  - int B { get; set; }
- some other Properties

添加 Wave 后,int 属性运行良好。但是,每个 Wave 中的 Couples 都会在每个 Wave 更改时更改。它们具有相同的 GUID。

为什么每个 Wave 中的 Couples 都不一样?

这是我的代码:

// Couple.cs
public class Couple : DependencyObject
{
    public int A { get { return (int)GetValue(AProperty); } set { SetValue(AProperty, value); } }
    public int B { get { return (int)GetValue(BProperty); } set { SetValue(BProperty, value); } }
    public static readonly DependencyProperty AProperty = DependencyProperty.Register("A", typeof(int), typeof(Couple), new PropertyMetadata(0));
    public static readonly DependencyProperty BProperty = DependencyProperty.Register("B", typeof(int), typeof(Couple), new PropertyMetadata(0));
}
// Wave.cs
class Wave : DependencyObject
{
    public ObservableCollection<Couple> Couples
    {
        get { return (ObservableCollection<Couple>)GetValue(CouplesProperty); }
        set { SetValue(CouplesProperty, value); }
    }
    public static readonly DependencyProperty CouplesProperty = DependencyProperty.Register("Couples", typeof(ObservableCollection<Couple>), typeof(Wave), new PropertyMetadata(new ObservableCollection<Couple>()));
}
// XAML of the UserControl
<ItemsControl Grid.Column="1" ItemsSource="{Binding Waves}" ScrollViewer.HorizontalScrollBarVisibility="Visible" Margin="0,0,0,6" Focusable="False" ScrollViewer.CanContentScroll="False" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Width="240">
                <ItemsControl ItemsSource="{Binding Couples}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="40"/>
                                    <ColumnDefinition/>
                                    <ColumnDefinition/>
                                </Grid.ColumnDefinitions>
                                <TextBlock Text="01" TextAlignment="Center"/>
                                <TextBox Grid.Column="1" Text="{Binding A}"/>
                                <TextBox Grid.Column="2" Text="{Binding B}"/>
                            </Grid>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

我是 MVVM 的新手。以前,这些值是通过Items 属性 of ListBox获取的,但现在我想更改这些代码以适应MVVM模式。谢谢!

这里的问题是您为可变引用类型的依赖项 属性 设置了非空默认值。拥有 属性 的 class 的所有实例(即所有 Wave 对象)将使用相同的默认集合对象。

Wave 的构造函数 class 应该设置一个像

这样的初始值
class Wave
{
    public static readonly DependencyProperty CouplesProperty =
        DependencyProperty.Register(
            nameof(Couples),
            typeof(ObservableCollection<Couple>),
            typeof(Wave));

    public Wave()
    {
        Couples = new ObservableCollection<Couple>();
    }

    ...
}

除此之外,您不应从 DependencyObject 派生视图模型 classes。如有必要,他们应该改为实现 INotifyPropertyChanged 接口:

public class Couple : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private int a;

    public int A
    {
        get { return a; }
        set
        {
            a = value;
            PropertyChanged?.Invoke(this,
                new PropertyChangedEventArgs(nameof(A)));
        }
    }

    private int b;

    public int B
    {
        get { return b; }
        set
        {
            b = value;
            PropertyChanged?.Invoke(this,
                new PropertyChangedEventArgs(nameof(B)));
        }
    }
}

class Wave
{
    public ObservableCollection<Couple> Couples { get; }
        = new ObservableCollection<Couple>();
}

请注意,Couples 属性 现在是只读的,拥有者 class 因此不需要为 属性 触发更改通知。


对于 INotifyPropertyChanged 实现,您通常会有一个封装 PropertyChanged 事件调用的基础 class,例如

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void NotifyPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    protected void SetValue<T>(ref T field, T value, string propertyName)
    {
        if (!EqualityComparer<T>.Default.Equals(field, value))
        {
            field = value;
            NotifyPropertyChanged(propertyName);
        }
    }
}

并像

一样使用它
public class Couple : ViewModelBase
{
    private int a;

    public int A
    {
        get { return a; }
        set { SetValue(ref a, value, nameof(A)); }
    }

    private int b;

    public int B
    {
        get { return b; }
        set { SetValue(ref b, value, nameof(B)); }
    }
}