【发布时间】:2014-05-14 17:44:36
【问题描述】:
我有一个 ObservableCollection,它是网格的 DataContext。 Grid 是插入到主窗口中的用户控件。
我想在状态栏中显示“记录 x of y”,因此,作为第一步,我尝试使用此 XAML 在网格中显示它:
<TextBlock Grid.Row="2" Grid.Column="3">
<TextBlock Text="{Binding CurrentPosition}" /> <TextBlock Text="{Binding Count}" />
</TextBlock>
Count 可以正常工作,并且会在添加新项目时自动更新。 CurrentPosition(在我下面的代码中定义)始终保持为 0。
如何使 CurrentPosition 自动更新?我希望不必使用 INotify**,因为这已经是 ObservableCollection。
我也没有任何代码隐藏,所以我希望它可以在我的类(或模型)和 XAML 中实现。
我确实尝试过使用 CurrentChanged 但没有成功:
public MyObservableCollection() : base() {
this.GetDefaultView().CurrentChanged += MyObservableCollection_CurrentChanged;
}
MyObservableCollection:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace ToDoApplication.Models {
public class MyObservableCollection<T> : ObservableCollection<T> {
public MyObservableCollection() : base() {
}
public MyObservableCollection(List<T> list) : base(list) {
}
public MyObservableCollection(IEnumerable<T> collection) : base(collection) {
}
private System.ComponentModel.ICollectionView GetDefaultView() {
return System.Windows.Data.CollectionViewSource.GetDefaultView(this);
}
public int CurrentPosition {
get {
return this.GetDefaultView().CurrentPosition;
}
}
public void MoveFirst() {
this.GetDefaultView().MoveCurrentToFirst();
}
public void MovePrevious() {
this.GetDefaultView().MoveCurrentToPrevious();
}
public void MoveNext() {
this.GetDefaultView().MoveCurrentToNext();
}
public void MoveLast() {
this.GetDefaultView().MoveCurrentToLast();
}
public bool CanMoveBack() {
return this.CurrentPosition > 0;
}
public bool CanMoveForward() {
return (this.Count > 0) && (this.CurrentPosition < this.Count - 1);
}
}
public enum Navigation {
First, Previous, Next, Last, Add
}
}
更新:我正在添加以下代码作为可能的解决方案,但我不太喜欢它,我希望出现一个不需要我使用的更好的代码INotifyPropertyChanged - 我怀疑我将结束重复 ObservableCollection 应该已经可用的所有功能。 (我也不知道为什么要重新通知 Count 的变化。)
更新 2:以下不是(完整)解决方案,因为它会干扰集合的其他行为(通知),但我将其保留在这里以防它包含任何有用信息.
namespace ToDoApplication.Models {
public class MyObservableCollection<T> : ObservableCollection<T>, INotifyPropertyChanged {
public new event PropertyChangedEventHandler PropertyChanged;
private int _currentPos = 1;
public MyObservableCollection() : base() {
this.GetDefaultView().CurrentChanged += MyObservableCollection_CurrentChanged;
this.CollectionChanged += MyObservableCollection_CollectionChanged;
}
public MyObservableCollection(List<T> list) : base(list) {
this.GetDefaultView().CurrentChanged += MyObservableCollection_CurrentChanged;
this.CollectionChanged += MyObservableCollection_CollectionChanged;
}
public MyObservableCollection(IEnumerable<T> collection) : base(collection) {
this.GetDefaultView().CurrentChanged += MyObservableCollection_CurrentChanged;
this.CollectionChanged += MyObservableCollection_CollectionChanged;
}
void MyObservableCollection_CurrentChanged(object sender, EventArgs e) {
this.CurrentPosition = this.GetDefaultView().CurrentPosition;
}
void MyObservableCollection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) {
RaisePropertyChanged("Count");
}
private System.ComponentModel.ICollectionView GetDefaultView() {
return System.Windows.Data.CollectionViewSource.GetDefaultView(this);
}
public int CurrentPosition {
get {
return _currentPos;
}
private set {
if (_currentPos == value + 1) return;
_currentPos = value + 1;
RaisePropertyChanged("CurrentPosition");
}
}
private void RaisePropertyChanged(string propertyName) {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public void MoveFirst() {
this.GetDefaultView().MoveCurrentToFirst();
}
public void MovePrevious() {
this.GetDefaultView().MoveCurrentToPrevious();
}
public void MoveNext() {
this.GetDefaultView().MoveCurrentToNext();
}
public void MoveLast() {
this.GetDefaultView().MoveCurrentToLast();
}
public bool CanMoveBack() {
return this.CurrentPosition > 1;
}
public bool CanMoveForward() {
return (this.Count > 0) && (this.CurrentPosition < this.Count);
}
}
public enum Navigation {
First, Previous, Next, Last, Add
}
}
这样我可以在网格中显示“第 1 项,共 3 项”:
<TextBlock Grid.Row="2" Grid.Column="3" x:Name="txtItemOf">Item
<TextBlock x:Name="txtItem" Text="{Binding CurrentPosition}" /> of
<TextBlock x:Name="txtOf" Text="{Binding Count}" />
</TextBlock>
我不再需要这个 TextBlock,因为我可以直接在(主)StatusBar 中引用 DataContext 属性:
<StatusBar DockPanel.Dock="Bottom">
Item <TextBlock Text="{Binding ElementName=vwToDo, Path=DataContext.CurrentPosition}" />
Of <TextBlock Text="{Binding ElementName=vwToDo, Path=DataContext.Count}" />
</StatusBar>
问题与解决方案
按照@JMarsch 的回答:命名我的属性CurrentPosition 是在屏蔽已经直接从DataContext 中可用的同名属性,因为绑定是到集合的默认视图(具有此属性)。
解决方案是重命名为MyCurrentPosition,并从状态栏中引用原始属性,或者像我一样,完全删除我的这个属性版本(以及GetDefaultView):他们没有这样做任何特别有用的东西。
然后我使用以下简单的 ValueConverter 将 StatusBar 中的 0,1,2,.. 转换为 1,2,3,..。
[ValueConversion(typeof(int), typeof(int))]
class PositionConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
return (int)value + 1;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
return (int)value - 1;
}
}
状态栏:
<StatusBar DockPanel.Dock="Bottom" x:Name="status">
Item <TextBlock Text="{Binding ElementName=vwToDo,
Path=DataContext.CurrentPosition, Converter={StaticResource posConverter}}" />
Of <TextBlock Text="{Binding ElementName=vwToDo, Path=DataContext.Count}" />
</StatusBar>
【问题讨论】:
-
基础集合的 Xaml 是否声明它与当前项同步?将此设置为 true 会对集合视图产生有益的副作用,可能会给您带来更好的结果。
-
IsSynchronizedWithCurrentItem适用于ItemsControl,例如 ListBox。我没有使用其中之一,因此该属性不可用。 (我使用 Next,Previous 按钮在项目之间移动。) -
感谢您的链接,但我认为没有什么可以用于我的情况。