Vereinsmeisterschaften  22aa7800eae54b428d40e835886cefe1fdefdfdf
This is a software that can be used to manage the internal competition of the swimming club Illertissen called "Vereinsmeisterschaften".
Loading...
Searching...
No Matches
ShellViewModel.cs
1using System.Collections.ObjectModel;
2using System.ComponentModel;
3using System.Windows.Input;
4using System.Windows.Threading;
5using CommunityToolkit.Mvvm.ComponentModel;
6using CommunityToolkit.Mvvm.Input;
7using MahApps.Metro.Controls;
8using MahApps.Metro.Controls.Dialogs;
12
14
18public partial class ShellViewModel : ObservableObject
19{
23 public string CurrentWorkspaceFolder => _workspaceService.PersistentPath;
24
28 public bool HasUnsavedChanges => _workspaceService.HasUnsavedChanges;
29
33 [ObservableProperty]
35
36 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
37
38 private readonly INavigationService _navigationService;
39 private IDialogCoordinator _dialogCoordinator;
40 private IWorkspaceService _workspaceService;
41
42 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
43
44 #region Setting Keys
45
46 private const string SETTING_KEY_LAST_WORKSPACE_PATHS = "LastWorkspacePaths";
47 private const string SETTING_KEY_LAST_WORKSPACE_FOLDER = "LastWorkspaceFolder";
48
49 #endregion
50
51 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
52
53 #region Hamburger menu items
54
58 [ObservableProperty]
59 private HamburgerMenuItem _selectedMenuItem;
60
64 [ObservableProperty]
65 private HamburgerMenuItem _selectedOptionsMenuItem;
66
70 public ObservableCollection<HamburgerMenuItem> MenuItems { get; } = new ObservableCollection<HamburgerMenuItem>()
71 {
72 new HamburgerMenuGlyphItem() { Label = Resources.ShellMainPage, Glyph = "\uE80F", TargetPageType = typeof(MainViewModel) },
73 new HamburgerMenuGlyphItem() { Label = Resources.ShellWorkspacePage, Glyph = "\uE821", TargetPageType = typeof(WorkspaceViewModel) },
74 new HamburgerMenuGlyphItem() { Label = Resources.ShellCompetitionPage, Glyph = "\uF0E3", TargetPageType = typeof(CompetitionViewModel) },
75 new HamburgerMenuGlyphItem() { Label = Resources.ShellPeoplePage, Glyph = "\uE77B", TargetPageType = typeof(PeopleViewModel) },
76 new HamburgerMenuGlyphItem() { Label = Resources.ShellPrepareRacesPage, Glyph = "\uE7C1", TargetPageType = typeof(PrepareRacesViewModel) },
77 new HamburgerMenuGlyphItem() { Label = Resources.ShellTimeInputPage, Glyph = "\uE916", TargetPageType = typeof(TimeInputViewModel) },
78 new HamburgerMenuGlyphItem() { Label = Resources.ShellResultsPage, Glyph = "\uE9F9", TargetPageType = typeof(ResultsViewModel) },
79 new HamburgerMenuGlyphItem() { Label = Resources.ShellAnalyticsPage, Glyph = "\uE9D2", TargetPageType = typeof(AnalyticsViewModel) },
80 new HamburgerMenuGlyphItem() { Label = Resources.ShellCreateDocumentsPage, Glyph = "\uE8A5", TargetPageType = typeof(CreateDocumentsViewModel) },
81 };
82
86 public ObservableCollection<HamburgerMenuItem> OptionMenuItems { get; } = new ObservableCollection<HamburgerMenuItem>()
87 {
88 new HamburgerMenuGlyphItem() { Label = Resources.ShellSettingsPage, Glyph = "\uE713", TargetPageType = typeof(SettingsViewModel) }
89 };
90
91 #endregion
92
93 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
94
95 #region Commands
96
97 private RelayCommand _goBackCommand;
98 private ICommand _menuItemInvokedCommand;
99 private ICommand _optionsMenuItemInvokedCommand;
100 private ICommand _loadedCommand;
101 private ICommand _unloadedCommand;
102 private ICommand _closingCommand;
103 private ICommand _saveWorkspaceCommand;
104 private ICommand _openWorkspaceFlyoutCommand;
105
109 public RelayCommand GoBackCommand => _goBackCommand ?? (_goBackCommand = new RelayCommand(() => _navigationService.GoBack(), () => _navigationService.CanGoBack));
110
114 public ICommand MenuItemInvokedCommand => _menuItemInvokedCommand ?? (_menuItemInvokedCommand = new RelayCommand(() => NavigateTo(SelectedMenuItem.TargetPageType)));
115
119 public ICommand OptionsMenuItemInvokedCommand => _optionsMenuItemInvokedCommand ?? (_optionsMenuItemInvokedCommand = new RelayCommand(() => NavigateTo(SelectedOptionsMenuItem.TargetPageType)));
120
124 public ICommand LoadedCommand => _loadedCommand ?? (_loadedCommand = new RelayCommand(OnLoaded));
125
129 public ICommand UnloadedCommand => _unloadedCommand ?? (_unloadedCommand = new RelayCommand(OnUnloaded));
130
134 public ICommand ClosingCommand => _closingCommand ?? (_closingCommand = new RelayCommand<CancelEventArgs>(OnClosing));
135
139 public ICommand SaveWorkspaceCommand => _saveWorkspaceCommand ?? (_saveWorkspaceCommand = new RelayCommand(async () => await _workspaceService?.Save(CancellationToken.None)));
140
144 public ICommand OpenWorkspaceFlyoutCommand => _openWorkspaceFlyoutCommand ?? (_openWorkspaceFlyoutCommand = new RelayCommand(() => IsWorkspaceFlyoutOpen = !IsWorkspaceFlyoutOpen));
145
146 #endregion
147
148 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
149
156 public ShellViewModel(INavigationService navigationService, IDialogCoordinator dialogCoordinator, IWorkspaceService workspaceService)
157 {
158 _navigationService = navigationService;
159 _dialogCoordinator = dialogCoordinator;
160 _workspaceService = workspaceService;
161 }
162
163 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
164
165 #region Load and unload
166
167 private async void OnLoaded()
168 {
169 _navigationService.Navigated += OnNavigated;
170 _workspaceService.PropertyChanged += _workspaceService_PropertyChanged;
172
173 object lastWorkspacePaths = App.Current.Properties[SETTING_KEY_LAST_WORKSPACE_PATHS];
174 if (lastWorkspacePaths != null)
175 {
176 Newtonsoft.Json.Linq.JArray lastWorkspacePathsJArray = lastWorkspacePaths as Newtonsoft.Json.Linq.JArray;
177 List<string> lastWorkspacePathsList = lastWorkspacePathsJArray.ToObject<List<string>>();
178 _workspaceService.LastWorkspacePaths = new ObservableCollection<string>(lastWorkspacePathsList);
179 }
180
181 // Load the workspace
182 try
183 {
184 await _workspaceService.Load(App.Current.Properties[SETTING_KEY_LAST_WORKSPACE_FOLDER]?.ToString(), CancellationToken.None);
185 }
186 catch (Exception ex)
187 {
188 await _dialogCoordinator.ShowMessageAsync(this, Resources.ErrorString, ex.Message);
189 }
190 }
191
192 private void OnUnloaded()
193 {
194 _navigationService.Navigated -= OnNavigated;
195 _workspaceService.PropertyChanged -= _workspaceService_PropertyChanged;
196 }
197
198 private void _workspaceService_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
199 {
200 switch (e.PropertyName)
201 {
202 case nameof(IWorkspaceService.HasUnsavedChanges): OnPropertyChanged(nameof(HasUnsavedChanges)); break;
203 case nameof(IWorkspaceService.PersistentPath): OnPropertyChanged(nameof(CurrentWorkspaceFolder)); break;
204 case nameof(IWorkspaceService.IsWorkspaceOpen):
205 {
206 OnPropertyChanged(nameof(CurrentWorkspaceFolder));
208 break;
209 }
210 default: break;
211 }
212 }
213
218 {
219 foreach (HamburgerMenuItem menuItem in MenuItems)
220 {
221 if (menuItem.TargetPageType == typeof(MainViewModel) || menuItem.TargetPageType == typeof(WorkspaceViewModel)) { continue; }
222
223 menuItem.IsEnabled = _workspaceService.IsWorkspaceOpen;
224 }
225 }
226
227 #endregion
228
229 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
230
231 #region Closing
232
233 // Close behaviour: https://github.com/MahApps/MahApps.Metro/issues/3535
234 private bool _forceClose = false;
235
239 public event EventHandler WindowCloseRequested;
240 protected void OnClosing(CancelEventArgs e)
241 {
242 // force method to abort - we'll force a close explicitly
243 e.Cancel = true;
244
245 if (_forceClose)
246 {
247 // cleanup code already ran
248 e.Cancel = false;
249 return;
250 }
251
252 // execute shutdown logic - set ForceClose and request Close() to actually close the window
253 Dispatcher.CurrentDispatcher.InvokeAsync(async () =>
254 {
255 bool save = false, cancel = false;
256 (save, cancel) = await CheckForUnsavedChangesAndQueryUserAction();
257 if (!cancel)
258 {
259 if (save)
260 {
261 try
262 {
263 await _workspaceService.Save(CancellationToken.None);
264 }
265 catch (Exception ex)
266 {
267 await _dialogCoordinator.ShowMessageAsync(this, Resources.ErrorString, ex.Message);
268 }
269 }
270 App.Current.Properties[SETTING_KEY_LAST_WORKSPACE_FOLDER] = CurrentWorkspaceFolder;
271 App.Current.Properties[SETTING_KEY_LAST_WORKSPACE_PATHS] = _workspaceService.LastWorkspacePaths.ToList();
272 _forceClose = true;
273 WindowCloseRequested?.Invoke(this, null); // Notify the ShellWindow to close
274 }
275 }, DispatcherPriority.Normal);
276 }
277
282 public async Task<(bool saveOut, bool cancelOut)> CheckForUnsavedChangesAndQueryUserAction()
283 {
284 bool save = false, cancel = false;
285 if (_workspaceService?.HasUnsavedChanges ?? false)
286 {
287 MetroDialogSettings dialogButtonSettings = new MetroDialogSettings()
288 {
289 AffirmativeButtonText = Resources.SaveString,
290 NegativeButtonText = Resources.DontSaveString,
291 FirstAuxiliaryButtonText = Resources.CancelString,
292 DefaultButtonFocus = MessageDialogResult.Affirmative
293 };
294 MessageDialogResult dialogResult = await _dialogCoordinator.ShowMessageAsync(this, Resources.UnsavedChangesString, Resources.UnsavedChangesSavePromptString,
295 MessageDialogStyle.AffirmativeAndNegativeAndSingleAuxiliary, dialogButtonSettings);
296 switch (dialogResult)
297 {
298 case MessageDialogResult.Affirmative: save = true; cancel = false; break;
299 case MessageDialogResult.Negative: save = false; cancel = false; break;
300 case MessageDialogResult.FirstAuxiliary: save = false; cancel = true; break;
301 default: break;
302 }
303 }
304 return (save, cancel);
305 }
306
307 #endregion
308
309 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
310
311 private void NavigateTo(Type targetViewModel)
312 {
313 if (targetViewModel != null)
314 {
315 _navigationService.NavigateTo(targetViewModel.FullName);
316 }
317 }
318
319 private void OnNavigated(object sender, string viewModelName)
320 {
321 var item = MenuItems.OfType<HamburgerMenuItem>().FirstOrDefault(i => viewModelName == i.TargetPageType?.FullName);
322 if (item != null)
323 {
324 SelectedMenuItem = item;
325 }
326 else
327 {
328 SelectedOptionsMenuItem = OptionMenuItems.OfType<HamburgerMenuItem>().FirstOrDefault(i => viewModelName == i.TargetPageType?.FullName);
329 }
330 GoBackCommand.NotifyCanExecuteChanged();
331 }
332}
Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
static string ShellMainPage
Sucht eine lokalisierte Zeichenfolge, die Main ähnelt.
static string CancelString
Sucht eine lokalisierte Zeichenfolge, die Cancel ähnelt.
static string ShellSettingsPage
Sucht eine lokalisierte Zeichenfolge, die Settings ähnelt.
static string ShellWorkspacePage
Sucht eine lokalisierte Zeichenfolge, die Workspace ähnelt.
static string UnsavedChangesString
Sucht eine lokalisierte Zeichenfolge, die Unsaved changes ähnelt.
static string SaveString
Sucht eine lokalisierte Zeichenfolge, die Save ähnelt.
static string UnsavedChangesSavePromptString
Sucht eine lokalisierte Zeichenfolge, die There are unsaved changes.
static string DontSaveString
Sucht eine lokalisierte Zeichenfolge, die Don't save ähnelt.
static string ErrorString
Sucht eine lokalisierte Zeichenfolge, die Error ähnelt.
View model for the main view of the application.
HamburgerMenuItem _selectedMenuItem
Menu item that is currently selected in the hamburger menu.
string CurrentWorkspaceFolder
Path to the current workspace folder.
ICommand SaveWorkspaceCommand
Command to save the current workspace.
RelayCommand GoBackCommand
Command to navigate back in the navigation stack.
ICommand ClosingCommand
Command that is invoked when the shell is closing.
ICommand OpenWorkspaceFlyoutCommand
Command to open the workspace flyout.
bool _isWorkspaceFlyoutOpen
True if the workspace flyout is open (used to open, save, ... the workspace)
ObservableCollection< HamburgerMenuItem > OptionMenuItems
Available menu items in the hamburger menu options section.
ICommand OptionsMenuItemInvokedCommand
Command that is invoked when an options menu item in the hamburger menu is clicked.
HamburgerMenuItem _selectedOptionsMenuItem
Menu item that is currently selected in the hamburger menu options section.
ShellViewModel(INavigationService navigationService, IDialogCoordinator dialogCoordinator, IWorkspaceService workspaceService)
Constructor of the view model of the main shell of the application.
async Task<(bool saveOut, bool cancelOut)> CheckForUnsavedChangesAndQueryUserAction()
Show a dialog to the user when there are unsaved changes.
ObservableCollection< HamburgerMenuItem > MenuItems
Available menu items in the hamburger menu.
EventHandler WindowCloseRequested
Event that is raised when the window close is requested.
ICommand MenuItemInvokedCommand
Command that is invoked when a menu item in the hamburger menu is clicked.
bool HasUnsavedChanges
True if the workspace has unsaved changes; otherwise false.
ICommand UnloadedCommand
Command that is invoked when the shell is unloaded.
void updateMenuItemsEnabledState()
Update the enabled state of the menu items based on whether a workspace is open.
ICommand LoadedCommand
Command that is invoked when the shell is loaded.
ViewModel for the workspace, managing the current workspace folder, settings, and commands to load,...
Interface for a service for handling navigation within the application.
Task< bool > Load(string path, CancellationToken cancellationToken)
Load from the given file This is using a separate Task because the file possibly can be large.
Interface for a service used to manage a workspace.