Skip to content

Commit ab1d60f

Browse files
Merge pull request #63 from CodebreakerApp/51-navigation
WinUI Navigation and InfoBar
2 parents ae050f1 + a2e6383 commit ab1d60f

55 files changed

Lines changed: 555 additions & 1080 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/CodeBreaker.WinUI.sln

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,23 @@ VisualStudioVersion = 17.7.33927.210
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Codebreaker.WinUI", "Codebreaker.WinUI\Codebreaker.WinUI.csproj", "{A518C63C-0864-4DF0-A0F6-2870441B54A5}"
77
EndProject
8+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Codebreaker.ViewModels", "Codebreaker.ViewModels\Codebreaker.ViewModels.csproj", "{339FEC3B-AF6D-49BF-9EF7-A44799CCBCDF}"
9+
EndProject
810
Global
911
GlobalSection(SolutionConfigurationPlatforms) = preSolution
12+
Debug|Any CPU = Debug|Any CPU
1013
Debug|arm64 = Debug|arm64
1114
Debug|x64 = Debug|x64
1215
Debug|x86 = Debug|x86
16+
Release|Any CPU = Release|Any CPU
1317
Release|arm64 = Release|arm64
1418
Release|x64 = Release|x64
1519
Release|x86 = Release|x86
1620
EndGlobalSection
1721
GlobalSection(ProjectConfigurationPlatforms) = postSolution
22+
{A518C63C-0864-4DF0-A0F6-2870441B54A5}.Debug|Any CPU.ActiveCfg = Debug|x64
23+
{A518C63C-0864-4DF0-A0F6-2870441B54A5}.Debug|Any CPU.Build.0 = Debug|x64
24+
{A518C63C-0864-4DF0-A0F6-2870441B54A5}.Debug|Any CPU.Deploy.0 = Debug|x64
1825
{A518C63C-0864-4DF0-A0F6-2870441B54A5}.Debug|arm64.ActiveCfg = Debug|arm64
1926
{A518C63C-0864-4DF0-A0F6-2870441B54A5}.Debug|arm64.Build.0 = Debug|arm64
2027
{A518C63C-0864-4DF0-A0F6-2870441B54A5}.Debug|arm64.Deploy.0 = Debug|arm64
@@ -24,6 +31,9 @@ Global
2431
{A518C63C-0864-4DF0-A0F6-2870441B54A5}.Debug|x86.ActiveCfg = Debug|x86
2532
{A518C63C-0864-4DF0-A0F6-2870441B54A5}.Debug|x86.Build.0 = Debug|x86
2633
{A518C63C-0864-4DF0-A0F6-2870441B54A5}.Debug|x86.Deploy.0 = Debug|x86
34+
{A518C63C-0864-4DF0-A0F6-2870441B54A5}.Release|Any CPU.ActiveCfg = Release|x64
35+
{A518C63C-0864-4DF0-A0F6-2870441B54A5}.Release|Any CPU.Build.0 = Release|x64
36+
{A518C63C-0864-4DF0-A0F6-2870441B54A5}.Release|Any CPU.Deploy.0 = Release|x64
2737
{A518C63C-0864-4DF0-A0F6-2870441B54A5}.Release|arm64.ActiveCfg = Release|arm64
2838
{A518C63C-0864-4DF0-A0F6-2870441B54A5}.Release|arm64.Build.0 = Release|arm64
2939
{A518C63C-0864-4DF0-A0F6-2870441B54A5}.Release|arm64.Deploy.0 = Release|arm64
@@ -33,6 +43,22 @@ Global
3343
{A518C63C-0864-4DF0-A0F6-2870441B54A5}.Release|x86.ActiveCfg = Release|x86
3444
{A518C63C-0864-4DF0-A0F6-2870441B54A5}.Release|x86.Build.0 = Release|x86
3545
{A518C63C-0864-4DF0-A0F6-2870441B54A5}.Release|x86.Deploy.0 = Release|x86
46+
{339FEC3B-AF6D-49BF-9EF7-A44799CCBCDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
47+
{339FEC3B-AF6D-49BF-9EF7-A44799CCBCDF}.Debug|Any CPU.Build.0 = Debug|Any CPU
48+
{339FEC3B-AF6D-49BF-9EF7-A44799CCBCDF}.Debug|arm64.ActiveCfg = Debug|Any CPU
49+
{339FEC3B-AF6D-49BF-9EF7-A44799CCBCDF}.Debug|arm64.Build.0 = Debug|Any CPU
50+
{339FEC3B-AF6D-49BF-9EF7-A44799CCBCDF}.Debug|x64.ActiveCfg = Debug|Any CPU
51+
{339FEC3B-AF6D-49BF-9EF7-A44799CCBCDF}.Debug|x64.Build.0 = Debug|Any CPU
52+
{339FEC3B-AF6D-49BF-9EF7-A44799CCBCDF}.Debug|x86.ActiveCfg = Debug|Any CPU
53+
{339FEC3B-AF6D-49BF-9EF7-A44799CCBCDF}.Debug|x86.Build.0 = Debug|Any CPU
54+
{339FEC3B-AF6D-49BF-9EF7-A44799CCBCDF}.Release|Any CPU.ActiveCfg = Release|Any CPU
55+
{339FEC3B-AF6D-49BF-9EF7-A44799CCBCDF}.Release|Any CPU.Build.0 = Release|Any CPU
56+
{339FEC3B-AF6D-49BF-9EF7-A44799CCBCDF}.Release|arm64.ActiveCfg = Release|Any CPU
57+
{339FEC3B-AF6D-49BF-9EF7-A44799CCBCDF}.Release|arm64.Build.0 = Release|Any CPU
58+
{339FEC3B-AF6D-49BF-9EF7-A44799CCBCDF}.Release|x64.ActiveCfg = Release|Any CPU
59+
{339FEC3B-AF6D-49BF-9EF7-A44799CCBCDF}.Release|x64.Build.0 = Release|Any CPU
60+
{339FEC3B-AF6D-49BF-9EF7-A44799CCBCDF}.Release|x86.ActiveCfg = Release|Any CPU
61+
{339FEC3B-AF6D-49BF-9EF7-A44799CCBCDF}.Release|x86.Build.0 = Release|Any CPU
3662
EndGlobalSection
3763
GlobalSection(SolutionProperties) = preSolution
3864
HideSolutionNode = FALSE
Lines changed: 21 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using System.Windows.Input;
2-
3-
namespace Codebreaker.ViewModels;
1+
namespace Codebreaker.ViewModels.Components;
42

53
public enum InfoMessageSeverity
64
{
@@ -10,82 +8,38 @@ public enum InfoMessageSeverity
108
Error
119
}
1210

13-
public partial class InfoMessageViewModel : ObservableObject
11+
public partial class InfoMessageViewModel(Action closeAction) : ObservableObject
1412
{
15-
public static InfoMessageViewModel Error(string content)
16-
{
17-
InfoMessageViewModel message = new()
18-
{
19-
Title = "Error",
20-
Message = content,
21-
Severity = InfoMessageSeverity.Error,
22-
ActionTitle = "OK"
23-
};
24-
message.ActionCommand = new RelayCommand(() => message.Close());
25-
return message;
26-
}
27-
28-
public static InfoMessageViewModel Warning(string content)
29-
{
30-
InfoMessageViewModel message = new()
31-
{
32-
Title = "Warning",
33-
Message = content,
34-
Severity = InfoMessageSeverity.Warning,
35-
ActionTitle = "OK"
36-
};
37-
message.ActionCommand = new RelayCommand(() => message.Close());
38-
return message;
39-
}
40-
41-
public static InfoMessageViewModel Information(string content)
42-
{
43-
InfoMessageViewModel message = new()
44-
{
45-
Title = "Information",
46-
Message = content,
47-
Severity = InfoMessageSeverity.Info,
48-
ActionTitle = "OK"
49-
};
50-
message.ActionCommand = new RelayCommand(() => message.Close());
51-
return message;
52-
}
53-
54-
public static InfoMessageViewModel Success(string content)
55-
{
56-
InfoMessageViewModel message = new()
57-
{
58-
Title = "Success",
59-
Message = content,
60-
Severity = InfoMessageSeverity.Success,
61-
ActionTitle = "OK"
62-
};
63-
message.ActionCommand = new RelayCommand(() => message.Close());
64-
return message;
65-
}
66-
67-
internal ICollection<InfoMessageViewModel>? ContainingCollection { get; set; }
68-
6913
[ObservableProperty]
70-
private InfoMessageSeverity _severity = InfoMessageSeverity.Info;
14+
private InfoMessageSeverity _severity;
7115

7216
[ObservableProperty]
7317
private string _message = string.Empty;
7418

7519
[ObservableProperty]
76-
private string _title = string.Empty;
20+
private string? _title;
7721

7822
[ObservableProperty]
7923
[NotifyPropertyChangedFor(nameof(HasAction))]
80-
private ICommand? _actionCommand;
24+
[NotifyCanExecuteChangedFor(nameof(ExecuteActionCommand))]
25+
private Action? _action;
26+
27+
[RelayCommand]
28+
public void ExecuteAction() => Action?.Invoke();
8129

8230
[ObservableProperty]
8331
[NotifyPropertyChangedFor(nameof(HasAction))]
84-
private string? _actionTitle = "OK";
32+
private string? _actionText = "OK";
8533

86-
public bool HasAction =>
87-
ActionCommand is not null && ActionTitle is not null;
34+
public bool HasAction => ExecuteActionCommand is not null && ActionText is not null;
8835

89-
public void Close() =>
90-
ContainingCollection?.Remove(this);
91-
}
36+
[ObservableProperty]
37+
[NotifyCanExecuteChangedFor(nameof(CloseCommand))]
38+
private Action _closeAction = closeAction;
39+
40+
[ObservableProperty]
41+
private bool _isClosable;
42+
43+
[RelayCommand]
44+
public void Close() => CloseAction();
45+
}

