Skip to content

Commit 6dec120

Browse files
committed
Update the View when edits disrupt the validity of a HotkeySetting
1 parent c760d70 commit 6dec120

File tree

2 files changed

+137
-8
lines changed

2 files changed

+137
-8
lines changed

Rubberduck.Core/UI/Settings/GeneralSettingsViewModel.cs

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
using Rubberduck.Resources;
1313
using Rubberduck.Resources.Settings;
1414
using Rubberduck.Parsing.Common;
15+
using System.Collections.Specialized;
16+
using Rubberduck.UI.WPF;
1517

1618
namespace Rubberduck.UI.Settings
1719
{
@@ -79,19 +81,32 @@ public DisplayLanguageSetting SelectedLanguage
7981
}
8082
}
8183

82-
private ObservableCollection<HotkeySetting> _hotkeys;
83-
public ObservableCollection<HotkeySetting> Hotkeys
84+
private ObservableViewModelCollection<HotkeySettingViewModel> _hotkeys;
85+
public ObservableViewModelCollection<HotkeySettingViewModel> Hotkeys
8486
{
8587
get => _hotkeys;
8688
set
8789
{
8890
if (_hotkeys != value)
8991
{
92+
if (_hotkeys != null)
93+
{
94+
_hotkeys.ElementPropertyChanged -= InvalidateShouldDisplayWarning;
95+
}
96+
if (value != null)
97+
{
98+
value.ElementPropertyChanged += InvalidateShouldDisplayWarning;
99+
}
90100
_hotkeys = value;
91101
OnPropertyChanged();
102+
OnPropertyChanged(nameof(ShouldDisplayHotkeyModificationLabel));
92103
}
93104
}
94105
}
106+
private void InvalidateShouldDisplayWarning(object sender, ElementPropertyChangedEventArgs<HotkeySettingViewModel> e)
107+
{
108+
OnPropertyChanged(nameof(ShouldDisplayHotkeyModificationLabel));
109+
}
95110

