Skip to content

Commit daa34c9

Browse files
Fix issue by for BubbleVerticalScroll
1 parent d325710 commit daa34c9

File tree

1 file changed

+99
-18
lines changed

1 file changed

+99
-18
lines changed

src/MaterialDesignThemes.Wpf/ScrollViewerAssist.cs

Lines changed: 99 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Windows.Interop;
1+
using System.Windows.Interop;
22

33
namespace MaterialDesignThemes.Wpf;
44

@@ -67,6 +67,9 @@ public static PaddingMode GetPaddingMode(DependencyObject element)
6767
private static readonly DependencyProperty HorizontalScrollSourceProperty = DependencyProperty.RegisterAttached(
6868
"HorizontalScrollSource", typeof(HwndSource), typeof(ScrollViewerAssist), new PropertyMetadata(null));
6969

70+
private static readonly DependencyProperty BubbleVerticalScrollHookProperty = DependencyProperty.RegisterAttached(
71+
"BubbleVerticalScrollHook", typeof(HwndSourceHook), typeof(ScrollViewerAssist), new PropertyMetadata(null));
72+
7073
public static readonly DependencyProperty SupportHorizontalScrollProperty = DependencyProperty.RegisterAttached(
7174
"SupportHorizontalScroll", typeof(bool), typeof(ScrollViewerAssist), new PropertyMetadata(false, OnSupportHorizontalScrollChanged));
7275

@@ -114,6 +117,12 @@ static void OnUnloaded(object? sender, RoutedEventArgs e)
114117
}
115118
}
116119

