In the absence of something slick like "IsCurrentFirst" and "IsCurrentLast"
Simple enough to create some extension methods on the ICollectionView
abstraction to provide the desired functionality
public static class CollectionViewExtensions {
public static bool IsCurrentFirst(this ICollectionView view) {
return view.CurrentItem != null && view.CurrentPosition == 0;
}
public static bool IsCurrentLast(this ICollectionView view) {
if (view.CurrentItem == null) return false;
var index = view.CurrentPosition;
var max = view.Count() - 1;
return index == max;
}
public static bool CanMoveCurrentToNext(this ICollectionView view) {
return !view.IsCurrentLast();
}
public static bool CanMoveCurrentToPrevious(this ICollectionView view) {
return !view.IsCurrentFirst();
}
static int Count(this ICollectionView source) {
int count = 0;
var e = source.GetEnumerator();
checked {
while (e.MoveNext()) count++;
}
return count;
}
}
The extension methods should now allow for such checks.
Creating some derived ICommand
implementations that can be hooked directly to previous and next buttons.
MoveCurrentToNextCommand
public class MoveCurrentToNextCommand : ICommand {
private readonly ICollectionView view;
public MoveCurrentToNextCommand(ICollectionView view) {
this.view = view;
this.view.CurrentChanged += (s, e) => {
CanExecuteChanged(this, EventArgs.Empty);
};
}
public event EventHandler CanExecuteChanged = delegate { };
public bool CanExecute(object parameter = null) => view.CanMoveCurrentToNext();
public void Execute(object parameter = null) {
if (CanExecute(parameter))
view.MoveCurrentToNext();
}
}
MoveCurrentToPreviousCommand
public class MoveCurrentToPreviousCommand : ICommand {
private readonly ICollectionView view;
public MoveCurrentToPreviousCommand(ICollectionView view) {
this.view = view;
this.view.CurrentChanged += (s, e) => {
CanExecuteChanged(this, EventArgs.Empty);
};
}
public event EventHandler CanExecuteChanged = delegate { };
public bool CanExecute(object parameter = null) => view.CanMoveCurrentToPrevious();
public void Execute(object parameter = null) {
if (CanExecute(parameter))
view.MoveCurrentToPrevious();
}
}
This simplifies binding with commands in a view model
public ICommand Next => new MoveCurrentToNextCommand(MyCollectionViewSource.View);
public ICommand Previous => new MoveCurrentToPreviousCommand(MyCollectionViewSource.View);
Here are some unit tests on the commands for good measure.
[TestClass]
public class CollectionViewCommandsTests {
[TestMethod]
public void Should_Not_Move_Previous() {
//Arrange
var items = new[] { new object(), new object(), new object() };
var view = new CollectionView(items);
var expected = view.CurrentItem;
bool changed = false;
ICommand command = new MoveCurrentToPreviousCommand(view);
command.CanExecuteChanged += delegate {
changed = true;
};
//Act
command.Execute(null);
//Assert
var actual = view.CurrentItem;
actual.Should().Be(expected);
changed.Should().BeFalse();
}
[TestMethod]
public void Should_Move_Next() {
//Arrange
var items = new[] { new object(), new object(), new object() };
var view = new CollectionView(items);
var expected = items[1];
bool changed = false;
ICommand command = new MoveCurrentToNextCommand(view);
command.CanExecuteChanged += delegate {
changed = true;
};
//Act
command.Execute(null);
//Assert
var actual = view.CurrentItem;
actual.Should().Be(expected);
changed.Should().BeTrue();
}
[TestMethod]
public void Should_Not_Move_Next() {
//Arrange
var items = new[] { new object(), new object(), new object() };
var view = new CollectionView(items);
view.MoveCurrentToLast();
var expected = view.CurrentItem;
bool changed = false;
ICommand command = new MoveCurrentToNextCommand(view);
command.CanExecuteChanged += delegate {
changed = true;
};
//Act
command.Execute(null);
//Assert
var actual = view.CurrentItem;
actual.Should().Be(expected);
changed.Should().BeFalse();
}
[TestMethod]
public void Should_Move_Previous() {
//Arrange
var items = new[] { new object(), new object(), new object() };
var view = new CollectionView(items);
view.MoveCurrentToLast();
var expected = items[1];
bool changed = false;
ICommand command = new MoveCurrentToPreviousCommand(view);
command.CanExecuteChanged += delegate {
changed = true;
};
//Act
command.Execute(null);
//Assert
var actual = view.CurrentItem;
actual.Should().Be(expected);
changed.Should().BeTrue();
}
}