1- using System . Windows . Interop ;
1+ using System . Windows . Interop ;
22
33namespace MaterialDesignThemes . Wpf ;
44
@@ -64,25 +64,25 @@ public static PaddingMode GetPaddingMode(DependencyObject element)
6464 private static readonly DependencyProperty HorizontalScrollHookProperty = DependencyProperty . RegisterAttached (
6565 "HorizontalScrollHook" , typeof ( HwndSourceHook ) , typeof ( ScrollViewerAssist ) , new PropertyMetadata ( null ) ) ;
6666
67- private static readonly DependencyProperty HorizontalScrollSourceProperty = DependencyProperty . RegisterAttached (
68- "HorizontalScrollSource " , typeof ( HwndSource ) , typeof ( ScrollViewerAssist ) , new PropertyMetadata ( null ) ) ;
67+ private static readonly DependencyProperty BubbleVerticalScrollHookProperty = DependencyProperty . RegisterAttached (
68+ "BubbleVerticalScrollHook " , typeof ( HwndSourceHook ) , typeof ( ScrollViewerAssist ) , new PropertyMetadata ( null ) ) ;
6969
7070 public static readonly DependencyProperty SupportHorizontalScrollProperty = DependencyProperty . RegisterAttached (
7171 "SupportHorizontalScroll" , typeof ( bool ) , typeof ( ScrollViewerAssist ) , new PropertyMetadata ( false , OnSupportHorizontalScrollChanged ) ) ;
7272
7373 private static void OnSupportHorizontalScrollChanged ( DependencyObject d , DependencyPropertyChangedEventArgs e )
7474 {
7575 //Based on: https://blog.walterlv.com/post/handle-horizontal-scrolling-of-touchpad-en.html
76- if ( d is ScrollViewer scrollViewer )
76+ if ( d is ScrollViewer sv )
7777 {
78- if ( scrollViewer . IsLoaded )
78+ if ( sv . IsLoaded )
7979 {
80- DoOnLoaded ( scrollViewer ) ;
80+ DoOnLoaded ( sv ) ;
8181 }
8282 else
8383 {
84- WeakEventManager < ScrollViewer , RoutedEventArgs > . AddHandler ( scrollViewer , nameof ( ScrollViewer . Loaded ) , OnLoaded ) ;
85- WeakEventManager < ScrollViewer , RoutedEventArgs > . AddHandler ( scrollViewer , nameof ( ScrollViewer . Unloaded ) , OnUnloaded ) ;
84+ RegisterForLoadedEvent ( sv ) ;
85+ RegisterForUnloadedEvent ( sv ) ;
8686 }
8787 }
8888
@@ -94,10 +94,35 @@ static void OnLoaded(object? sender, RoutedEventArgs e)
9494 }
9595 }
9696
97+ static void UnregisterForLoadedEvent ( ScrollViewer sv )
98+ {
99+ WeakEventManager < ScrollViewer , RoutedEventArgs > . RemoveHandler ( sv , nameof ( ScrollViewer . Loaded ) , OnLoaded ) ;
100+ }
101+
102+ static void RegisterForLoadedEvent ( ScrollViewer sv )
103+ {
104+ // Avoid double registrations
105+ UnregisterForLoadedEvent ( sv ) ;
106+ WeakEventManager < ScrollViewer , RoutedEventArgs > . AddHandler ( sv , nameof ( ScrollViewer . Loaded ) , OnLoaded ) ;
107+ }
108+
109+ static void UnregisterForUnloadedEvent ( ScrollViewer sv )
110+ {
111+ WeakEventManager < ScrollViewer , RoutedEventArgs > . RemoveHandler ( sv , nameof ( ScrollViewer . Unloaded ) , OnUnloaded ) ;
112+ }
113+
114+ static void RegisterForUnloadedEvent ( ScrollViewer sv )
115+ {
116+ // Avoid double registrations
117+ UnregisterForUnloadedEvent ( sv ) ;
118+ WeakEventManager < ScrollViewer , RoutedEventArgs > . AddHandler ( sv , nameof ( ScrollViewer . Unloaded ) , OnUnloaded ) ;
119+ }
120+
97121 static void DoOnLoaded ( ScrollViewer sv )
98122 {
99123 if ( GetSupportHorizontalScroll ( sv ) )
100124 {
125+ RegisterForUnloadedEvent ( sv ) ;
101126 RegisterHook ( sv ) ;
102127 }
103128 else
@@ -114,30 +139,23 @@ static void OnUnloaded(object? sender, RoutedEventArgs e)
114139 }
115140 }
116141
117- static void RemoveHandlers ( ScrollViewer scrollViewer )
118- {
119- WeakEventManager < ScrollViewer , RoutedEventArgs > . RemoveHandler ( scrollViewer , nameof ( ScrollViewer . Loaded ) , OnLoaded ) ;
120- WeakEventManager < ScrollViewer , RoutedEventArgs > . RemoveHandler ( scrollViewer , nameof ( ScrollViewer . Unloaded ) , OnUnloaded ) ;
121- }
122-
123- static void RemoveHook ( ScrollViewer scrollViewer )
142+ static void RemoveHook ( ScrollViewer sv )
124143 {
125- if ( scrollViewer . GetValue ( HorizontalScrollHookProperty ) is HwndSourceHook hook &&
126- scrollViewer . GetValue ( HorizontalScrollSourceProperty ) is HwndSource source )
144+ var source = PresentationSource . FromVisual ( sv ) as HwndSource ;
145+ if ( source is not null && sv . GetValue ( HorizontalScrollHookProperty ) is HwndSourceHook hook )
127146 {
128147 source . RemoveHook ( hook ) ;
129- scrollViewer . SetValue ( HorizontalScrollHookProperty , null ) ;
148+ sv . SetValue ( HorizontalScrollHookProperty , null ) ;
130149 }
131150 }
132151
133- static void RegisterHook ( ScrollViewer scrollViewer )
152+ static void RegisterHook ( ScrollViewer sv )
134153 {
135- RemoveHook ( scrollViewer ) ;
136- if ( PresentationSource . FromVisual ( scrollViewer ) is HwndSource source )
154+ RemoveHook ( sv ) ;
155+ if ( PresentationSource . FromVisual ( sv ) is HwndSource source )
137156 {
138157 HwndSourceHook hook = Hook ;
139- scrollViewer . SetValue ( HorizontalScrollSourceProperty , source ) ;
140- scrollViewer . SetValue ( HorizontalScrollHookProperty , hook ) ;
158+ sv . SetValue ( HorizontalScrollHookProperty , hook ) ;
141159 source . AddHook ( hook ) ;
142160 }
143161
@@ -148,15 +166,15 @@ IntPtr Hook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled
148166 const int WM_NCDESTROY = 0x0082 ;
149167 switch ( msg )
150168 {
151- case WM_MOUSEHWHEEL when scrollViewer . IsMouseOver :
169+ case WM_MOUSEHWHEEL when sv . IsMouseOver :
152170 int tilt = ( short ) ( ( wParam . ToInt64 ( ) >> 16 ) & 0xFFFF ) ;
153- scrollViewer . ScrollToHorizontalOffset ( scrollViewer . HorizontalOffset + tilt ) ;
171+ sv . ScrollToHorizontalOffset ( sv . HorizontalOffset + tilt ) ;
154172 return ( IntPtr ) 1 ;
155173 case WM_DESTROY :
156174 case WM_NCDESTROY :
157- RemoveHandlers ( scrollViewer ) ;
158- var source = PresentationSource . FromVisual ( scrollViewer ) as HwndSource ;
159- source ? . RemoveHook ( Hook ) ;
175+ UnregisterForLoadedEvent ( sv ) ;
176+ UnregisterForUnloadedEvent ( sv ) ;
177+ RemoveHook ( sv ) ;
160178 break ;
161179 }
162180 return IntPtr . Zero ;
@@ -166,19 +184,19 @@ IntPtr Hook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled
166184
167185 public static readonly DependencyProperty BubbleVerticalScrollProperty = DependencyProperty . RegisterAttached (
168186 "BubbleVerticalScroll" , typeof ( bool ) , typeof ( ScrollViewerAssist ) , new PropertyMetadata ( false , OnBubbleVerticalScrollChanged ) ) ;
169-
187+
170188 private static void OnBubbleVerticalScrollChanged ( DependencyObject d , DependencyPropertyChangedEventArgs e )
171189 {
172- if ( d is ScrollViewer scrollViewer )
190+ if ( d is ScrollViewer sv )
173191 {
174- if ( scrollViewer . IsLoaded )
192+ if ( sv . IsLoaded )
175193 {
176- DoOnLoaded ( scrollViewer ) ;
194+ DoOnLoaded ( sv ) ;
177195 }
178196 else
179197 {
180- WeakEventManager < ScrollViewer , RoutedEventArgs > . AddHandler ( scrollViewer , nameof ( ScrollViewer . Loaded ) , OnLoaded ) ;
181- WeakEventManager < ScrollViewer , RoutedEventArgs > . AddHandler ( scrollViewer , nameof ( ScrollViewer . Unloaded ) , OnUnloaded ) ;
198+ RegisterForLoadedEvent ( sv ) ;
199+ RegisterForUnloadedEvent ( sv ) ;
182200 }
183201 }
184202
@@ -190,10 +208,48 @@ static void OnLoaded(object? sender, RoutedEventArgs e)
190208 }
191209 }
192210
211+ static void UnregisterForLoadedEvent ( ScrollViewer sv )
212+ {
213+ WeakEventManager < ScrollViewer , RoutedEventArgs > . RemoveHandler ( sv , nameof ( ScrollViewer . Loaded ) , OnLoaded ) ;
214+ }
215+
216+ static void RegisterForLoadedEvent ( ScrollViewer sv )
217+ {
218+ // Avoid double registrations
219+ UnregisterForLoadedEvent ( sv ) ;
220+ WeakEventManager < ScrollViewer , RoutedEventArgs > . AddHandler ( sv , nameof ( ScrollViewer . Loaded ) , OnLoaded ) ;
221+ }
222+
223+ static void UnregisterForUnloadedEvent ( ScrollViewer sv )
224+ {
225+ WeakEventManager < ScrollViewer , RoutedEventArgs > . RemoveHandler ( sv , nameof ( ScrollViewer . Unloaded ) , OnUnloaded ) ;
226+ }
227+
228+ static void RegisterForUnloadedEvent ( ScrollViewer sv )
229+ {
230+ // Avoid double registrations
231+ UnregisterForUnloadedEvent ( sv ) ;
232+ WeakEventManager < ScrollViewer , RoutedEventArgs > . AddHandler ( sv , nameof ( ScrollViewer . Unloaded ) , OnUnloaded ) ;
233+ }
234+
235+ static void UnregisterForMouseWheelEvent ( ScrollViewer sv )
236+ {
237+ sv . RemoveHandler ( UIElement . MouseWheelEvent , ( RoutedEventHandler ) ScrollViewerOnMouseWheel ) ;
238+ }
239+
240+ static void RegisterForMouseWheelEvent ( ScrollViewer sv )
241+ {
242+ // Avoid double registrations
243+ UnregisterForMouseWheelEvent ( sv ) ;
244+ sv . AddHandler ( UIElement . MouseWheelEvent , ( RoutedEventHandler ) ScrollViewerOnMouseWheel , true ) ;
245+ }
246+
193247 static void DoOnLoaded ( ScrollViewer sv )
194248 {
195249 if ( GetBubbleVerticalScroll ( sv ) )
196250 {
251+ RegisterForUnloadedEvent ( sv ) ;
252+ RegisterForMouseWheelEvent ( sv ) ;
197253 RegisterHook ( sv ) ;
198254 }
199255 else
@@ -206,29 +262,29 @@ static void OnUnloaded(object? sender, RoutedEventArgs e)
206262 {
207263 if ( sender is ScrollViewer sv )
208264 {
209- RemoveHook ( sv ) ;
265+ UnregisterForUnloadedEvent ( sv ) ;
266+ UnregisterForMouseWheelEvent ( sv ) ;
210267 }
211268 }
212269
213- static void RemoveHandlers ( ScrollViewer scrollViewer )
214- {
215- WeakEventManager < ScrollViewer , RoutedEventArgs > . RemoveHandler ( scrollViewer , nameof ( ScrollViewer . Loaded ) , OnLoaded ) ;
216- WeakEventManager < ScrollViewer , RoutedEventArgs > . RemoveHandler ( scrollViewer , nameof ( ScrollViewer . Unloaded ) , OnUnloaded ) ;
217- }
218-
219- static void RemoveHook ( ScrollViewer scrollViewer )
270+ static void RemoveHook ( ScrollViewer sv )
220271 {
221- scrollViewer . RemoveHandler ( UIElement . MouseWheelEvent , ( RoutedEventHandler ) ScrollViewerOnMouseWheel ) ;
272+ var source = PresentationSource . FromVisual ( sv ) as HwndSource ;
273+ if ( source is not null && sv . GetValue ( BubbleVerticalScrollHookProperty ) is HwndSourceHook hook )
274+ {
275+ source . RemoveHook ( hook ) ;
276+ sv . SetValue ( BubbleVerticalScrollHookProperty , null ) ;
277+ }
222278 }
223279
224- static void RegisterHook ( ScrollViewer scrollViewer )
280+ static void RegisterHook ( ScrollViewer sv )
225281 {
226- RemoveHook ( scrollViewer ) ;
227- if ( PresentationSource . FromVisual ( scrollViewer ) is HwndSource source )
282+ RemoveHook ( sv ) ;
283+ if ( PresentationSource . FromVisual ( sv ) is HwndSource source )
228284 {
229285 HwndSourceHook hook = Hook ;
230286 source . AddHook ( hook ) ;
231- scrollViewer . AddHandler ( UIElement . MouseWheelEvent , ( RoutedEventHandler ) ScrollViewerOnMouseWheel , true ) ;
287+ sv . SetValue ( BubbleVerticalScrollHookProperty , hook ) ;
232288 }
233289
234290 IntPtr Hook ( IntPtr hwnd , int msg , IntPtr wParam , IntPtr lParam , ref bool handled )
@@ -239,9 +295,10 @@ IntPtr Hook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled
239295 {
240296 case WM_DESTROY :
241297 case WM_NCDESTROY :
242- RemoveHandlers ( scrollViewer ) ;
243- var source = PresentationSource . FromVisual ( scrollViewer ) as HwndSource ;
244- source ? . RemoveHook ( Hook ) ;
298+ UnregisterForMouseWheelEvent ( sv ) ;
299+ UnregisterForLoadedEvent ( sv ) ;
300+ UnregisterForUnloadedEvent ( sv ) ;
301+ RemoveHook ( sv ) ;
245302 break ;
246303 }
247304 return IntPtr . Zero ;
0 commit comments