如何在另一个 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)); }
}
}
在我的 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)); }
}
}