Skip to content

Commit e805f92

Browse files
Move sub-classes of "PropertyGridView" classes to their own files (#4603)
1 parent 0479a3e commit e805f92

File tree

5 files changed

+907
-853
lines changed

5 files changed

+907
-853
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#nullable disable
6+
7+
using System.Collections;
8+
9+
namespace System.Windows.Forms.PropertyGridInternal
10+
{
11+
internal partial class PropertyGridView
12+
{
13+
internal class GridPositionData
14+
{
15+
readonly ArrayList expandedState;
16+
readonly GridEntryCollection selectedItemTree;
17+
readonly int itemRow;
18+
readonly int itemCount;
19+
20+
public GridPositionData(PropertyGridView gridView)
21+
{
22+
selectedItemTree = gridView.GetGridEntryHierarchy(gridView.selectedGridEntry);
23+
expandedState = gridView.SaveHierarchyState(gridView.topLevelGridEntries);
24+
itemRow = gridView.selectedRow;
25+
itemCount = gridView.totalProps;
26+
}
27+
28+
public GridEntry Restore(PropertyGridView gridView)
29+
{
30+
gridView.RestoreHierarchyState(expandedState);
31+
GridEntry entry = gridView.FindEquivalentGridEntry(selectedItemTree);
32+
33+
if (entry is not null)
34+
{
35+
gridView.SelectGridEntry(entry, true);
36+
37+
int delta = gridView.selectedRow - itemRow;
38+
if (delta != 0 && gridView.ScrollBar.Visible)
39+
{
40+
if (itemRow < gridView.visibleRows)
41+
{
42+
delta += gridView.GetScrollOffset();
43+
44+
if (delta < 0)
45+
{
46+
delta = 0;
47+
}
48+
else if (delta > gridView.ScrollBar.Maximum)
49+
{
50+
delta = gridView.ScrollBar.Maximum - 1;
51+
}
52+
gridView.SetScrollOffset(delta);
53+
}
54+
}
55+
}
56+
return entry;
57+
}
58+
}
59+
}
60+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
namespace System.Windows.Forms.PropertyGridInternal
6+
{
7+
internal partial class PropertyGridView
8+
{
9+
internal interface IMouseHookClient
10+
{
11+
// return true if the click is handled, false
12+
// to pass it on
13+
bool OnClickHooked();
14+
}
15+
}
16+
}
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#nullable disable
6+
7+
using System.ComponentModel;
8+
using System.Diagnostics;
9+
using System.Runtime.InteropServices;
10+
using static Interop;
11+
12+
namespace System.Windows.Forms.PropertyGridInternal
13+
{
14+
internal partial class PropertyGridView
15+
{
16+
internal class MouseHook
17+
{
18+
private readonly PropertyGridView gridView;
19+
private readonly Control control;
20+
private readonly IMouseHookClient client;
21+
22+
internal uint _thisProcessID;
23+
private GCHandle _mouseHookRoot;
24+
private IntPtr _mouseHookHandle = IntPtr.Zero;
25+
private bool hookDisable;
26+
27+
private bool processing;
28+
29+
public MouseHook(Control control, IMouseHookClient client, PropertyGridView gridView)
30+
{
31+
this.control = control;
32+
this.gridView = gridView;
33+
this.client = client;
34+
#if DEBUG
35+
callingStack = Environment.StackTrace;
36+
#endif
37+
}
38+
39+
#if DEBUG
40+
readonly string callingStack;
41+
~MouseHook()
42+
{
43+
Debug.Assert(_mouseHookHandle == IntPtr.Zero, "Finalizing an active mouse hook. This will crash the process. Calling stack: " + callingStack);
44+
}
45+
#endif
46+
47+
public bool DisableMouseHook
48+
{
49+
set
50+
{
51+
hookDisable = value;
52+
if (value)
53+
{
54+
UnhookMouse();
55+
}
56+
}
57+
}
58+
59+
public virtual bool HookMouseDown
60+
{
61+
get
62+
{
63+
GC.KeepAlive(this);
64+
return _mouseHookHandle != IntPtr.Zero;
65+
}
66+
set
67+
{
68+
if (value && !hookDisable)
69+
{
70+
HookMouse();
71+
}
72+
else
73+
{
74+
UnhookMouse();
75+
}
76+
}
77+
}
78+
79+
public void Dispose()
80+
{
81+
UnhookMouse();
82+
}
83+
84+
/// <summary>
85+
/// Sets up the needed windows hooks to catch messages.
86+
/// </summary>
87+
private void HookMouse()
88+
{
89+
GC.KeepAlive(this);
90+
// Locking 'this' here is ok since this is an internal class.
91+
lock (this)
92+
{
93+
if (_mouseHookHandle != IntPtr.Zero)
94+
{
95+
return;
96+
}
97+
98+
if (_thisProcessID == 0)
99+
{
100+
User32.GetWindowThreadProcessId(control, out _thisProcessID);
101+
}
102+
103+
var hook = new User32.HOOKPROC(new MouseHookObject(this).Callback);
104+
_mouseHookRoot = GCHandle.Alloc(hook);
105+
_mouseHookHandle = User32.SetWindowsHookExW(
106+
User32.WH.MOUSE,
107+
hook,
108+
IntPtr.Zero,
109+
Kernel32.GetCurrentThreadId());
110+
Debug.Assert(_mouseHookHandle != IntPtr.Zero, "Failed to install mouse hook");
111+
Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "DropDownHolder:HookMouse()");
112+
}
113+
}
114+
115+
/// <summary>
116+
/// HookProc used for catch mouse messages.
117+
/// </summary>
118+
private unsafe IntPtr MouseHookProc(User32.HC nCode, IntPtr wparam, IntPtr lparam)
119+
{
120+
GC.KeepAlive(this);
121+
if (nCode == User32.HC.ACTION)
122+
{
123+
User32.MOUSEHOOKSTRUCT* mhs = (User32.MOUSEHOOKSTRUCT*)lparam;
124+
if (mhs is not null)
125+
{
126+
switch (unchecked((User32.WM)(long)wparam))
127+
{
128+
case User32.WM.LBUTTONDOWN:
129+
case User32.WM.MBUTTONDOWN:
130+
case User32.WM.RBUTTONDOWN:
131+
case User32.WM.NCLBUTTONDOWN:
132+
case User32.WM.NCMBUTTONDOWN:
133+
case User32.WM.NCRBUTTONDOWN:
134+
case User32.WM.MOUSEACTIVATE:
135+
if (ProcessMouseDown(mhs->hWnd, mhs->pt.X, mhs->pt.Y))
136+
{
137+
return (IntPtr)1;
138+
}
139+
break;
140+
}
141+
}
142+
}
143+
144+
return User32.CallNextHookEx(new HandleRef(this, _mouseHookHandle), nCode, wparam, lparam);
145+
}
146+
147+
/// <summary>
148+
/// Removes the windowshook that was installed.
149+
/// </summary>
150+
private void UnhookMouse()
151+
{
152+
GC.KeepAlive(this);
153+
// Locking 'this' here is ok since this is an internal class.
154+
lock (this)
155+
{
156+
if (_mouseHookHandle != IntPtr.Zero)
157+
{
158+
User32.UnhookWindowsHookEx(new HandleRef(this, _mouseHookHandle));
159+
_mouseHookRoot.Free();
160+
_mouseHookHandle = IntPtr.Zero;
161+
Debug.WriteLineIf(CompModSwitches.DebugGridView.TraceVerbose, "DropDownHolder:UnhookMouse()");
162+
}
163+
}
164+
}
165+
166+
/*
167+
* Here is where we force validation on any clicks outside the
168+
*/
169+
private bool ProcessMouseDown(IntPtr hWnd, int x, int y)
170+
{
171+
// If we put up the "invalid" message box, it appears this
172+
// method is getting called re-entrantly when it shouldn't be.
173+
// this prevents us from recursing.
174+
if (processing)
175+
{
176+
return false;
177+
}
178+
179+
IntPtr hWndAtPoint = hWnd;
180+
IntPtr handle = control.Handle;
181+
Control ctrlAtPoint = FromHandle(hWndAtPoint);
182+
183+
// if it's us or one of our children, just process as normal
184+
if (hWndAtPoint != handle && !control.Contains(ctrlAtPoint))
185+
{
186+
Debug.Assert(_thisProcessID != 0, "Didn't get our process id!");
187+
188+
// Make sure the window is in our process
189+
User32.GetWindowThreadProcessId(hWndAtPoint, out uint pid);
190+
191+
// if this isn't our process, unhook the mouse.
192+
if (pid != _thisProcessID)
193+
{
194+
HookMouseDown = false;
195+
return false;
196+
}
197+
198+
bool needCommit = false;
199+
200+
// if this a sibling control (e.g. the drop down or buttons), just forward the message and skip the commit
201+
needCommit = ctrlAtPoint is null ? true : !gridView.IsSiblingControl(control, ctrlAtPoint);
202+
203+
try
204+
{
205+
processing = true;
206+
207+
if (needCommit)
208+
{
209+
if (client.OnClickHooked())
210+
{
211+
return true; // there was an error, so eat the mouse
212+
}
213+
}
214+
}
215+
finally
216+
{
217+
processing = false;
218+
}
219+
220+
// cancel our hook at this point
221+
HookMouseDown = false;
222+
//gridView.UnfocusSelection();
223+
}
224+
return false;
225+
}
226+
227+
/// <summary>
228+
/// Forwards messageHook calls to ToolTip.messageHookProc
229+
/// </summary>
230+
private class MouseHookObject
231+
{
232+
internal WeakReference reference;
233+
234+
public MouseHookObject(MouseHook parent)
235+
{
236+
reference = new WeakReference(parent, false);
237+
}
238+
239+
public virtual IntPtr Callback(User32.HC nCode, IntPtr wparam, IntPtr lparam)
240+
{
241+
IntPtr ret = IntPtr.Zero;
242+
try
243+
{
244+
MouseHook control = (MouseHook)reference.Target;
245+
if (control is not null)
246+
{
247+
ret = control.MouseHookProc(nCode, wparam, lparam);
248+
}
249+
}
250+
catch
251+
{
252+
// ignore
253+
}
254+
return ret;
255+
}
256+
}
257+
}
258+
}
259+
}

0 commit comments

Comments
 (0)