@@ -89,6 +89,7 @@ T FindTemplatePart<T>(string name) where T : class =>
89
89
_translation = FindTemplatePart < TranslateTransform > ( TemplateParts . TranslateTransform ) ;
90
90
91
91
ResetViewport ( ) ;
92
+ UpdateScrollDetails ( ) ;
92
93
}
93
94
94
95
protected override void OnContentChanged ( object oldContent , object newContent )
@@ -98,6 +99,8 @@ protected override void OnContentChanged(object oldContent, object newContent)
98
99
{
99
100
fe . Loaded += OnContentLoaded ;
100
101
fe . SizeChanged += OnContentSizeChanged ;
102
+ UpdateScrollDetails ( ) ;
103
+
101
104
_contentSubscriptions . Disposable = Disposable . Create ( ( ) =>
102
105
{
103
106
fe . Loaded -= OnContentLoaded ;
@@ -107,19 +110,18 @@ protected override void OnContentChanged(object oldContent, object newContent)
107
110
108
111
void OnContentLoaded ( object sender , RoutedEventArgs e )
109
112
{
113
+ if ( Content is Canvas )
114
+ {
115
+ UpdateScrollDetails ( ) ;
116
+ }
110
117
if ( AutoFitToCanvas )
111
118
{
112
119
FitToCanvas ( ) ;
113
120
}
114
121
}
115
122
void OnContentSizeChanged ( object sender , SizeChangedEventArgs e )
116
123
{
117
- _contentSize = new Size ( fe . ActualWidth , fe . ActualHeight ) ;
118
- HorizontalZoomCenter = _contentSize . Width / 2 ;
119
- VerticalZoomCenter = _contentSize . Height / 2 ;
120
-
121
- UpdateScrollBars ( ) ;
122
- UpdateScrollVisibility ( ) ;
124
+ UpdateScrollDetails ( ) ;
123
125
}
124
126
}
125
127
@@ -152,28 +154,19 @@ private async void OnZoomLevelChanged()
152
154
}
153
155
154
156
UpdateScrollBars ( ) ;
155
- UpdateScrollVisibility ( ) ;
156
157
await RaiseRenderedContentUpdated ( ) ;
157
158
}
158
159
159
- private void UpdateScrollVisibility ( )
160
+ private async void UpdateScrollDetails ( )
160
161
{
161
- if ( Viewport is { } vp )
162
- {
163
- ToggleScrollBarVisibility ( _scrollH , vp . ActualWidth < ScrollExtentWidth ) ;
164
- ToggleScrollBarVisibility ( _scrollV , vp . ActualHeight < ScrollExtentHeight ) ;
165
- }
166
-
167
- void ToggleScrollBarVisibility ( ScrollBar ? sb , bool value )
162
+ if ( Content is FrameworkElement fe )
168
163
{
169
- if ( sb is null ) return ;
170
-
171
- // Showing/hiding the ScrollBar(s)could cause the ContentPresenter to move as it re-centers.
172
- // This adds unnecessary complexity for the zooming logics as we need to preserve the focal point
173
- // under the cursor position or the pinch center point after zooming.
174
- // To avoid all that, we just make them permanently there for layout calculation.
175
- sb . IsEnabled = value ;
176
- sb . Opacity = value ? 1 : 0 ;
164
+ _contentSize = new Size ( fe . ActualWidth , fe . ActualHeight ) ;
165
+ HorizontalZoomCenter = _contentSize . Width / 2 ;
166
+ VerticalZoomCenter = _contentSize . Height / 2 ;
167
+
168
+ UpdateScrollBars ( ) ;
169
+ await RaiseRenderedContentUpdated ( ) ;
177
170
}
178
171
}
179
172
@@ -207,20 +200,57 @@ private void UpdateScrollBars()
207
200
{
208
201
if ( Viewport is { } vp )
209
202
{
210
- var scrollableWidth = Math . Max ( 0 , ScrollExtentWidth - vp . ActualWidth ) ;
211
- var scrollableHeight = Math . Max ( 0 , ScrollExtentHeight - vp . ActualHeight ) ;
212
-
213
- // since the content is always centered, we need to able to scroll both way equally:
214
- // [Content-Content-Content]
215
- // [=======[Viewport]======]
216
- HorizontalMaxScroll = scrollableWidth / 2 ;
217
- HorizontalMinScroll = - HorizontalMaxScroll ;
218
- VerticalMaxScroll = scrollableHeight / 2 ;
219
- VerticalMinScroll = - VerticalMaxScroll ;
220
-
221
- // update size of thumb
222
- if ( _scrollH is { } ) _scrollH . ViewportSize = vp . ActualWidth ;
223
- if ( _scrollV is { } ) _scrollV . ViewportSize = vp . ActualHeight ;
203
+ if ( Content is Canvas { Children : { Count : > 0 } } canvas )
204
+ {
205
+ var realm = canvas . Children
206
+ . Select ( x => new Rect ( x . ActualOffset . X , x . ActualOffset . Y , x . ActualSize . X , x . ActualSize . Y ) )
207
+ . Aggregate ( RectHelper . Union ) ;
208
+ if ( realm != default )
209
+ {
210
+ realm = realm . Multiply ( ZoomLevel ) . Inflate ( AdditionalMargin ) ;
211
+
212
+ HorizontalMinScroll = - realm . Right + ( vp . ActualWidth / 2 ) ;
213
+ HorizontalMaxScroll = - realm . Left - ( vp . ActualWidth / 2 ) ;
214
+
215
+ VerticalMinScroll = realm . Top + ( vp . ActualHeight / 2 ) ;
216
+ VerticalMaxScroll = realm . Bottom - ( vp . ActualHeight / 2 ) ;
217
+ }
218
+ else
219
+ {
220
+ HorizontalMaxScroll = HorizontalMinScroll = 0 ;
221
+ VerticalMaxScroll = VerticalMinScroll = 0 ;
222
+ }
223
+ }
224
+ else
225
+ {
226
+ var scrollableWidth = Math . Max ( 0 , ScrollExtentWidth - vp . ActualWidth ) ;
227
+ var scrollableHeight = Math . Max ( 0 , ScrollExtentHeight - vp . ActualHeight ) ;
228
+
229
+ // since the content is always centered, we need to able to scroll both way equally:
230
+ // [Content-Content-Content]
231
+ // [=======[Viewport]======]
232
+ HorizontalMaxScroll = scrollableWidth / 2 ;
233
+ HorizontalMinScroll = - HorizontalMaxScroll ;
234
+ VerticalMaxScroll = scrollableHeight / 2 ;
235
+ VerticalMinScroll = - VerticalMaxScroll ;
236
+ }
237
+
238
+ Update ( _scrollH , HorizontalMinScroll < HorizontalMaxScroll , vp . ActualWidth ) ;
239
+ Update ( _scrollV , VerticalMinScroll < VerticalMaxScroll , vp . ActualHeight ) ;
240
+ void Update ( ScrollBar ? sb , bool shown , double thumbSize )
241
+ {
242
+ if ( sb is null ) return ;
243
+
244
+ // update size of thumb
245
+ sb . ViewportSize = thumbSize ;
246
+
247
+ // Showing/hiding the ScrollBar(s)could cause the ContentPresenter to move as it re-centers.
248
+ // This adds unnecessary complexity for the zooming logics as we need to preserve the focal point
249
+ // under the cursor position or the pinch center point after zooming.
250
+ // To avoid all that, we just make them permanently there for layout calculation.
251
+ sb . IsEnabled = shown ;
252
+ sb . Opacity = shown ? 1 : 0 ;
253
+ }
224
254
}
225
255
}
226
256
0 commit comments