なろうブックマーク分析用ツールのPrism+WinUI3サンプル実装
Revisão | 71ec7075978b32b6f50058d8f72c8b0ac870b142 (tree) |
---|---|
Hora | 2022-08-18 00:28:38 |
Autor | yoshy <yoshy.org.bitbucket@gz.j...> |
Commiter | yoshy |
[UPD] アプリケーション終了時に各 ViewModel を明示的に破棄するようにした
@@ -7,6 +7,7 @@ namespace TestNarou3.Adaptor.Boundary.Gateway.ViewModel | ||
7 | 7 | public static readonly string REGION_NAME = "MainContentPane"; |
8 | 8 | |
9 | 9 | ReactiveProperty<string> Title { get; } |
10 | - ReactiveCommand CommandClosed { get; } | |
10 | + | |
11 | + void OnClosing(); | |
11 | 12 | } |
12 | 13 | } |
\ No newline at end of file |
@@ -16,7 +16,7 @@ using TestNarou3.Domain.Model.Entity.Child; | ||
16 | 16 | namespace TestNarou3.Adaptor.Gateway.ViewModel |
17 | 17 | { |
18 | 18 | internal class BookmarkCategoryViewModel |
19 | - : IBookmarkCategoryViewModel, INotifyPropertyChanged, IDestructible | |
19 | + : IBookmarkCategoryViewModel, INotifyPropertyChanged, IDestructible, IDisposable | |
20 | 20 | { |
21 | 21 | private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); |
22 | 22 |
@@ -33,7 +33,9 @@ namespace TestNarou3.Adaptor.Gateway.ViewModel | ||
33 | 33 | public IAppWindowController WindowController { get; } |
34 | 34 | |
35 | 35 | private readonly IBookmarkCategoryRowViewModelTranslator vmTranslator; |
36 | + | |
36 | 37 | private readonly CompositeDisposable disposables = new(); |
38 | + private bool disposedValue; | |
37 | 39 | |
38 | 40 | public BookmarkCategoryViewModel( |
39 | 41 | IAppWindowController wc, |
@@ -46,6 +48,8 @@ namespace TestNarou3.Adaptor.Gateway.ViewModel | ||
46 | 48 | this.SelectedItem.Subscribe(OnSelectedItemChanged).AddTo(disposables); |
47 | 49 | } |
48 | 50 | |
51 | + #region event handler | |
52 | + | |
49 | 53 | private void OnSelectedItemChanged(IBookmarkCategoryRowViewModel row) |
50 | 54 | { |
51 | 55 | if (row == null) |
@@ -55,6 +59,10 @@ namespace TestNarou3.Adaptor.Gateway.ViewModel | ||
55 | 59 | this.WindowController.NavigateBookmarkListView(row.Index); |
56 | 60 | } |
57 | 61 | |
62 | + #endregion | |
63 | + | |
64 | + #region vm operation | |
65 | + | |
58 | 66 | public void SynchronizeWith(BookmarkCategory source) |
59 | 67 | { |
60 | 68 | if (this.Source == null) |
@@ -83,10 +91,55 @@ namespace TestNarou3.Adaptor.Gateway.ViewModel | ||
83 | 91 | } |
84 | 92 | } |
85 | 93 | |
94 | + #endregion | |
95 | + | |
96 | + #region IDestructible | |
97 | + | |
86 | 98 | public void Destroy() |
87 | 99 | { |
88 | - this.disposables.Dispose(); | |
100 | + this.Dispose(); | |
89 | 101 | } |
90 | 102 | |
103 | + #endregion | |
104 | + | |
105 | + #region IDisposable | |
106 | + | |
107 | + protected virtual void Dispose(bool disposing) | |
108 | + { | |
109 | + if (!disposedValue) | |
110 | + { | |
111 | + if (disposing) | |
112 | + { | |
113 | + DisposeSynchronizedView(); | |
114 | + | |
115 | + this.disposables.Dispose(); | |
116 | + | |
117 | + logger.Trace("BookmarkCategoryViewModel disposed."); | |
118 | + } | |
119 | + | |
120 | + disposedValue = true; | |
121 | + } | |
122 | + } | |
123 | + | |
124 | + private void DisposeSynchronizedView() | |
125 | + { | |
126 | + foreach (var row in this.Rows) | |
127 | + { | |
128 | + if (row is IDisposable disposable) | |
129 | + { | |
130 | + disposable.Dispose(); | |
131 | + } | |
132 | + } | |
133 | + } | |
134 | + | |
135 | + public void Dispose() | |
136 | + { | |
137 | + // このコードを変更しないでください。クリーンアップ コードを 'Dispose(bool disposing)' メソッドに記述します | |
138 | + Dispose(disposing: true); | |
139 | + GC.SuppressFinalize(this); | |
140 | + } | |
141 | + | |
142 | + #endregion | |
143 | + | |
91 | 144 | } |
92 | 145 | } |
@@ -17,7 +17,7 @@ using TestNarou3.Domain.Model.Entity.Child; | ||
17 | 17 | namespace TestNarou3.Adaptor.Gateway.ViewModel |
18 | 18 | { |
19 | 19 | internal class BookmarkDetailListViewModel |
20 | - : IBookmarkDetailListViewModel, INotifyPropertyChanged, IDestructible | |
20 | + : IBookmarkDetailListViewModel, INotifyPropertyChanged, IDestructible, IDisposable | |
21 | 21 | { |
22 | 22 | private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); |
23 | 23 |
@@ -36,9 +36,10 @@ namespace TestNarou3.Adaptor.Gateway.ViewModel | ||
36 | 36 | private IAppWindowController WindowController { get; } |
37 | 37 | |
38 | 38 | private IBookmarkDetailListRowViewModel selectedRow; |
39 | - | |
40 | 39 | private readonly IBookmarkDetailListRowViewModelTranslator vmTranslator; |
40 | + | |
41 | 41 | private readonly CompositeDisposable disposables = new(); |
42 | + private bool disposedValue; | |
42 | 43 | |
43 | 44 | public BookmarkDetailListViewModel( |
44 | 45 | IAppWindowController wc, |
@@ -54,6 +55,8 @@ namespace TestNarou3.Adaptor.Gateway.ViewModel | ||
54 | 55 | .WithSubscribe(OpenBookmark).AddTo(disposables); |
55 | 56 | } |
56 | 57 | |
58 | + #region event handler | |
59 | + | |
57 | 60 | private void OnSelectedItemChanged(IBookmarkDetailListRowViewModel selectedRow) |
58 | 61 | { |
59 | 62 | this.selectedRow = selectedRow; |
@@ -74,6 +77,10 @@ namespace TestNarou3.Adaptor.Gateway.ViewModel | ||
74 | 77 | Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }); |
75 | 78 | } |
76 | 79 | |
80 | + #endregion | |
81 | + | |
82 | + #region vm operation | |
83 | + | |
77 | 84 | public void SynchronizeWith(BookmarkDetailList source) |
78 | 85 | { |
79 | 86 | if (this.Source == null) |
@@ -96,10 +103,55 @@ namespace TestNarou3.Adaptor.Gateway.ViewModel | ||
96 | 103 | } |
97 | 104 | } |
98 | 105 | |
106 | + #endregion | |
107 | + | |
108 | + #region IDestructible | |
109 | + | |
99 | 110 | public void Destroy() |
100 | 111 | { |
101 | - this.disposables.Dispose(); | |
112 | + this.Dispose(); | |
113 | + } | |
114 | + | |
115 | + #endregion | |
116 | + | |
117 | + #region IDisposable | |
118 | + | |
119 | + protected virtual void Dispose(bool disposing) | |
120 | + { | |
121 | + if (!disposedValue) | |
122 | + { | |
123 | + if (disposing) | |
124 | + { | |
125 | + DisposeSynchronizedView(); | |
126 | + | |
127 | + this.disposables.Dispose(); | |
128 | + | |
129 | + logger.Trace("BookmarkDetailListViewModel disposed."); | |
130 | + } | |
131 | + | |
132 | + disposedValue = true; | |
133 | + } | |
134 | + } | |
135 | + | |
136 | + private void DisposeSynchronizedView() | |
137 | + { | |
138 | + foreach (var row in this.Rows) | |
139 | + { | |
140 | + if (row is IDisposable disposable) | |
141 | + { | |
142 | + disposable.Dispose(); | |
143 | + } | |
144 | + } | |
145 | + } | |
146 | + | |
147 | + public void Dispose() | |
148 | + { | |
149 | + // このコードを変更しないでください。クリーンアップ コードを 'Dispose(bool disposing)' メソッドに記述します | |
150 | + Dispose(disposing: true); | |
151 | + GC.SuppressFinalize(this); | |
102 | 152 | } |
103 | 153 | |
154 | + #endregion | |
155 | + | |
104 | 156 | } |
105 | 157 | } |
@@ -1,24 +1,56 @@ | ||
1 | -using Prism.Navigation; | |
1 | +using NLog; | |
2 | +using Prism.Navigation; | |
2 | 3 | using System.ComponentModel; |
3 | 4 | using System.Reactive.Disposables; |
4 | 5 | using TestNarou3.Adaptor.Boundary.Gateway.ViewModel; |
5 | 6 | |
6 | 7 | namespace TestNarou3.Adaptor.Gateway.ViewModel |
7 | 8 | { |
8 | - internal class BookmarkViewModel : IBookmarkViewModel, IDestructible | |
9 | + internal class BookmarkViewModel : IBookmarkViewModel, IDestructible, IDisposable | |
9 | 10 | { |
10 | - //private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); | |
11 | + private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); | |
11 | 12 | |
12 | 13 | #pragma warning disable CS0067 |
13 | 14 | public event PropertyChangedEventHandler PropertyChanged; |
14 | 15 | #pragma warning restore CS0067 |
15 | 16 | |
16 | 17 | private readonly CompositeDisposable disposables = new(); |
18 | + private bool disposedValue; | |
19 | + | |
20 | + #region IDestructible | |
17 | 21 | |
18 | 22 | public void Destroy() |
19 | 23 | { |
20 | - this.disposables.Dispose(); | |
24 | + this.Dispose(); | |
25 | + } | |
26 | + | |
27 | + #endregion | |
28 | + | |
29 | + #region IDisposable | |
30 | + | |
31 | + protected virtual void Dispose(bool disposing) | |
32 | + { | |
33 | + if (!disposedValue) | |
34 | + { | |
35 | + if (disposing) | |
36 | + { | |
37 | + this.disposables.Dispose(); | |
38 | + | |
39 | + logger.Trace("BookmarkViewModel disposed."); | |
40 | + } | |
41 | + | |
42 | + disposedValue = true; | |
43 | + } | |
44 | + } | |
45 | + | |
46 | + public void Dispose() | |
47 | + { | |
48 | + // このコードを変更しないでください。クリーンアップ コードを 'Dispose(bool disposing)' メソッドに記述します | |
49 | + Dispose(disposing: true); | |
50 | + GC.SuppressFinalize(this); | |
21 | 51 | } |
22 | 52 | |
53 | + #endregion | |
54 | + | |
23 | 55 | } |
24 | 56 | } |
@@ -11,7 +11,7 @@ using TestNarou3.UseCase.Request; | ||
11 | 11 | |
12 | 12 | namespace TestNarou3.Adaptor.Gateway.ViewModel |
13 | 13 | { |
14 | - internal class LoginFormViewModel : ILoginFormViewModel, IDestructible | |
14 | + internal class LoginFormViewModel : ILoginFormViewModel, IDestructible, IDisposable | |
15 | 15 | { |
16 | 16 | private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); |
17 | 17 |
@@ -26,6 +26,7 @@ namespace TestNarou3.Adaptor.Gateway.ViewModel | ||
26 | 26 | private readonly IAppWindowController wc; |
27 | 27 | |
28 | 28 | private readonly CompositeDisposable disposables = new(); |
29 | + private bool disposedValue; | |
29 | 30 | |
30 | 31 | public LoginFormViewModel(IAppWindowController wc) |
31 | 32 | { |
@@ -37,11 +38,41 @@ namespace TestNarou3.Adaptor.Gateway.ViewModel | ||
37 | 38 | this.CommandLogin = new ReactiveCommand().WithSubscribe(OnLogin).AddTo(disposables); |
38 | 39 | } |
39 | 40 | |
41 | + #region IDestructible | |
42 | + | |
40 | 43 | public void Destroy() |
41 | 44 | { |
42 | - this.disposables.Dispose(); | |
45 | + this.Dispose(); | |
43 | 46 | } |
44 | 47 | |
48 | + #endregion | |
49 | + | |
50 | + #region IDisposable | |
51 | + | |
52 | + protected virtual void Dispose(bool disposing) | |
53 | + { | |
54 | + if (!disposedValue) | |
55 | + { | |
56 | + if (disposing) | |
57 | + { | |
58 | + this.disposables.Dispose(); | |
59 | + | |
60 | + logger.Trace("LoginFormViewModel disposed."); | |
61 | + } | |
62 | + | |
63 | + disposedValue = true; | |
64 | + } | |
65 | + } | |
66 | + | |
67 | + public void Dispose() | |
68 | + { | |
69 | + // このコードを変更しないでください。クリーンアップ コードを 'Dispose(bool disposing)' メソッドに記述します | |
70 | + Dispose(disposing: true); | |
71 | + GC.SuppressFinalize(this); | |
72 | + } | |
73 | + | |
74 | + #endregion | |
75 | + | |
45 | 76 | #region event handler |
46 | 77 | |
47 | 78 | private void OnLogin() |
@@ -72,5 +103,6 @@ namespace TestNarou3.Adaptor.Gateway.ViewModel | ||
72 | 103 | } |
73 | 104 | |
74 | 105 | #endregion |
106 | + | |
75 | 107 | } |
76 | 108 | } |
@@ -1,4 +1,6 @@ | ||
1 | -using Prism.Navigation; | |
1 | +using NLog; | |
2 | +using Prism.Navigation; | |
3 | +using Prism.Regions; | |
2 | 4 | using Reactive.Bindings; |
3 | 5 | using Reactive.Bindings.Extensions; |
4 | 6 | using System.Reactive.Disposables; |
@@ -9,37 +11,75 @@ using TestNarou3.UseCase.Request; | ||
9 | 11 | |
10 | 12 | namespace TestNarou3.Adaptor.Gateway.ViewModel |
11 | 13 | { |
12 | - internal class MainWindowViewModel : IMainWindowViewModel, IDestructible | |
14 | + internal class MainWindowViewModel : IMainWindowViewModel, IDestructible, IDisposable | |
13 | 15 | { |
14 | - private IAppWindowController wc; | |
16 | + private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); | |
15 | 17 | |
16 | 18 | public ReactiveProperty<string> Title { get; } |
17 | 19 | |
18 | - public ReactiveCommand CommandClosed { get; } | |
20 | + private readonly IAppWindowController wc; | |
21 | + private readonly IRegionManager rm; | |
19 | 22 | |
20 | 23 | private readonly CompositeDisposable disposables = new(); |
24 | + private bool disposedValue; | |
21 | 25 | |
22 | 26 | public MainWindowViewModel( |
23 | 27 | IAppCaptionFormatter caption, |
24 | - IAppWindowController wc) | |
28 | + IAppWindowController wc, | |
29 | + IRegionManager rm) | |
25 | 30 | { |
26 | 31 | this.wc = wc; |
32 | + this.rm = rm; | |
27 | 33 | |
28 | 34 | this.Title = new ReactiveProperty<string>(caption.GetCaption()).AddTo(disposables); |
29 | - | |
30 | - this.CommandClosed = new ReactiveCommand().WithSubscribe(OnClosed).AddTo(disposables); | |
31 | 35 | } |
32 | 36 | |
33 | - private void OnClosed() | |
37 | + public void OnClosing() | |
34 | 38 | { |
35 | 39 | _ = this.wc.Execute(new NarouLogoutRequest()); |
36 | 40 | _ = this.wc.Execute(new AppConfigSaveRequest()); |
41 | + | |
42 | + this.Dispose(); | |
37 | 43 | } |
38 | 44 | |
45 | + #region IDestructible | |
46 | + | |
39 | 47 | public void Destroy() |
40 | 48 | { |
41 | - this.disposables.Dispose(); | |
49 | + this.Dispose(); | |
50 | + } | |
51 | + | |
52 | + #endregion | |
53 | + | |
54 | + #region IDisposable | |
55 | + | |
56 | + protected virtual void Dispose(bool disposing) | |
57 | + { | |
58 | + if (!disposedValue) | |
59 | + { | |
60 | + if (disposing) | |
61 | + { | |
62 | + this.disposables.Dispose(); | |
63 | + | |
64 | + logger.Trace("MainWindowViewModel disposed."); | |
65 | + | |
66 | + foreach (IRegion region in rm.Regions) | |
67 | + { | |
68 | + region.RemoveAll(); | |
69 | + } | |
70 | + } | |
71 | + | |
72 | + disposedValue = true; | |
73 | + } | |
74 | + } | |
75 | + | |
76 | + public void Dispose() | |
77 | + { | |
78 | + // このコードを変更しないでください。クリーンアップ コードを 'Dispose(bool disposing)' メソッドに記述します | |
79 | + Dispose(disposing: true); | |
80 | + GC.SuppressFinalize(this); | |
42 | 81 | } |
82 | + #endregion | |
43 | 83 | |
44 | 84 | } |
45 | 85 | } |
@@ -10,6 +10,7 @@ using System.Collections.Generic; | ||
10 | 10 | using System.IO; |
11 | 11 | using System.Linq; |
12 | 12 | using System.Runtime.InteropServices.WindowsRuntime; |
13 | +using TestNarou3.Adaptor.Boundary.Gateway.ViewModel; | |
13 | 14 | using Windows.Foundation; |
14 | 15 | using Windows.Foundation.Collections; |
15 | 16 |
@@ -20,8 +21,12 @@ namespace TestNarou3.OuterEdge.UI.View | ||
20 | 21 | { |
21 | 22 | public sealed partial class MainWindowView : UserControl |
22 | 23 | { |
23 | - public MainWindowView() | |
24 | + public IMainWindowViewModel ViewModel { get; } | |
25 | + | |
26 | + public MainWindowView(IMainWindowViewModel viewModel) | |
24 | 27 | { |
28 | + this.ViewModel = viewModel; | |
29 | + | |
25 | 30 | this.InitializeComponent(); |
26 | 31 | } |
27 | 32 | } |
@@ -3,6 +3,8 @@ | ||
3 | 3 | using AutoMapper; |
4 | 4 | using CleanAuLait; |
5 | 5 | using CleanAuLait.Core.Log; |
6 | +using Microsoft.UI; | |
7 | +using Microsoft.UI.Windowing; | |
6 | 8 | using Microsoft.UI.Xaml; |
7 | 9 | using Microsoft.UI.Xaml.Controls; |
8 | 10 | using NLog; |
@@ -14,6 +16,7 @@ using System.Reflection; | ||
14 | 16 | using System.Threading.Tasks; |
15 | 17 | using TestNarou3.Adaptor; |
16 | 18 | using TestNarou3.Adaptor.Boundary.Controller; |
19 | +using TestNarou3.Adaptor.Boundary.Gateway.ViewModel; | |
17 | 20 | using TestNarou3.Core.Mapper; |
18 | 21 | using TestNarou3.Domain; |
19 | 22 | using TestNarou3.Infra; |
@@ -243,6 +246,9 @@ public partial class App : PrismApplication | ||
243 | 246 | |
244 | 247 | PrepareInitialView(); |
245 | 248 | |
249 | + AppWindow appWindow = GetCurrentAppWindow(); | |
250 | + appWindow.Closing += OnClosing; | |
251 | + | |
246 | 252 | logger.Trace("OnStartup end"); |
247 | 253 | } |
248 | 254 |
@@ -257,6 +263,24 @@ public partial class App : PrismApplication | ||
257 | 263 | wc.NavigateLoginFormView(); |
258 | 264 | } |
259 | 265 | |
266 | + private AppWindow GetCurrentAppWindow() | |
267 | + { | |
268 | + IntPtr hwnd = WinRT.Interop.WindowNative.GetWindowHandle(MainWindow); | |
269 | + WindowId winId = Win32Interop.GetWindowIdFromWindow(hwnd); | |
270 | + | |
271 | + return AppWindow.GetFromWindowId(winId); | |
272 | + } | |
273 | + | |
274 | + private void OnClosing(AppWindow sender, AppWindowClosingEventArgs args) | |
275 | + { | |
276 | + logger.Trace("OnClosing called."); | |
277 | + | |
278 | + var vm = Container.Resolve<IMainWindowViewModel>(); | |
279 | + vm.OnClosing(); | |
280 | + | |
281 | + LogManager.Shutdown(); | |
282 | + } | |
283 | + | |
260 | 284 | #if false |
261 | 285 | protected override void OnExit(ExitEventArgs e) |
262 | 286 | { |