4
4
using Microsoft . UI . Windowing ;
5
5
using Microsoft . UI . Xaml ;
6
6
using System . Runtime . InteropServices ;
7
+ using Windows . Foundation ;
8
+ using Windows . Foundation . Collections ;
9
+ using Windows . Storage ;
7
10
using Windows . Win32 ;
8
11
using Windows . Win32 . Foundation ;
12
+ using Windows . Win32 . Graphics . Gdi ;
9
13
using Windows . Win32 . UI . WindowsAndMessaging ;
10
14
11
15
namespace Files . App . Data . Items
12
16
{
13
- public unsafe class WindowEx : Window
17
+ public unsafe class WindowEx : Window , IDisposable
14
18
{
15
19
private readonly WNDPROC _oldWndProc ;
16
20
private readonly WNDPROC _newWndProc ;
17
21
22
+ private readonly ApplicationDataContainer _applicationDataContainer = ApplicationData . Current . LocalSettings ;
23
+
18
24
public nint WindowHandle { get ; }
19
25
20
26
public int MinWidth { get ; set ; }
@@ -27,7 +33,9 @@ public bool IsMaximizable
27
33
set
28
34
{
29
35
_IsMaximizable = value ;
30
- UpdateOverlappedPresenter ( ( c ) => c . IsMaximizable = value ) ;
36
+
37
+ if ( AppWindow . Presenter is OverlappedPresenter overlapped )
38
+ overlapped . IsMaximizable = value ;
31
39
32
40
if ( value )
33
41
{
@@ -51,8 +59,10 @@ public bool IsMinimizable
51
59
get => _IsMinimizable ;
52
60
set
53
61
{
54
- _IsMaximizable = value ;
55
- UpdateOverlappedPresenter ( ( c ) => c . IsMinimizable = value ) ;
62
+ _IsMinimizable = value ;
63
+
64
+ if ( AppWindow . Presenter is OverlappedPresenter overlapped )
65
+ overlapped . IsMinimizable = value ;
56
66
}
57
67
}
58
68
@@ -62,18 +72,164 @@ public unsafe WindowEx(int minWidth = 400, int minHeight = 300)
62
72
MinWidth = minWidth ;
63
73
MinHeight = minHeight ;
64
74
75
+ RestoreWindowPlacementData ( ) ;
76
+
65
77
_newWndProc = new ( NewWindowProc ) ;
66
78
var pNewWndProc = Marshal . GetFunctionPointerForDelegate ( _newWndProc ) ;
67
79
var pOldWndProc = PInvoke . SetWindowLongPtr ( new ( WindowHandle ) , WINDOW_LONG_PTR_INDEX . GWL_WNDPROC , pNewWndProc ) ;
68
80
_oldWndProc = Marshal . GetDelegateForFunctionPointer < WNDPROC > ( pOldWndProc ) ;
69
81
}
70
82
71
- private void UpdateOverlappedPresenter ( Action < OverlappedPresenter > action )
83
+ private unsafe void StoreWindowPlacementData ( )
72
84
{
73
- if ( AppWindow . Presenter is OverlappedPresenter overlapped )
74
- action ( overlapped ) ;
85
+ // Save window placement only for MainWindow
86
+ if ( ! GetType ( ) . Name . Equals ( nameof ( MainWindow ) , StringComparison . OrdinalIgnoreCase ) )
87
+ return ;
88
+
89
+ // Store monitor info
90
+ using var data = new SystemIO . MemoryStream ( ) ;
91
+ using var sw = new SystemIO . BinaryWriter ( data ) ;
92
+
93
+ var monitors = GetAllMonitorInfo ( ) ;
94
+ int nMonitors = PInvoke . GetSystemMetrics ( SYSTEM_METRICS_INDEX . SM_CMONITORS ) ;
95
+ sw . Write ( nMonitors ) ;
96
+
97
+ foreach ( var monitor in monitors )
98
+ {
99
+ sw . Write ( monitor . Item1 ) ;
100
+ sw . Write ( monitor . Item2 . Left ) ;
101
+ sw . Write ( monitor . Item2 . Top ) ;
102
+ sw . Write ( monitor . Item2 . Right ) ;
103
+ sw . Write ( monitor . Item2 . Bottom ) ;
104
+ }
105
+
106
+ WINDOWPLACEMENT placement = default ;
107
+ PInvoke . GetWindowPlacement ( new ( WindowHandle ) , ref placement ) ;
108
+
109
+ int structSize = Marshal . SizeOf ( typeof ( WINDOWPLACEMENT ) ) ;
110
+ IntPtr buffer = Marshal . AllocHGlobal ( structSize ) ;
111
+ Marshal . StructureToPtr ( placement , buffer , false ) ;
112
+ byte [ ] placementData = new byte [ structSize ] ;
113
+ Marshal . Copy ( buffer , placementData , 0 , structSize ) ;
114
+ Marshal . FreeHGlobal ( buffer ) ;
115
+
116
+ sw . Write ( placementData ) ;
117
+ sw . Flush ( ) ;
118
+
119
+ var values = GetDataStore ( out var oldDataExists ) ;
120
+
121
+ values [ oldDataExists ? "WindowPersistance_FilesMainWindow" : "MainWindowPlacementData" ] = Convert . ToBase64String ( data . ToArray ( ) ) ;
122
+ }
123
+
124
+ private void RestoreWindowPlacementData ( )
125
+ {
126
+ // Save window placement only for MainWindow
127
+ if ( ! GetType ( ) . Name . Equals ( nameof ( MainWindow ) , StringComparison . OrdinalIgnoreCase ) )
128
+ return ;
129
+
130
+ var values = GetDataStore ( out var oldDataExists ) ;
131
+
132
+ byte [ ] ? data = null ;
133
+ if ( values . TryGetValue ( oldDataExists ? "WindowPersistance_FilesMainWindow" : "MainWindowPlacementData" , out object ? value ) )
134
+ {
135
+ if ( value is string base64 )
136
+ data = Convert . FromBase64String ( base64 ) ;
137
+ }
138
+
139
+ if ( data is null )
140
+ return ;
141
+
142
+ SystemIO . BinaryReader br = new ( new SystemIO . MemoryStream ( data ) ) ;
143
+
144
+ // Check if monitor layout changed since we stored position
145
+ var monitors = GetAllMonitorInfo ( ) ;
146
+ int monitorCount = br . ReadInt32 ( ) ;
147
+ int nMonitors = PInvoke . GetSystemMetrics ( SYSTEM_METRICS_INDEX . SM_CMONITORS ) ;
148
+ if ( monitorCount != nMonitors )
149
+ return ;
150
+
151
+ for ( int i = 0 ; i < monitorCount ; i ++ )
152
+ {
153
+ var pMonitor = monitors [ i ] ;
154
+ br . ReadString ( ) ;
155
+ if ( pMonitor . Item2 . Left != br . ReadDouble ( ) ||
156
+ pMonitor . Item2 . Top != br . ReadDouble ( ) ||
157
+ pMonitor . Item2 . Right != br . ReadDouble ( ) ||
158
+ pMonitor . Item2 . Bottom != br . ReadDouble ( ) )
159
+ return ;
160
+ }
161
+
162
+ int structSize = Marshal . SizeOf ( typeof ( WINDOWPLACEMENT ) ) ;
163
+ byte [ ] placementData = br . ReadBytes ( structSize ) ;
164
+ IntPtr buffer = Marshal . AllocHGlobal ( structSize ) ;
165
+ Marshal . Copy ( placementData , 0 , buffer , structSize ) ;
166
+ var windowPlacementData = ( WINDOWPLACEMENT ) Marshal . PtrToStructure ( buffer , typeof ( WINDOWPLACEMENT ) ) ! ;
167
+
168
+ Marshal . FreeHGlobal ( buffer ) ;
169
+
170
+ // Ignore anything by maximized or normal
171
+ if ( windowPlacementData . showCmd == ( SHOW_WINDOW_CMD ) 0x0002 /*SW_INVALIDATE*/ &&
172
+ windowPlacementData . flags == WINDOWPLACEMENT_FLAGS . WPF_RESTORETOMAXIMIZED )
173
+ windowPlacementData . showCmd = SHOW_WINDOW_CMD . SW_MAXIMIZE ;
174
+ else if ( windowPlacementData . showCmd != SHOW_WINDOW_CMD . SW_MAXIMIZE )
175
+ windowPlacementData . showCmd = SHOW_WINDOW_CMD . SW_NORMAL ;
176
+
177
+ PInvoke . SetWindowPlacement ( new ( WindowHandle ) , in windowPlacementData ) ;
178
+
179
+ return ;
180
+ }
181
+
182
+ private IPropertySet GetDataStore ( out bool oldDataExists )
183
+ {
184
+ IPropertySet values ;
185
+ oldDataExists = false ;
186
+
187
+ // TODO: Remove this after a couple of months past
188
+ if ( _applicationDataContainer . Containers . TryGetValue ( "WinUIEx" , out var oldDataContainer ) )
189
+ {
190
+ values = oldDataContainer . Values ;
191
+ oldDataExists = true ;
192
+ }
193
+ else if ( _applicationDataContainer . Containers . TryGetValue ( "Files" , out var dataContainer ) )
194
+ {
195
+ values = dataContainer . Values ;
196
+ }
75
197
else
76
- throw new NotSupportedException ( $ "'{ AppWindow . Presenter . Kind } ' presenter is not supported.") ;
198
+ {
199
+ values = _applicationDataContainer . CreateContainer (
200
+ "Files" ,
201
+ ApplicationDataCreateDisposition . Always ) . Values ;
202
+ }
203
+
204
+ return values ;
205
+ }
206
+
207
+ private unsafe List < Tuple < string , Rect > > GetAllMonitorInfo ( )
208
+ {
209
+ List < Tuple < string , Rect > > monitors = [ ] ;
210
+ MONITORENUMPROC callback = new ( ( HMONITOR monitor , HDC deviceContext , RECT * rect , LPARAM data ) =>
211
+ {
212
+ MONITORINFOEXW info = default ;
213
+ info . monitorInfo . cbSize = ( uint ) Marshal . SizeOf < MONITORINFOEXW > ( ) ;
214
+
215
+ //fixed (MONITORINFOEXW* pInfo = &info)
216
+ //{
217
+ PInvoke . GetMonitorInfo ( monitor , ( MONITORINFO * ) & info ) ;
218
+
219
+ monitors . Add ( new (
220
+ info . szDevice . ToString ( ) ,
221
+ new ( new Point ( rect ->left , rect ->top ) , new Point ( rect ->right , rect ->bottom ) ) ) ) ;
222
+ //}
223
+
224
+ return true ;
225
+ } ) ;
226
+
227
+ LPARAM lParam = default ;
228
+ bool ok = PInvoke . EnumDisplayMonitors ( new ( nint . Zero ) , ( RECT * ) null , callback , lParam ) ;
229
+ if ( ! ok )
230
+ Marshal . ThrowExceptionForHR ( Marshal . GetLastWin32Error ( ) ) ;
231
+
232
+ return monitors ;
77
233
}
78
234
79
235
private LRESULT NewWindowProc ( HWND param0 , uint param1 , WPARAM param2 , LPARAM param3 )
@@ -95,5 +251,12 @@ private LRESULT NewWindowProc(HWND param0, uint param1, WPARAM param2, LPARAM pa
95
251
96
252
return PInvoke . CallWindowProc ( _oldWndProc , param0 , param1 , param2 , param3 ) ;
97
253
}
254
+
255
+ // Disposer
256
+
257
+ public void Dispose ( )
258
+ {
259
+ StoreWindowPlacementData ( ) ;
260
+ }
98
261
}
99
262
}
0 commit comments