QuickImage – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | using System; |
2 | using System.Collections.Generic; |
||
3 | using System.Diagnostics; |
||
4 | using System.Globalization; |
||
5 | using System.IO; |
||
6 | using System.Linq; |
||
7 | using System.Reflection; |
||
8 | using System.Runtime.CompilerServices; |
||
9 | using System.Security; |
||
10 | using System.Text; |
||
11 | using System.Threading.Tasks; |
||
12 | using Microsoft.VisualBasic; |
||
13 | using System.Windows.Forms; |
||
14 | using System.ComponentModel; |
||
15 | using System.Runtime.InteropServices; |
||
16 | using System.Security.Cryptography; |
||
17 | using static System.Windows.Forms.AxHost; |
||
18 | using static Microsoft.WindowsAPICodePack.Shell.PropertySystem.SystemProperties.System; |
||
19 | using Message = System.Windows.Forms.Message; |
||
20 | |||
21 | namespace QuickImage.Utilities.Controls |
||
22 | { |
||
23 | |||
24 | /// <summary> |
||
25 | /// |
||
26 | /// </summary> |
||
27 | /// <remarks>https://stackoverflow.com/questions/1330050/collapsible-listview</remarks> |
||
28 | /// <remarks>https://stackoverflow.com/questions/41816335/listview-group-header-click-how-to-add-a-context-menu-to-listview-group-header</remarks> |
||
29 | public class ListViewCollapsible : ListView |
||
30 | { |
||
31 | public event EventHandler<ListViewGroup> GroupHeaderClick; |
||
32 | private Dictionary<int, GroupState> groupStates = new Dictionary<int, GroupState>(); |
||
33 | private Dictionary<int, GroupState> groupStateMasks = new Dictionary<int, GroupState>(); |
||
34 | |||
35 | private const int LVM_FIRST = 0x1000; |
||
36 | // ListView messages |
||
37 | private const int LVM_SETGROUPINFO = LVM_FIRST + 147; |
||
38 | |||
39 | private const int LVM_GETGROUPINFO = LVM_FIRST + 149; |
||
40 | private const int LVM_GETGROUPSTATE = LVM_FIRST + 92; |
||
41 | // ListView messages Setinfo on Group |
||
42 | private const int WM_LBUTTONUP = 0x202; |
||
43 | private const int LVM_HITTEST = LVM_FIRST + 18; |
||
44 | private const int LVM_SUBITEMHITTEST = LVM_FIRST + 57; |
||
45 | |||
46 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] |
||
47 | public static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, int lParam); |
||
48 | [DllImport("user32.dll", CharSet = CharSet.Auto)] |
||
49 | public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, int lParam); |
||
50 | [DllImport("user32.dll", CharSet = CharSet.Auto)] |
||
51 | public static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, IntPtr lParam); |
||
52 | [DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)] |
||
53 | public static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, ref LVHITTESTINFO ht); |
||
54 | [DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)] |
||
55 | public static extern IntPtr SendMessageString(IntPtr hWnd, int Msg, int wParam, string lParam); |
||
56 | [DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)] |
||
57 | public static extern IntPtr SendMessageIUnknown(IntPtr hWnd, int msg, |
||
58 | [MarshalAs(UnmanagedType.IUnknown)] object wParam, int lParam); |
||
59 | [DllImport("user32.dll", CharSet = CharSet.Auto)] |
||
60 | public static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, ref LVGROUP lParam); |
||
61 | [DllImport("user32.dll", CharSet = CharSet.Auto)] |
||
62 | public static extern IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, ref LVGROUP2 lParam); |
||
63 | |||
64 | public ListViewCollapsible() |
||
65 | { |
||
66 | //Activate double buffering |
||
67 | SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true); |
||
68 | |||
69 | //Enable the OnNotifyMessage event so we get a chance to filter out |
||
70 | // Windows messages before they get to the form's WndProc |
||
71 | SetStyle(ControlStyles.EnableNotifyMessage, true); |
||
72 | } |
||
73 | |||
74 | public bool GetCollapsed(ListViewGroup group) |
||
75 | { |
||
76 | //var index = group.ListView.Groups.IndexOf(group); |
||
77 | var index = ExtractID(group); |
||
78 | return GetOneState(this, index, GroupState.LVGS_COLLAPSED); |
||
79 | } |
||
80 | |||
81 | public void SetCollapsed(ListViewGroup group, bool value) |
||
82 | { |
||
83 | //var index = group.ListView.Groups.IndexOf(group); |
||
84 | var index = ExtractID(group); |
||
85 | SetOneState(this, index, value, GroupState.LVGS_COLLAPSED); |
||
86 | } |
||
87 | |||
88 | private GroupState GetState(ListViewCollapsible olv, int groupId) |
||
89 | { |
||
90 | return (GroupState)SendMessage(olv.Handle, LVM_GETGROUPSTATE, groupId, (int)GroupState.LVGS_ALL); |
||
91 | } |
||
92 | |||
93 | private int SetGroupInfo(ListViewCollapsible olv, int groupId, LVGROUP2 group) |
||
94 | { |
||
95 | return (int)SendMessage(olv.Handle, LVM_SETGROUPINFO, groupId, ref group); |
||
96 | } |
||
97 | |||
98 | private int SetState(ListViewCollapsible olv, int groupId, GroupState newState, GroupState mask) |
||
99 | { |
||
100 | LVGROUP2 group = new LVGROUP2(); |
||
101 | group.cbSize = ((uint)Marshal.SizeOf(typeof(LVGROUP2))); |
||
102 | group.mask = (uint)GroupMask.LVGF_STATE; |
||
103 | group.state = (uint)newState; |
||
104 | group.stateMask = (uint)mask; |
||
105 | return SetGroupInfo(olv,groupId, group); |
||
106 | } |
||
107 | |||
108 | private void SetOneState(ListViewCollapsible olv, int groupId, bool value, GroupState mask) |
||
109 | { |
||
110 | if (!groupStateMasks.ContainsKey(groupId)) |
||
111 | { |
||
112 | groupStateMasks.Add(groupId, GetState(olv, groupId)); |
||
113 | } |
||
114 | |||
115 | groupStateMasks[groupId] ^= mask; |
||
116 | |||
117 | if (!groupStates.ContainsKey(groupId)) |
||
118 | { |
||
119 | groupStates.Add(groupId, GetState(olv, groupId)); |
||
120 | } |
||
121 | |||
122 | if (value) |
||
123 | { |
||
124 | groupStates[groupId] ^= mask; |
||
125 | } |
||
126 | else |
||
127 | { |
||
128 | groupStates[groupId] &= ~mask; |
||
129 | } |
||
130 | |||
131 | if (Created) |
||
132 | SetState(olv, groupId, groupStates[groupId], mask); |
||
133 | } |
||
134 | |||
135 | private bool GetOneState(ListViewCollapsible olv, int groupId, GroupState mask) |
||
136 | { |
||
137 | if (Created) |
||
138 | { |
||
139 | if (!groupStates.ContainsKey(groupId)) |
||
140 | { |
||
141 | groupStates.Add(groupId, GetState(olv, groupId)); |
||
142 | return (groupStates[groupId] & mask) == mask; |
||
143 | |||
144 | } |
||
145 | |||
146 | groupStates[groupId] = GetState(olv, groupId); |
||
147 | } |
||
148 | |||
149 | return (groupStates[groupId] & mask) == mask; |
||
150 | } |
||
151 | |||
152 | protected override void OnMouseDown(MouseEventArgs e) |
||
153 | { |
||
154 | base.OnMouseDown(e); |
||
155 | |||
156 | var group = TestGroupHit(e); |
||
157 | if (group == null) |
||
158 | { |
||
159 | return; |
||
160 | } |
||
161 | |||
162 | switch (e.Clicks) |
||
163 | { |
||
164 | case 1: |
||
165 | if (GroupHeaderClick != null) |
||
166 | { |
||
167 | GroupHeaderClick(this, group); |
||
168 | } |
||
169 | break; |
||
170 | } |
||
171 | } |
||
172 | |||
173 | private ListViewGroup TestGroupHit(MouseEventArgs e) |
||
174 | { |
||
175 | var ht = new LVHITTESTINFO(); |
||
176 | ht.pt.x = e.X; |
||
177 | ht.pt.y = e.Y; |
||
178 | var msg = View == System.Windows.Forms.View.Details ? LVM_SUBITEMHITTEST : LVM_HITTEST; |
||
179 | var value = (int)SendMessage(Handle, msg, -1, ref ht); |
||
180 | |||
181 | if (value != -1 && ht.flags.HasFlag(LVHITTESTFLAGS.LVHT_EX_GROUP_HEADER)) |
||
182 | { |
||
183 | return FindGroupById(value); |
||
184 | } |
||
185 | |||
186 | return null; |
||
187 | } |
||
188 | |||
189 | private ListViewGroup FindGroupById(int id) |
||
190 | { |
||
191 | foreach (ListViewGroup group in Groups) |
||
192 | { |
||
193 | if (ExtractID(group) == id) |
||
194 | { |
||
195 | return group; |
||
196 | } |
||
197 | } |
||
198 | |||
199 | return null; |
||
200 | } |
||
201 | |||
202 | public static int ExtractID(ListViewGroup group) |
||
203 | { |
||
204 | try |
||
205 | { |
||
206 | return (int)group |
||
207 | .GetType() |
||
208 | .GetProperty("ID", BindingFlags.NonPublic | BindingFlags.Instance) |
||
209 | .GetValue(group, new object[0]); |
||
210 | } |
||
211 | catch |
||
212 | { |
||
213 | return -1; |
||
214 | } |
||
215 | } |
||
216 | |||
217 | protected override void OnNotifyMessage(Message m) |
||
218 | { |
||
219 | //Filter out the WM_ERASEBKGND message |
||
220 | if (m.Msg != 0x14) |
||
221 | { |
||
222 | base.OnNotifyMessage(m); |
||
223 | } |
||
224 | } |
||
225 | |||
226 | /// <summary> |
||
227 | /// convert the IntPtr LParam to an Point. |
||
228 | /// </summary> |
||
229 | private static POINT LParamToPoint(IntPtr lparam) |
||
230 | { |
||
231 | return new POINT(lparam.ToInt32() & 0xFFFF, lparam.ToInt32() >> 16); |
||
232 | } |
||
233 | |||
234 | #region Natives |
||
235 | |||
236 | /// <summary> |
||
237 | /// see http://msdn.microsoft.com/en-us/library/bb774754%28v=VS.85%29.aspx |
||
238 | /// </summary> |
||
239 | [Flags] |
||
240 | public enum LVHITTESTFLAGS : uint |
||
241 | { |
||
242 | LVHT_NOWHERE = 0x00000001, |
||
243 | LVHT_ONITEMICON = 0x00000002, |
||
244 | LVHT_ONITEMLABEL = 0x00000004, |
||
245 | LVHT_ONITEMSTATEICON = 0x00000008, |
||
246 | LVHT_ONITEM = (LVHT_ONITEMICON | LVHT_ONITEMLABEL | LVHT_ONITEMSTATEICON), |
||
247 | LVHT_ABOVE = 0x00000008, |
||
248 | LVHT_BELOW = 0x00000010, |
||
249 | LVHT_TORIGHT = 0x00000020, |
||
250 | LVHT_TOLEFT = 0x00000040, |
||
251 | // Vista/Win7+ only |
||
252 | LVHT_EX_GROUP_HEADER = 0x10000000, |
||
253 | LVHT_EX_GROUP_FOOTER = 0x20000000, |
||
254 | LVHT_EX_GROUP_COLLAPSE = 0x40000000, |
||
255 | LVHT_EX_GROUP_BACKGROUND = 0x80000000, |
||
256 | LVHT_EX_GROUP_STATEICON = 0x01000000, |
||
257 | LVHT_EX_GROUP_SUBSETLINK = 0x02000000, |
||
258 | } |
||
259 | |||
260 | /// <summary> |
||
261 | /// see http://msdn.microsoft.com/en-us/library/dd162805%28v=VS.85%29.aspx |
||
262 | /// </summary> |
||
263 | [StructLayout(LayoutKind.Sequential)] |
||
264 | public struct POINT |
||
265 | { |
||
266 | public POINT(int x, int y) |
||
267 | { |
||
268 | this.x = x; |
||
269 | this.y = y; |
||
270 | } |
||
271 | public int x; |
||
272 | public int y; |
||
273 | } |
||
274 | |||
275 | [StructLayout(LayoutKind.Sequential)] |
||
276 | public struct LVGROUP |
||
277 | { |
||
278 | public uint cbSize; |
||
279 | public uint mask; |
||
280 | [MarshalAs(UnmanagedType.LPTStr)] |
||
281 | public string pszHeader; |
||
282 | public int cchHeader; |
||
283 | [MarshalAs(UnmanagedType.LPTStr)] |
||
284 | public string pszFooter; |
||
285 | public int cchFooter; |
||
286 | public int iGroupId; |
||
287 | public uint stateMask; |
||
288 | public uint state; |
||
289 | public uint uAlign; |
||
290 | } |
||
291 | |||
292 | [StructLayout(LayoutKind.Sequential)] |
||
293 | public struct LVGROUP2 |
||
294 | { |
||
295 | public uint cbSize; |
||
296 | public uint mask; |
||
297 | [MarshalAs(UnmanagedType.LPTStr)] |
||
298 | public string pszHeader; |
||
299 | public uint cchHeader; |
||
300 | [MarshalAs(UnmanagedType.LPTStr)] |
||
301 | public string pszFooter; |
||
302 | public int cchFooter; |
||
303 | public int iGroupId; |
||
304 | public uint stateMask; |
||
305 | public uint state; |
||
306 | public uint uAlign; |
||
307 | [MarshalAs(UnmanagedType.LPTStr)] |
||
308 | public string pszSubtitle; |
||
309 | public uint cchSubtitle; |
||
310 | [MarshalAs(UnmanagedType.LPTStr)] |
||
311 | public string pszTask; |
||
312 | public uint cchTask; |
||
313 | [MarshalAs(UnmanagedType.LPTStr)] |
||
314 | public string pszDescriptionTop; |
||
315 | public uint cchDescriptionTop; |
||
316 | [MarshalAs(UnmanagedType.LPTStr)] |
||
317 | public string pszDescriptionBottom; |
||
318 | public uint cchDescriptionBottom; |
||
319 | public int iTitleImage; |
||
320 | public int iExtendedImage; |
||
321 | public int iFirstItem; // Read only |
||
322 | public int cItems; // Read only |
||
323 | [MarshalAs(UnmanagedType.LPTStr)] |
||
324 | public string pszSubsetTitle; // NULL if group is not subset |
||
325 | public uint cchSubsetTitle; |
||
326 | } |
||
327 | |||
328 | /// <summary> |
||
329 | /// These values indicate what is the state of the group. These values |
||
330 | /// are taken directly from the SDK and many are not used by ObjectListView. |
||
331 | /// </summary> |
||
332 | [Flags] |
||
333 | public enum GroupState |
||
334 | { |
||
335 | /// <summary> |
||
336 | /// Normal |
||
337 | /// </summary> |
||
338 | LVGS_NORMAL = 0x0, |
||
339 | |||
340 | /// <summary> |
||
341 | /// Collapsed |
||
342 | /// </summary> |
||
343 | LVGS_COLLAPSED = 0x1, |
||
344 | |||
345 | /// <summary> |
||
346 | /// Hidden |
||
347 | /// </summary> |
||
348 | LVGS_HIDDEN = 0x2, |
||
349 | |||
350 | /// <summary> |
||
351 | /// NoHeader |
||
352 | /// </summary> |
||
353 | LVGS_NOHEADER = 0x4, |
||
354 | |||
355 | /// <summary> |
||
356 | /// Can be collapsed |
||
357 | /// </summary> |
||
358 | LVGS_COLLAPSIBLE = 0x8, |
||
359 | |||
360 | /// <summary> |
||
361 | /// Has focus |
||
362 | /// </summary> |
||
363 | LVGS_FOCUSED = 0x10, |
||
364 | |||
365 | /// <summary> |
||
366 | /// Is Selected |
||
367 | /// </summary> |
||
368 | LVGS_SELECTED = 0x20, |
||
369 | |||
370 | /// <summary> |
||
371 | /// Is subsetted |
||
372 | /// </summary> |
||
373 | LVGS_SUBSETED = 0x40, |
||
374 | |||
375 | /// <summary> |
||
376 | /// Subset link has focus |
||
377 | /// </summary> |
||
378 | LVGS_SUBSETLINKFOCUSED = 0x80, |
||
379 | |||
380 | /// <summary> |
||
381 | /// All styles |
||
382 | /// </summary> |
||
383 | LVGS_ALL = 0xFFFF |
||
384 | } |
||
385 | |||
386 | /// <summary> |
||
387 | /// This mask indicates which members of a LVGROUP have valid data. These values |
||
388 | /// are taken directly from the SDK and many are not used by ObjectListView. |
||
389 | /// </summary> |
||
390 | [Flags] |
||
391 | public enum GroupMask |
||
392 | { |
||
393 | /// <summary> |
||
394 | /// No mask |
||
395 | /// </summary> |
||
396 | LVGF_NONE = 0, |
||
397 | |||
398 | /// <summary> |
||
399 | /// Group has header |
||
400 | /// </summary> |
||
401 | LVGF_HEADER = 1, |
||
402 | |||
403 | /// <summary> |
||
404 | /// Group has footer |
||
405 | /// </summary> |
||
406 | LVGF_FOOTER = 2, |
||
407 | |||
408 | /// <summary> |
||
409 | /// Group has state |
||
410 | /// </summary> |
||
411 | LVGF_STATE = 4, |
||
412 | |||
413 | /// <summary> |
||
414 | /// |
||
415 | /// </summary> |
||
416 | LVGF_ALIGN = 8, |
||
417 | |||
418 | /// <summary> |
||
419 | /// |
||
420 | /// </summary> |
||
421 | LVGF_GROUPID = 0x10, |
||
422 | |||
423 | /// <summary> |
||
424 | /// pszSubtitle is valid |
||
425 | /// </summary> |
||
426 | LVGF_SUBTITLE = 0x00100, |
||
427 | |||
428 | /// <summary> |
||
429 | /// pszTask is valid |
||
430 | /// </summary> |
||
431 | LVGF_TASK = 0x00200, |
||
432 | |||
433 | /// <summary> |
||
434 | /// pszDescriptionTop is valid |
||
435 | /// </summary> |
||
436 | LVGF_DESCRIPTIONTOP = 0x00400, |
||
437 | |||
438 | /// <summary> |
||
439 | /// pszDescriptionBottom is valid |
||
440 | /// </summary> |
||
441 | LVGF_DESCRIPTIONBOTTOM = 0x00800, |
||
442 | |||
443 | /// <summary> |
||
444 | /// iTitleImage is valid |
||
445 | /// </summary> |
||
446 | LVGF_TITLEIMAGE = 0x01000, |
||
447 | |||
448 | /// <summary> |
||
449 | /// iExtendedImage is valid |
||
450 | /// </summary> |
||
451 | LVGF_EXTENDEDIMAGE = 0x02000, |
||
452 | |||
453 | /// <summary> |
||
454 | /// iFirstItem and cItems are valid |
||
455 | /// </summary> |
||
456 | LVGF_ITEMS = 0x04000, |
||
457 | |||
458 | /// <summary> |
||
459 | /// pszSubsetTitle is valid |
||
460 | /// </summary> |
||
461 | LVGF_SUBSET = 0x08000, |
||
462 | |||
463 | /// <summary> |
||
464 | /// readonly, cItems holds count of items in visible subset, iFirstItem is valid |
||
465 | /// </summary> |
||
466 | LVGF_SUBSETITEMS = 0x10000 |
||
467 | } |
||
468 | |||
469 | /// <summary> |
||
470 | /// see http://msdn.microsoft.com/en-us/library/bb774754%28v=VS.85%29.aspx |
||
471 | /// </summary> |
||
472 | [StructLayout(LayoutKind.Sequential)] |
||
473 | public struct LVHITTESTINFO |
||
474 | { |
||
475 | public POINT pt; |
||
476 | public LVHITTESTFLAGS flags; |
||
477 | public int iItem; |
||
478 | public int iSubItem; |
||
479 | // Vista/Win7+ |
||
480 | public int iGroup; |
||
481 | } |
||
482 | |||
483 | #endregion |
||
484 | |||
485 | } |
||
486 | } |