In the upcoming version, I will address
- Advanced navigation
- More "candy" UI
- Save the drawing to image
public abstract class MoveCommand : ICommand { protected readonly MainPageViewModel ViewModel; protected MoveCommand(MainPageViewModel viewModel) { ViewModel = viewModel; } public bool CanExecute(object parameter) { return true; } public abstract void Execute(object parameter); public event EventHandler CanExecuteChanged; } public class MoveNextCommand : MoveCommand { public MoveNextCommand(MainPageViewModel viewModel) : base(viewModel) { } public override void Execute(object parameter) { ViewModel.MoveNext(); } } public class MovePreviousCommand : MoveCommand { public MovePreviousCommand(MainPageViewModel viewModel) : base(viewModel) { } public override void Execute(object parameter) { ViewModel.MovePrevious(); } }The most important thing is CanExecute() always return true, and I removed RaiseCanExecuteChanged() as I don't need it anymore.
public class MainPageViewModel : INotifyPropertyChanged { private readonly ListIn additional raising the PropterChanged event for Number property, I also raise for CanMovePrevious and CanMoveNext property. These properties are used to bind to the XAML as below_numbers; private int _currentIndex; public int Number { get { return _numbers[_currentIndex]; } } public ICommand MovePreviousCommand { get; private set; } public ICommand MoveNextCommand { get; private set; } public MainPageViewModel() { _numbers = new List {1, 2, 3}; _currentIndex = 0; MovePreviousCommand = new MovePreviousCommand(this); MoveNextCommand = new MoveNextCommand(this); } public bool CanMovePrevious { get { return _currentIndex > 0; } } public bool CanMoveNext { get { return _currentIndex < _numbers.Count - 1; } } public void MovePrevious() { if (CanMovePrevious) { _currentIndex--; PropertyChanged(this, new PropertyChangedEventArgs("Number")); PropertyChanged(this, new PropertyChangedEventArgs("CanMovePrevious")); PropertyChanged(this, new PropertyChangedEventArgs("CanMoveNext")); } } public void MoveNext() { if (CanMoveNext) { _currentIndex++; PropertyChanged(this, new PropertyChangedEventArgs("Number")); PropertyChanged(this, new PropertyChangedEventArgs("CanMovePrevious")); PropertyChanged(this, new PropertyChangedEventArgs("CanMoveNext")); } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); } }
<StackPanel Background="{StaticResource ApplicationPageBackgroundThemeBrush}" HorizontalAlignment="Center" Orientation="Horizontal" VerticalAlignment="Center"> </StackPanel>The last thing I need to do is removing all redundant code from code behind file.
public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); DataContext = new MainPageViewModel(); } ///Now the app is written with pure MVVM./// Invoked when this page is about to be displayed in a Frame. /// /// Event data that describes how this page was reached. The Parameter /// property is typically used to configure the page. protected override void OnNavigatedTo(NavigationEventArgs e) { } }
The most important parts are binding commands and the number to display from the view model.
public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); DataContext = new MainPageViewModel(); } ///Now are the commands. I create a abstract MoveCommand which implement ICommand to share CanExecuteChanged and RaiseCanExecuteChanged()/// Invoked when this page is about to be displayed in a Frame. /// /// Event data that describes how this page was reached. The Parameter /// property is typically used to configure the page. protected override void OnNavigatedTo(NavigationEventArgs e) { } }
public abstract class MoveCommand : ICommand { public abstract bool CanExecute(object parameter); public abstract void Execute(object parameter); public event EventHandler CanExecuteChanged; public void RaiseCanExecuteChanged() { if (CanExecuteChanged != null) { CanExecuteChanged(this, EventArgs.Empty); } } }The MoveNextCommand and MovePreviousCommand extend MoveCommand and provide implementation for abstract methods.
public class MoveNextCommand : MoveCommand { private readonly MainPageViewModel _viewModel; public MoveNextCommand(MainPageViewModel viewModel) { _viewModel = viewModel; } public override bool CanExecute(object parameter) { return _viewModel.CanMoveNext; } public override void Execute(object parameter) { _viewModel.MoveNext(); RaiseCanExecuteChanged(); } } public class MovePreviousCommand : MoveCommand { private readonly MainPageViewModel _viewModel; public MovePreviousCommand(MainPageViewModel viewModel) { _viewModel = viewModel; } public override bool CanExecute(object parameter) { return _viewModel.CanMovePrevious; } public override void Execute(object parameter) { _viewModel.MovePrevious(); RaiseCanExecuteChanged(); } }The last thing is the view model class
public class MainPageViewModel : INotifyPropertyChanged { private readonly ListWhen MoveNextCommand or MovePreviousCommand is fired, the appropriate method (MoveNext() or MovePrevious()) is called. The method checks if it's possible to move then increase or decrease the index and fire the ProperyChanged event to notify the UI to update the label's text._numbers; private int _currentIndex; public int Number { get { return _numbers[_currentIndex]; } } public ICommand MovePreviousCommand { get; private set; } public ICommand MoveNextCommand { get; private set; } public MainPageViewModel() { _numbers = new List {1, 2, 3}; _currentIndex = 0; MovePreviousCommand = new MovePreviousCommand(this); MoveNextCommand = new MoveNextCommand(this); } public bool CanMovePrevious { get { return _currentIndex > 0; } } public bool CanMoveNext { get { return _currentIndex < _numbers.Count - 1; } } public void MovePrevious() { if (CanMovePrevious) { _currentIndex--; PropertyChanged(this, new PropertyChangedEventArgs("Number")); } } public void MoveNext() { if (CanMoveNext) { _currentIndex++; PropertyChanged(this, new PropertyChangedEventArgs("Number")); } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); } }
public sealed partial class MainPage : Page { private readonly MainPageViewModel _viewModel; public MainPage() { this.InitializeComponent(); _viewModel = new MainPageViewModel(); _viewModel.MovePreviousCommand.CanExecuteChanged += MovePreviousCommand_CanExecuteChanged; _viewModel.MoveNextCommand.CanExecuteChanged += MoveNextCommand_CanExecuteChanged; DataContext = _viewModel; } void MoveNextCommand_CanExecuteChanged(object sender, EventArgs e) { if (((CancelEventArgs)e).Cancel) { return; } ((MoveCommand)_viewModel.MovePreviousCommand).RaiseCanExecuteChanged(true); } void MovePreviousCommand_CanExecuteChanged(object sender, EventArgs e) { if (((CancelEventArgs)e).Cancel) { return; } ((MoveCommand)_viewModel.MoveNextCommand).RaiseCanExecuteChanged(true); } ///Here I use CancelEventArgs instead of EventArgs to stop firing event at the right time. Otherwise it can lead to StackOverflowException./// Invoked when this page is about to be displayed in a Frame. /// /// Event data that describes how this page was reached. The Parameter /// property is typically used to configure the page. protected override void OnNavigatedTo(NavigationEventArgs e) { } } public abstract class MoveCommand : ICommand { public abstract bool CanExecute(object parameter); public abstract void Execute(object parameter); public event EventHandler CanExecuteChanged; public void RaiseCanExecuteChanged(bool cancel) { if (CanExecuteChanged != null) { CanExecuteChanged(this, new CancelEventArgs {Cancel = cancel}); } } } public class MoveNextCommand : MoveCommand { private readonly MainPageViewModel _viewModel; public MoveNextCommand(MainPageViewModel viewModel) { _viewModel = viewModel; } public override bool CanExecute(object parameter) { return _viewModel.CanMoveNext; } public override void Execute(object parameter) { _viewModel.MoveNext(); RaiseCanExecuteChanged(!_viewModel.CanMoveNext); } } public class MovePreviousCommand : MoveCommand { private readonly MainPageViewModel _viewModel; public MovePreviousCommand(MainPageViewModel viewModel) { _viewModel = viewModel; } public override bool CanExecute(object parameter) { return _viewModel.CanMovePrevious; } public override void Execute(object parameter) { _viewModel.MovePrevious(); RaiseCanExecuteChanged(!_viewModel.CanMovePrevious); } }