我有一个ObservableCollection,它是Grid的DataContext。该Grid是插入到主窗口中的用户控件。
我想在状态栏中显示“第x条记录,共y条记录”,因此作为第一步,我尝试使用以下XAML在Grid中显示它:
<TextBlock Grid.Row="2" Grid.Column="3">
<TextBlock Text="{Binding CurrentPosition}" /> <TextBlock Text="{Binding Count}" />
</TextBlock>
计数器可以正常工作,并在添加新项时自动更新。我的代码中定义的CurrentPosition始终保持为0。
如何使CurrentPosition自动更新?我希望不必使用INotify**,因为这已经是一个ObservableCollection了。
我也没有任何代码后台处理,所以我希望可以在我的类(或模型)和XAML中实现。
我尝试过使用CurrentChanged,但没有成功:
public MyObservableCollection() : base() {
this.GetDefaultView().CurrentChanged += MyObservableCollection_CurrentChanged;
}
我的可观察集合:
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
,并从 StatusBar 引用原始属性; 或者像我一样,彻底删除这个属性 (以及 GetDefaultView
),它们没有做任何特别有用的事情。
然后,我使用以下简单的ValueConverter将 0、1、2、.. 转换为 StatusBar 中的 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>
IsSynchronizedWithCurrentItem
适用于像ListBox这样的ItemsControl
。我没有使用这些控件,所以该属性不可用。(我使用“下一个”、“上一个”按钮在项之间移动。) - Andy G