状态栏记录x (y)
本文关键字:记录 状态栏 | 更新日期: 2023-09-27 18:03:26
我有一个ObservableCollection,这是一个网格的数据上下文。网格是插入到主窗口中的用户控件。
我想在状态栏中显示"记录y的x",因此,作为第一步,我试图使用这个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可用。(我也不知道为什么我需要重新通知计数的变化。)
更新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
}
}
这样我可以在网格中显示"Item 1 of 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虽然,因为我可以直接参考DataContext属性在(主)StatusBar
:
<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来转换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 总是数据绑定到集合视图,而不是集合本身。因此,即使您的XAML似乎绑定到集合,在运行时,您实际上是绑定到默认的集合视图。
这是一个很酷的副作用:一个集合视图已经有一个CurrentPosition属性。我认为事情对你来说是坏的,因为你无意中干预了你的收藏。
下面是一个非常快速和粗糙的小程序,它演示了一个绑定到currentposition和Count的工作,而没有在集合上定义currentposition(因为在幕后,你实际上是绑定到CollectionView,并且它已经有了一个currentposition属性来通知变化)。
运行这个程序,注意当您单击增量按钮时,UI会相应地更新。
这里是XAML:<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel x:Name="ContentPanel">
<Button x:Name="IncrementButton" Content="Increment" Click="IncrementButton_Click"/>
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="CurrentPositionTextBlock" Text="{Binding CurrentPosition}"/>
<TextBlock Text=" / "/>
<TextBlock Text="{Binding Count}"/>
</StackPanel>
</StackPanel>
</Window>
下面是代码:
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Data;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Collection = new TestObservableCollection<object>() {new object(), new object(), new object()};
this.ContentPanel.DataContext = this.Collection;
}
public TestObservableCollection<object> Collection { get; set; }
private void IncrementButton_Click(object sender, RoutedEventArgs e)
{
this.Collection.GetDefaultView().MoveCurrentToNext();
}
}
public class TestObservableCollection<T> : ObservableCollection<T>
{
public ICollectionView GetDefaultView()
{
return CollectionViewSource.GetDefaultView(this);
}
}
}
这里有更多的阅读给你:
http://msdn.microsoft.com/en-us/library/ms752347 (v = vs.110) . aspx
http://msdn.microsoft.com/en-us/library/system.windows.data.collectionviewsource (v = vs.110) . aspx
编辑
如果您想要更多的证据,将下面的代码粘贴到我的button-click处理程序中——您将看到文本框绑定到的实际对象类型是ListCollectionView,而不是实际的集合:
System.Diagnostics.Debug.WriteLine(this.CurrentPositionTextBlock.GetBindingExpression(TextBlock.TextProperty).ResolvedSource.GetType().FullName);
你的Count绑定正在更新,因为当添加和删除项时,基本ObservableCollection<T>
会引发PropertyChanged
。
当您重新定位当前记录指针时,您的定位代码需要引发PropertyChanged
,以便绑定子系统知道重新查询属性,这就是INotifyPropertyChanged
存在的原因。不过,我可能会这样写。注意,使用ObservableCollections<T>
的OnPropertyChanged
从继承树中已经实现的INotifyPropertyChanged中引发正确的事件。
public void MoveFirst() {
this.GetDefaultView().MoveCurrentToFirst();
OnPropertyChanged(new PropertyChangedEventArgs("CurrentPosition"));
}
public void MovePrevious() {
this.GetDefaultView().MoveCurrentToPrevious();
OnPropertyChanged(new PropertyChangedEventArgs("CurrentPosition"));
}
public void MoveNext() {
this.GetDefaultView().MoveCurrentToNext();
OnPropertyChanged(new PropertyChangedEventArgs("CurrentPosition"));
}
public void MoveLast() {
this.GetDefaultView().MoveCurrentToLast();
OnPropertyChanged(new PropertyChangedEventArgs("CurrentPosition"));
}