96111
public bool ShouldDisplayHotkeyModificationLabel
97112
{
@@ -259,7 +274,7 @@ private void ShowLogFolder()
259274
public void UpdateConfig(Configuration config)
260275
{
261276
config.UserSettings.GeneralSettings = GetCurrentGeneralSettings();
262-
config.UserSettings.HotkeySettings.Settings = Hotkeys.ToArray();
277+
config.UserSettings.HotkeySettings.Settings = Hotkeys.Select(vm => vm.Unwrap()).ToArray();
263278
}
264279

265280
public void SetToDefaults(Configuration config)
@@ -290,13 +305,13 @@ protected override void TransferSettingsToView(Rubberduck.Settings.GeneralSettin
290305
TransferSettingsToView(toLoad, null);
291306
}
292307

293-
private void TransferSettingsToView(IGeneralSettings general, IHotkeySettings hottkey)
308+
private void TransferSettingsToView(IGeneralSettings general, IHotkeySettings hotkey)
294309
{
295310
SelectedLanguage = Languages.FirstOrDefault(culture => culture.Code == general.Language.Code);
296311

297-
Hotkeys = hottkey == null
298-
? new ObservableCollection<HotkeySetting>()
299-
: new ObservableCollection<HotkeySetting>(hottkey.Settings);
312+
Hotkeys = hotkey == null
313+
? new ObservableViewModelCollection<HotkeySettingViewModel>()
314+
: new ObservableViewModelCollection<HotkeySettingViewModel>(hotkey.Settings.Select(data => new HotkeySettingViewModel(data)));
300315
ShowSplashAtStartup = general.CanShowSplash;
301316
CheckVersionAtStartup = general.CanCheckVersion;
302317
CompileBeforeParse = general.CompileBeforeParse;
@@ -352,8 +367,60 @@ protected override void ExportSettings(Rubberduck.Settings.GeneralSettings setti
352367
dialog.ShowDialog();
353368
if (string.IsNullOrEmpty(dialog.FileName)) return;
354369
Service.Save(settings, dialog.FileName);
355-
_hotkeyService.Save(new HotkeySettings { Settings = Hotkeys.ToArray() }, dialog.FileName);
370+
_hotkeyService.Save(new HotkeySettings { Settings = Hotkeys.Select(vm => vm.Unwrap()).ToArray() }, dialog.FileName);
356371
}
357372
}
358373
}
374+
375+
public class HotkeySettingViewModel : ViewModelBase
376+
{
377+
private readonly HotkeySetting wrapped;
378+
379+
public HotkeySettingViewModel(HotkeySetting wrapped)
380+
{
381+
this.wrapped = wrapped;
382+
}
383+
384+
public HotkeySetting Unwrap() { return wrapped; }
385+
386+
public string Key1
387+
{
388+
get { return wrapped.Key1; }
389+
set { wrapped.Key1 = value; OnPropertyChanged(); }
390+
}
391+
392+
public bool IsEnabled
393+
{
394+
get { return wrapped.IsEnabled; }
395+
set { wrapped.IsEnabled = value; OnPropertyChanged(); }
396+
}
397+
398+
public bool HasShiftModifier
399+
{
400+
get { return wrapped.HasShiftModifier; }
401+
set { wrapped.HasShiftModifier = value; OnPropertyChanged(); OnPropertyChanged(nameof(IsValid)); }
402+
}
403+
404+
public bool HasAltModifier
405+
{
406+
get { return wrapped.HasAltModifier; }
407+
set { wrapped.HasAltModifier = value; OnPropertyChanged(); OnPropertyChanged(nameof(IsValid)); }
408+
}
409+
410+
public bool HasCtrlModifier
411+
{
412+
get { return wrapped.HasCtrlModifier; }
413+
set { wrapped.HasCtrlModifier = value; OnPropertyChanged(); OnPropertyChanged(nameof(IsValid)); }
414+
}
415+
416+
public string CommandTypeName
417+
{
418+
get { return wrapped.CommandTypeName; }
419+
set { wrapped.CommandTypeName = value; OnPropertyChanged(); }
420+
}
421+
422+
public bool IsValid { get { return wrapped.IsValid; } }
423+
// FIXME If this is the only use, the property should be inlined to here
424+
public string Prompt { get { return wrapped.Prompt; } }
425+
}
359426
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Collections.ObjectModel;
4+
using System.Collections.Specialized;
5+
using System.ComponentModel;
6+
7+
namespace Rubberduck.UI.WPF
8+
{
9+
// Taken and adjusted from https://stackoverflow.com/a/5256827/1803692
10+
/// <summary>
11+
/// This class extends the capabilities of ObservableCollection.
12+
/// It adds an additional event, which is raised when the properties of an item stored in the collection change.
13+
/// </summary>
14+
public class ObservableViewModelCollection<T> : ObservableCollection<T>
15+
where T : INotifyPropertyChanged
16+
{
17+
public event EventHandler<ElementPropertyChangedEventArgs<T>> ElementPropertyChanged;
18+
19+
public ObservableViewModelCollection()
20+
{
21+
CollectionChanged += UpdateContributorEventHandlers;
22+
}
23+
24+
public ObservableViewModelCollection(IEnumerable<T> items) : this()
25+
{
26+
foreach (var item in items)
27+
{
28+
this.Add(item);
29+
}
30+
}
31+
32+
private void UpdateContributorEventHandlers(object sender, NotifyCollectionChangedEventArgs e)
33+
{
34+
foreach (INotifyPropertyChanged added in e.NewItems ?? new List<INotifyPropertyChanged>())
35+
{
36+
added.PropertyChanged += OnItemPropertyChanged;
37+
}
38+
foreach (INotifyPropertyChanged removed in e.OldItems ?? new List<INotifyPropertyChanged>())
39+
{
40+
removed.PropertyChanged -= OnItemPropertyChanged;
41+
}
42+
}
43+
44+
private void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
45+
{
46+
var specializedArgs = new ElementPropertyChangedEventArgs<T>((T)sender, e.PropertyName);
47+
ElementPropertyChanged?.Invoke(this, specializedArgs);
48+
}
49+
}
50+
51+
public class ElementPropertyChangedEventArgs<T>
52+
{
53+
public T Element { get; private set; }
54+
public string PropertyName { get; private set; }
55+
56+
public ElementPropertyChangedEventArgs(T element, string propertyName)
57+
{
58+
Element = element;
59+
PropertyName = propertyName;
60+
}
61+
}
62+
}

0 commit comments

Comments
 (0)