src/Codebreaker.ViewModels/Components/SelectedFieldViewModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace Codebreaker.ViewModels;
1+
namespace Codebreaker.ViewModels.Components;
22

33
public partial class SelectedFieldViewModel : ObservableObject
44
{
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using Codebreaker.ViewModels.Components;
2+
3+
namespace Codebreaker.ViewModels.Contracts.Services;
4+
5+
public interface IInfoBarService
6+
{
7+
ObservableCollection<InfoMessageViewModel> Messages { get; }
8+
9+
InfoMessageBuilder New { get; }
10+
11+
void Clear();
12+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace Codebreaker.ViewModels.Contracts.Services;
2+
3+
public interface INavigationService
4+
{
5+
bool CanGoBack { get; }
6+
7+
ValueTask<bool> NavigateToAsync(string key, object? parameter = null, bool clearNavigation = false);
8+
9+
ValueTask<bool> GoBackAsync();
10+
}

src/Codebreaker.ViewModels/Pages/GamePageViewModel.cs

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using Microsoft.Extensions.Options;
1+
using Codebreaker.ViewModels.Components;
2+
using Codebreaker.ViewModels.Contracts.Services;
3+
using Microsoft.Extensions.Options;
24

35
namespace Codebreaker.ViewModels;
46

@@ -18,29 +20,30 @@ public enum GameMoveValue
1820
}
1921

2022
/// <summary>
21-
/// Configure to enable dialogs (via <see cref="IDialogService"/>), or use the <see cref="InfoBarMessageService"/>.
23+
/// Configure to enable dialogs (via <see cref="IDialogService"/>), or use the <see cref="InfoBarService"/>.
2224
/// </summary>
2325
public class GamePageViewModelOptions
2426
{
25-
public bool EnableDialogs { get; set; } = false;
2627
}
2728

2829
public partial class GamePageViewModel : ObservableObject
2930
{
3031
private readonly IGamesClient _client;
3132
private int _moveNumber = 0;
3233

33-
private readonly bool _enableDialogs = false;
3434
private readonly IDialogService _dialogService;
35+
private readonly IInfoBarService _infoBarService;
3536

3637
public GamePageViewModel(
3738
IGamesClient client,
3839
IOptions<GamePageViewModelOptions> options,
39-
IDialogService dialogService)
40+
IDialogService dialogService,
41+
IInfoBarService infoBarService
42+
)
4043
{
4144
_client = client;
4245
_dialogService = dialogService;
43-
_enableDialogs = options.Value.EnableDialogs;
46+
_infoBarService = infoBarService;
4447

4548
PropertyChanged += (sender, e) =>
4649
{
@@ -52,13 +55,13 @@ public GamePageViewModel(
5255
/// <summary>
5356
/// Information on the game - messages, errors, etc. See <see cref="InfoBarMessageService"/>.
5457
/// </summary>
55-
public InfoBarMessageService InfoBarMessageService { get; } = new();
58+
public InfoBarService InfoBarMessageService { get; } = new();
5659

57-
private Models.Game? _game;
60+
private Game? _game;
5861
/// <summary>
5962
/// <see cref="Models.Game"/> instance."/>
6063
/// </summary>
61-
public Models.Game? Game
64+
public Game? Game
6265
{
6366
get => _game;
6467
set
@@ -141,16 +144,15 @@ private async Task StartGameAsync()
141144
}
142145
catch (Exception ex)
143146
{
144-
InfoMessageViewModel message = InfoMessageViewModel.Error(ex.Message);
145-
message.ActionCommand = new RelayCommand(() =>
146-
{
147-
GameStatus = GameMode.NotRunning;
148-
message.Close();
149-
});
150-
InfoBarMessageService.ShowMessage(message);
151-
152-
if (_enableDialogs)
153-
await _dialogService.ShowMessageAsync(ex.Message);
147+
_infoBarService.New
148+
.IsErrorMessage()
149+
.WithMessage(ex.Message)
150+
.WithAction((message) =>
151+
{
152+
GameStatus = GameMode.NotRunning;
153+
message.Close();
154+
})
155+
.Show();
154156
}
155157
finally
156158
{
@@ -217,26 +219,17 @@ private async Task SetMoveAsync()
217219
if (isVictory)
218220
{
219221
GameStatus = GameMode.Won;
220-
InfoBarMessageService.ShowInformation("Congratulations - you won!");
221-
222-
if (_enableDialogs)
223-
await _dialogService.ShowMessageAsync("Congratulations - you won!");
222+
InfoBarMessageService.New.IsSuccessMessage().WithMessage("Congratulations - you won!").Show();
224223
}
225224
else if (ended)
226225
{
227226
GameStatus = GameMode.Lost;
228-
InfoBarMessageService.ShowInformation("Sorry, you didn't find the matching colors!");
229-
230-
if (_enableDialogs)
231-
await _dialogService.ShowMessageAsync("Sorry, you didn't find the matching colors!");
227+
InfoBarMessageService.New.WithMessage("Sorry, you didn't find the matching colors!").Show();
232228
}
233229
}
234230
catch (Exception ex)
235231
{
236-
InfoBarMessageService.ShowError(ex.Message);
237-
238-
if (_enableDialogs)
239-
await _dialogService.ShowMessageAsync(ex.Message);
232+
InfoBarMessageService.New.WithMessage(ex.Message).IsErrorMessage().Show();
240233
}
241234
finally
242235
{

src/Codebreaker.ViewModels/Services/InfoBarMessageService.cs

Lines changed: 0 additions & 23 deletions
This file was deleted.

0 commit comments

Comments
 (0)