QuickImage – Blame information for rev 1

Subversion Repositories:
Rev:
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 }