我正在设置一个 ListView,其
Source
属性设置为我的一个类的 ivar,称为 Cat
。
每个
Cat
都有 ObservableCollection
个 Trait
对象:
private ObservableCollection<Trait> _traits = new ObservableCollection<Trait>();
public ObservableCollection<Trait> Traits
{
get
{
return _traits;
}
}
public void AddTrait(Trait t)
{
_traits.Add(t);
// Is this redundant? Is one better than the other?
this.OnPropertyChanged("_traits");
this.OnPropertyChanged("Traits");
}
public IEnumerator<Object> GetEnumerator()
{
return _traits.GetEnumerator();
}
然后我将
Source
属性分配给这个 Traits
集合:
this.CollectionViewSource.Source = CurrentCat.Traits;
这工作正常,并且
Trait
对象正确显示在我的 ListView
中。
问题是对此底层
_traits
集合的更改不会导致 UI 正确更新。例如,这个:
void AddTraitButton_Click(object sender, RoutedEventArgs e)
{
if (this.CurrentCat != null)
{
this.CurrentCat.AddTrait(new Trait());
}
}
在 UI 中似乎没有任何效果立即,但是如果我像这样重置
Source
属性:
var oldSource = this.CollectionViewSource.Source;
this.CollectionViewSource.Source = null;
this.CollectionViewSource.Source = oldSource;
然后
ListView
就会正确更新。但是,我确信一定有一些东西是我遗漏的,因为我希望 UI 在添加/删除项目时进行更新。
编辑:
CollectionViewSource
正在应用于我的 XAML 文件中的 ListView
:
<CollectionViewSource x:Name="CollectionViewSource" x:Key="CollectionViewSource" />
...
<ListView x:Name="ItemListView" ItemsSource="{Binding Source={StaticResource CollectionViewSource}}" ...
我现在似乎找不到它,但我似乎记得绑定到
CollectionViewSource
的一些问题。您是否尝试过直接绑定到 CurrentCat.Traits
并在代码隐藏中设置 this.DataContext = this
(我假设您在这里没有使用 MVVM)?
<ListView x:Name="ItemListView" ItemsSource="{Binding CurrentCat.Traits}" />
我相信您不想直接绑定到 CollectionViewSource 并替换其
Source
来强制刷新,我相信您想要绑定到 CVS 的 View
属性...
<ListView x:Name="ItemListView"
ItemsSource="{Binding Source={StaticResource CollectionViewSource}, Path=View}" ...
...并在更新源集合后调用
CollectionViewSource.Refresh()
。
void AddTraitButton_Click(object sender, RoutedEventArgs e)
{
if (this.CurrentCat != null)
{
this.CurrentCat.AddTrait(new Trait());
this.CollectionViewSource.Refresh();
}
}
此外,还有一些注意事项,因为您似乎对 .NET/WPF 约定相对较新:
this
关键字作为类成员前缀通常是多余的,除非作用域中存在另一个具有相同名称的标识符如果您要在 WPF 中做任何重要的事情,那么值得探索 MVVM 和相关模式;它们帮助您使视图(XAML 对象)尽可能轻便且易于更改。
例如,在您的情况下,我假设您显示的代码来自包含您的 ListView 的任何 Window 或 UserControl 的隐藏代码。遵循 MVVM 模式将涉及创建一个单独的“ViewModel”类,该类将包含
Traits
集合并通过 CollectionViewSource
公开它(使用 View 属性,正如我所提到的)。然后,您的 UserControl 将分配一个 ViewModel 实例作为其 DataContext
,并且 ListView 可以绑定到公开的 CollectionView。您仍然可以专门使用 ObservableCollection。 虽然有一个问题 - 它不会在 IsInDesignMode 中显示数据。也许将来会有所改善。
public class MainViewModel : ViewModelBase
{
...
private ObservableCollection<PartViewModel> _parts;
public ObservableCollection<PartViewModel> Parts
{
get
{
if (_parts == null)
{
_parts = new ObservableCollection<PartViewModel>();
_parts.CollectionChanged += _parts_CollectionChanged;
}
return _parts;
}
}
object m_ReorderItem;
int m_ReorderIndexFrom;
void _parts_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Remove:
m_ReorderItem = e.OldItems[0];
m_ReorderIndexFrom = e.OldStartingIndex;
break;
case NotifyCollectionChangedAction.Add:
if (m_ReorderItem == null)
return;
var _ReorderIndexTo = e.NewStartingIndex;
m_ReorderItem = null;
break;
}
}
private PartViewModel _selectedItem;
public PartViewModel SelectedItem
{
get
{
return _selectedItem;
}
set
{
if (_selectedItem != value)
{
_selectedItem = value;
RaisePropertyChanged("SelectedItem");
}
}
}
...
#region ViewModelBase
public override void Cleanup()
{
if (_parts != null)
{
_parts.CollectionChanged -= _parts_CollectionChanged;
}
base.Cleanup();
}
#endregion
}
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.Resources>
<CollectionViewSource x:Name="PartsCollection" Source="{Binding Parts}"/>
</Grid.Resources>
<ListView Margin="20" CanReorderItems="True" CanDragItems="True" AllowDrop="True"
ItemsSource="{Binding Source={StaticResource PartsCollection}}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" SelectionMode="Single">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
...
</ListView.ItemTemplate>
</ListView>
</Grid>