120+
static void RemoveHandlers(ScrollViewer scrollViewer)
121+
{
122+
WeakEventManager<ScrollViewer, RoutedEventArgs>.RemoveHandler(scrollViewer, nameof(ScrollViewer.Loaded), OnLoaded);
123+
WeakEventManager<ScrollViewer, RoutedEventArgs>.RemoveHandler(scrollViewer, nameof(ScrollViewer.Unloaded), OnUnloaded);
124+
}
125+
117126
static void RemoveHook(ScrollViewer scrollViewer)
118127
{
119128
if (scrollViewer.GetValue(HorizontalScrollHookProperty) is HwndSourceHook hook &&
@@ -138,12 +147,20 @@ static void RegisterHook(ScrollViewer scrollViewer)
138147
IntPtr Hook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
139148
{
140149
const int WM_MOUSEHWHEEL = 0x020E;
150+
const int WM_DESTROY = 0x0002;
151+
const int WM_NCDESTROY = 0x0082;
141152
switch (msg)
142153
{
143154
case WM_MOUSEHWHEEL when scrollViewer.IsMouseOver:
144155
int tilt = (short)((wParam.ToInt64() >> 16) & 0xFFFF);
145156
scrollViewer.ScrollToHorizontalOffset(scrollViewer.HorizontalOffset + tilt);
146157
return (IntPtr)1;
158+
case WM_DESTROY:
159+
case WM_NCDESTROY:
160+
RemoveHandlers(scrollViewer);
161+
var source = PresentationSource.FromVisual(scrollViewer) as HwndSource;
162+
source?.RemoveHook(Hook);
163+
break;
147164
}
148165
return IntPtr.Zero;
149166
}
@@ -152,19 +169,19 @@ IntPtr Hook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled
152169

153170
public static readonly DependencyProperty BubbleVerticalScrollProperty = DependencyProperty.RegisterAttached(
154171
"BubbleVerticalScroll", typeof(bool), typeof(ScrollViewerAssist), new PropertyMetadata(false, OnBubbleVerticalScrollChanged));
155-
172+
156173
private static void OnBubbleVerticalScrollChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
157174
{
158-
if (d is ScrollViewer scrollViewer)
175+
if (d is ScrollViewer sv)
159176
{
160-
if (scrollViewer.IsLoaded)
177+
if (sv.IsLoaded)
161178
{
162-
DoOnLoaded(scrollViewer);
179+
DoOnLoaded(sv);
163180
}
164181
else
165182
{
166-
WeakEventManager<ScrollViewer, RoutedEventArgs>.AddHandler(scrollViewer, nameof(ScrollViewer.Loaded), OnLoaded);
167-
WeakEventManager<ScrollViewer, RoutedEventArgs>.AddHandler(scrollViewer, nameof(ScrollViewer.Unloaded), OnUnloaded);
183+
RegisterForLoadedEvent(sv);
184+
RegisterForUnloadedEvent(sv);
168185
}
169186
}
170187

@@ -176,10 +193,48 @@ static void OnLoaded(object? sender, RoutedEventArgs e)
176193
}
177194
}
178195

196+
static void UnregisterForLoadedEvent(ScrollViewer sv)
197+
{
198+
WeakEventManager<ScrollViewer, RoutedEventArgs>.RemoveHandler(sv, nameof(ScrollViewer.Loaded), OnLoaded);
199+
}
200+
201+
static void RegisterForLoadedEvent(ScrollViewer sv)
202+
{
203+
// Avoid double registrations
204+
UnregisterForLoadedEvent(sv);
205+
WeakEventManager<ScrollViewer, RoutedEventArgs>.AddHandler(sv, nameof(ScrollViewer.Loaded), OnLoaded);
206+
}
207+
208+
static void UnregisterForUnloadedEvent(ScrollViewer sv)
209+
{
210+
WeakEventManager<ScrollViewer, RoutedEventArgs>.RemoveHandler(sv, nameof(ScrollViewer.Unloaded), OnUnloaded);
211+
}
212+
213+
static void RegisterForUnloadedEvent(ScrollViewer sv)
214+
{
215+
// Avoid double registrations
216+
UnregisterForUnloadedEvent(sv);
217+
WeakEventManager<ScrollViewer, RoutedEventArgs>.AddHandler(sv, nameof(ScrollViewer.Unloaded), OnUnloaded);
218+
}
219+
220+
static void UnregisterForMouseWheelEvent(ScrollViewer sv)
221+
{
222+
sv.RemoveHandler(UIElement.MouseWheelEvent, (RoutedEventHandler)ScrollViewerOnMouseWheel);
223+
}
224+
225+
static void RegisterForMouseWheelEvent(ScrollViewer sv)
226+
{
227+
// Avoid double registrations
228+
UnregisterForMouseWheelEvent(sv);
229+
sv.AddHandler(UIElement.MouseWheelEvent, (RoutedEventHandler)ScrollViewerOnMouseWheel, true);
230+
}
231+
179232
static void DoOnLoaded(ScrollViewer sv)
180233
{
181234
if (GetBubbleVerticalScroll(sv))
182235
{
236+
RegisterForUnloadedEvent(sv);
237+
RegisterForMouseWheelEvent(sv);
183238
RegisterHook(sv);
184239
}
185240
else
@@ -192,29 +247,55 @@ static void OnUnloaded(object? sender, RoutedEventArgs e)
192247
{
193248
if (sender is ScrollViewer sv)
194249
{
195-
RemoveHook(sv);
250+
UnregisterForUnloadedEvent(sv);
251+
UnregisterForMouseWheelEvent(sv);
196252
}
197253
}
198254

199-
static void RemoveHook(ScrollViewer scrollViewer)
255+
static void RemoveHook(ScrollViewer sv)
200256
{
201-
scrollViewer.RemoveHandler(UIElement.MouseWheelEvent, (RoutedEventHandler)ScrollViewerOnMouseWheel);
257+
var source = PresentationSource.FromVisual(sv) as HwndSource;
258+
if (source is not null && sv.GetValue(BubbleVerticalScrollHookProperty) is HwndSourceHook hook)
259+
{
260+
source.RemoveHook(hook);
261+
sv.SetValue(BubbleVerticalScrollHookProperty, null);
262+
}
202263
}
203264

204-
static void RegisterHook(ScrollViewer scrollViewer)
265+
static void RegisterHook(ScrollViewer sv)
205266
{
206-
RemoveHook(scrollViewer);
207-
scrollViewer.AddHandler(UIElement.MouseWheelEvent, (RoutedEventHandler)ScrollViewerOnMouseWheel, true);
267+
RemoveHook(sv);
268+
if (PresentationSource.FromVisual(sv) is HwndSource source)
269+
{
270+
HwndSourceHook hook = Hook;
271+
source.AddHook(hook);
272+
sv.SetValue(BubbleVerticalScrollHookProperty, hook);
273+
}
274+
275+
IntPtr Hook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
276+
{
277+
const int WM_DESTROY = 0x0002;
278+
const int WM_NCDESTROY = 0x0082;
279+
switch (msg)
280+
{
281+
case WM_DESTROY:
282+
case WM_NCDESTROY:
283+
UnregisterForMouseWheelEvent(sv);
284+
UnregisterForLoadedEvent(sv);
285+
UnregisterForUnloadedEvent(sv);
286+
RemoveHook(sv);
287+
break;
288+
}
289+
return IntPtr.Zero;
290+
}
208291
}
209292

210293
// This relay is only needed because the UIElement.AddHandler() has strict requirements for the signature of the passed Delegate
211-
static void ScrollViewerOnMouseWheel(object sender, RoutedEventArgs e) => HandleMouseWheel(sender, (MouseWheelEventArgs)e);
294+
static void ScrollViewerOnMouseWheel(object? sender, RoutedEventArgs e) => HandleMouseWheel(sender, (MouseWheelEventArgs)e);
212295

213-
static void HandleMouseWheel(object sender, MouseWheelEventArgs e)
296+
static void HandleMouseWheel(object? sender, MouseWheelEventArgs e)
214297
{
215-
var scrollViewer = (ScrollViewer)sender;
216-
217-
if (scrollViewer.GetVisualAncestry().Skip(1).FirstOrDefault() is not UIElement parentUiElement)
298+
if (sender is not ScrollViewer sv || sv.GetVisualAncestry().Skip(1).FirstOrDefault() is not UIElement parentUiElement)
218299
return;
219300

220301
// Re-raise the mouse wheel event on the visual parent to bubble it upwards

0 commit comments

Comments
 (0)