프로젝트

일반

사용자정보

통계
| 브랜치(Branch): | 개정판:

markus / ZoomAndPan / ZoomAndPanControl.cs.bak @ e1c892f7

이력 | 보기 | 이력해설 | 다운로드 (36 KB)

1 787a4489 KangIngu
using System;
2
using System.Collections.Generic;
3
using System.Linq;
4
using System.Text;
5
using System.Threading.Tasks;
6
using System.Windows;
7
using System.Windows.Controls;
8
using System.Windows.Media;
9
10
/// 출처 : https://www.codeproject.com/Articles/85603/A-WPF-custom-control-for-zooming-and-panning#ZoomAndPanControlMethods
11
namespace ZoomAndPan
12
{
13
    public partial class ZoomAndPanControl : ContentControl
14
    {
15
        /// <summary>
16
        /// Reference to the underlying content, which is named PART_Content in the template.
17
        /// </summary>
18
        private FrameworkElement content;
19
20
        /// <summary>
21
        /// The transform that is applied to the content to scale it by 'ContentScale'
22
        /// </summary>
23
        private ScaleTransform contentScaleTransform = null;
24
25
        /// <summary>
26
        /// The transform that is applied to the content to offset it by 'ContentOffsetX' and 'ContentOffsetY'
27
        /// </summary>
28
        private TranslateTransform contentOffsetTransform = null;
29
30
        /// <summary>
31
        /// Enable the udate of the content offset as the content scale changes.
32
        /// This enabled for zooming about a point (google-maps stlye zooming) and zooming to a rect
33
        /// </summary>
34
        private bool enableContentOffsetUpdateFromScale = false;
35
36
        private bool disableScrollOffsetSync = false; 
37
38
        //public double UnScaledExtentWidth
39
        //{
40
        //    get
41
        //    {
42
        //        return (double)GetValue(UnScaledExtentWidthProperty);
43
        //    }
44
        //    set
45
        //    {
46
        //        SetValue(UnScaledExtentWidthProperty, value);
47
        //    }
48
        //}
49
50
        //public double UnScaledExtentHeight
51
        //{
52
        //    get
53
        //    {
54
        //        return (double)GetValue(UnScaledExtentHeightProperty);
55
        //    }
56
        //    set
57
        //    {
58
        //        SetValue(UnScaledExtentHeightProperty, value);
59
        //    }
60
        //}
61
62
        ///// <summary>
63
        ///// The width of the viewport in content coordinate, clamped to the width of the content.
64
        ///// </summary>
65
        public double ConstrainedContentViewportWidth
66
        {
67
            get
68
            {
69
                return (double)GetValue(ConstrainedContentViewportWidthProperty);
70
            }
71
            set
72
            {
73
                SetValue(ConstrainedContentViewportWidthProperty, value);
74
            }
75
        }
76
77
        ///// <summary>
78
        ///// The height of the viewport in content coordinate, clamped to the height of the content.
79
        ///// </summary>
80
        public double ConstrainedContentViewportHeight
81
        {
82
            get
83
            {
84
                return (double)GetValue(ConstrainedContentViewportHeightProperty);
85
            }
86
            set
87
            {
88
                SetValue(ConstrainedContentViewportHeightProperty, value);
89
            }
90
        }
91
92
        #region IScrollInfo
93
        /// <summary>
94
        /// Set to 'true' when the vertical scrollbar is enabled.
95
        /// </summary>
96
        private bool canVerticallyScroll = false;
97
98
        /// <summary>
99
        /// Set to 'true' when the vertical scrollbar is enabled.
100
        /// </summary>
101
        private bool canHorizontallyScroll = false;
102
103
        /// <summary>
104
        /// Reference to the ScrollViewer that is wrapped (in XAML) around the ZoomAndPanControl.
105
        /// Or set to null if there is no ScrollViewer.
106
        /// </summary>
107
        private ScrollViewer scrollOwner;
108
109
        /// <summary>
110
        /// Records the unscaled extent of the content.
111
        /// This is calculated during the measure and arrange.
112
        /// </summary>
113
        private Size unScaledExtent = new Size();
114
115
        /// <summary>
116
        /// Records the size of the viewport (in viewport coordinates) onto the content.
117
        /// This is calculated during the measure and arrange.
118
        /// </summary>
119
        private Size viewport = new Size(0, 0);
120
121
        #endregion
122
123
        #region Properties
124
        /// <summary>
125
        /// Get/set the current scale (or zoom factor) of the content.
126
        /// </summary>
127
        public double ContentScale
128
        {
129
            get
130
            {
131
                return (double)GetValue(ContentScaleProperty);
132
            }
133
            set
134
            {
135
                SetValue(ContentScaleProperty, value);
136
            }
137
        }
138
139
        /// <summary>
140
        /// Get/set the minimum value for 'ContentScale'
141
        /// </summary>
142
        public double MinContentScale
143
        {
144
            get
145
            {
146
                return (double)GetValue(MinContentScaleProperty);
147
            }
148
            set
149
            {
150
                SetValue(MinContentScaleProperty, value);
151
            }
152
        }
153
154
        /// <summary>
155
        /// Get/set the maximum value  for 'ContentScale'
156
        /// </summary>
157
        public double MaxContentScale
158
        {
159
            get
160
            {
161
                return (double)GetValue(MaxContentScaleProperty);
162
            }
163
            set
164
            {
165
                SetValue(MaxContentScaleProperty, value);
166
            }
167
        }
168
169
        /// <summary>
170
        /// Get the viewport width, in content coordinates.
171
        /// </summary>
172
        public double ContentViewportWidth
173
        {
174
            get 
175
            { 
176
                return (double)GetValue(ContentViewportWidthProperty); 
177
            }
178
            set
179
            { 
180
                SetValue(ContentViewportWidthProperty, value); 
181
            }
182
        }
183
184
        /// <summary>
185
        /// Get the viewport height, in content coordinates
186
        /// </summary>
187
        public double ContentViewportHeight
188
        {
189
            get
190
            { 
191
                return (double)GetValue(ContentViewportHeightProperty); 
192
            }
193
            set 
194
            { 
195
                SetValue(ContentViewportHeightProperty, value); 
196
            }
197
        }
198
199
        /// <summary>
200
        /// Get/set the X offset (in content coordinates) of the view on the content.
201
        /// </summary>
202
        public double ContentOffsetX
203
        {
204
            get
205
            {
206
                return (double)GetValue(ContentOffsetXProperty);
207
            }
208
            set
209
            {
210
                SetValue(ContentOffsetXProperty, value);
211
            }
212
        }
213
214
        /// <summary>
215
        /// Get/Set the offset (in content coordinates) of the view on the content
216
        /// </summary>
217
        public double ContentOffsetY
218
        {
219
            get
220
            {
221
                return (double)GetValue(ContentOffsetYProperty);
222
            }
223
            set
224
            {
225
                SetValue(ContentOffsetYProperty, value);
226
            }
227
        }
228
229
        /// <summary>
230
        /// ContentOffsetTransformX is TranslateTranform value
231
        /// </summary>
232
        public double ContentOffsetTransformX 
233
        {
234
            get
235
            {
236
                return (double)GetValue(ContentOffsetTransformXProperty);
237
            }
238
            set
239
            {
240
                SetValue(ContentOffsetTransformXProperty, value);
241
            }
242
        }
243
244
        /// <summary>
245
        /// ContentOffsetTransformY is TranslateTranform value
246
        /// </summary>
247
        public double ContentOffsetTransformY
248
        {
249
            get
250
            {
251
                return (double)GetValue(ContentOffsetTransformYProperty);
252
            }
253
            set
254
            {
255
                SetValue(ContentOffsetTransformYProperty, value);
256
            }
257
        }
258
259
        /// <summary>
260
        /// unScaledExtentWidth * ContentScale
261
        /// </summary>
262
        public double ScaledContentWidth
263
        {
264
            get
265
            {
266
                return (double)GetValue(ScaledContentWidthProperty);
267
            }
268
            set
269
            {
270
                SetValue(ScaledContentWidthProperty, value);
271
            }
272
        }
273
274
        /// <summary>
275
        /// unScaledExtentHeight * ContentScale
276
        /// </summary>
277
        public double ScaledContentHeight
278
        {
279
            get
280
            {
281
                return (double)GetValue(ScaledContentHeightProperty);
282
            }
283
            set
284
            {
285
                SetValue(ScaledContentHeightProperty, value);
286
            }
287
        }
288
289
        /// <summary>
290
        /// The X coordinate of the content focus, this is the point that we are focusing on when zooming.
291
        /// </summary>
292
        public double ContentZoomFocusX 
293
        { 
294
            get 
295
            {
296
                return (double)GetValue(ContentZoomFocusXProperty);
297
            }
298
            set
299
            {
300
                SetValue(ContentZoomFocusXProperty, value) ;
301
            }
302
        }
303
304
        /// <summary>
305
        /// The Y coordinate of the content focus, this is the point that we are focusing on when zooming.
306
        /// </summary>
307
        public double ContentZoomFocusY
308
        {
309
            get
310
            {
311
                return (double)GetValue(ContentZoomFocusYProperty);
312
            }
313
            set
314
            {
315
                SetValue(ContentZoomFocusYProperty,value);
316
            }
317
        }
318
319
        public double RotationAngle
320
        {
321
            get { return (double)GetValue(RotationAngleProperty); }
322
323
            set { SetValue(RotationAngleProperty, value); }
324
        }
325
326
        public bool IsMouseWheelScrollingEnabled
327
        {
328
            get
329
            {
330
                return (bool)GetValue(IsMouseWheelScrollingEnabledProperty);
331
            }
332
            set
333
            {
334
                SetValue(IsMouseWheelScrollingEnabledProperty, value);
335
            }
336
337
        }
338
339
        public double AnimationDuration
340
        {
341
            get 
342
            {
343
                return (double)GetValue(AnimationDurationProperty); 
344
            }
345
            set 
346
            { 
347
                SetValue(AnimationDurationProperty, value);
348
            }
349
        }
350
351
        /// <summary>
352
        /// The X coordinate of the viewport focus, this is the point in the viewport (in viewport coordinates) 
353
        /// that the content focus point is locked to while zooming in.
354
        /// </summary>
355
        public double ViewportZoomFocusX
356
        {
357
            get
358
            {
359
                return (double)GetValue(ViewportZoomFocusXProperty);
360
            }
361
            set
362
            {
363
                SetValue(ViewportZoomFocusXProperty, value);
364
            }
365
        }
366
367
        /// <summary>
368
        /// The Y coordinate of the viewport focus, this is the point in the viewport (in viewport coordinates) 
369
        /// that the content focus point is locked to while zooming in.
370
        /// </summary>
371
        public double ViewportZoomFocusY
372
        {
373
            get
374
            {
375
                return (double)GetValue(ViewportZoomFocusYProperty);
376
            }
377
            set
378
            {
379
                SetValue(ViewportZoomFocusYProperty, value);
380
            }
381
        }
382
383
        public ImageBrush BackgroundImage
384
        {
385
            get { return (ImageBrush)GetValue(BackgroundImageProperty); }
386
            set { SetValue(BackgroundImageProperty, value); }
387
        }
388
389
        #endregion Properties
390
391
        #region Internal Method
392
        static ZoomAndPanControl()
393
        {
394
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ZoomAndPanControl),
395
            new FrameworkPropertyMetadata(typeof(ZoomAndPanControl)));
396
        }
397
398
        public override void OnApplyTemplate()
399
        {
400
            base.OnApplyTemplate();
401
402
            content = this.Template.FindName("PART_Content", this) as FrameworkElement;
403
404
            if (content != null)
405
            {
406
               
407
                this.contentScaleTransform = new ScaleTransform(ContentScale, ContentScale);
408
                this.contentOffsetTransform = new TranslateTransform();
409
                TransformGroup transformGroup = new TransformGroup();
410
                transformGroup.Children.Add(this.contentOffsetTransform);
411
                transformGroup.Children.Add(this.contentScaleTransform);
412
                content.RenderTransform = transformGroup;
413
            }
414
        }
415
416
        protected override Size MeasureOverride(Size constraint)
417
        {
418
            //System.Diagnostics.Debug.WriteLine(Name);
419
            Size infiniteSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
420
            Size childSize = base.MeasureOverride(infiniteSize);
421
422
            if (childSize != unScaledExtent )
423
            {
424
                unScaledExtent = childSize;
425
            }
426
427
            //unScaledExtent가 결정된 이후 UpdateViewportSize가 일어나야한다.
428
            UpdateViewportSize(constraint);
429
430
            double width = constraint.Width;
431
            double height = constraint.Height;
432
433
            if (double.IsPositiveInfinity(width))
434
                width = childSize.Width;
435
436
            if (double.IsPositiveInfinity(height))
437
                height = childSize.Height;
438
439
            if (scrollOwner != null)
440
                scrollOwner.InvalidateScrollInfo();
441
442
443
            return new Size(width, height);
444
        }
445
446
        protected override Size ArrangeOverride(Size arrangeBounds)
447
        {
448
            Size size = base.ArrangeOverride(arrangeBounds);
449
450
            //unScaledExtent가 결정된 이후 UpdateViewportSize가 일어나야한다.
451
            if (content.DesiredSize != unScaledExtent)
452
            {
453
                unScaledExtent = content.DesiredSize;
454
455
                if (scrollOwner != null)
456
                    scrollOwner.InvalidateScrollInfo();
457
            }
458
                
459
460
            UpdateViewportSize(arrangeBounds);
461
462
            return size;
463
        } 
464
        #endregion
465
466
        #region Dependency Properties
467
468
        #region Max & Min ContentScale
469
470
        public static readonly DependencyProperty ContentScaleProperty =
471
                DependencyProperty.Register("ContentScale", typeof(double), typeof(ZoomAndPanControl),
472
                                            new FrameworkPropertyMetadata(1.0, ContentScale_PropertyChanged, ContentScale_Coerce));
473
474
        public static readonly DependencyProperty MinContentScaleProperty =
475
          DependencyProperty.Register("MinContentScale", typeof(double), typeof(ZoomAndPanControl),
476
                                      new FrameworkPropertyMetadata(0.01, MinOrMaxContentScale_PropertyChanged));
477
478
        public static readonly DependencyProperty MaxContentScaleProperty =
479
                DependencyProperty.Register("MaxContentScale", typeof(double), typeof(ZoomAndPanControl),
480
                                            new FrameworkPropertyMetadata(10.0, MinOrMaxContentScale_PropertyChanged));
481
482
        #endregion Max & Min ContentScale
483
484
        #region ContentOffset X & Y
485
        public static readonly DependencyProperty ContentOffsetXProperty =
486
              DependencyProperty.Register("ContentOffsetX", typeof(double), typeof(ZoomAndPanControl),
487
                                          new FrameworkPropertyMetadata(0.0, ContentOffsetX_PropertyChanged, ContentOffsetX_Coerce));
488
489
        public static readonly DependencyProperty ContentOffsetYProperty =
490
                DependencyProperty.Register("ContentOffsetY", typeof(double), typeof(ZoomAndPanControl),
491
                                        new FrameworkPropertyMetadata(0.0, ContentOffsetY_PropertyChanged, ContentOffsetY_Coerce));
492
        #endregion ContentOffset X & Y
493
494
        #region ContentViewportProperty
495
        public static readonly DependencyProperty ContentViewportHeightProperty =
496
                DependencyProperty.Register("ContentViewportHeight", typeof(double), typeof(ZoomAndPanControl),
497
                                        new FrameworkPropertyMetadata(0.0));
498
499
        public static readonly DependencyProperty ContentViewportWidthProperty =
500
                DependencyProperty.Register("ContentViewportWidth", typeof(double), typeof(ZoomAndPanControl),
501
                                        new FrameworkPropertyMetadata(0.0));
502
        #endregion ContentViewportProperty
503
504
        #region TranslateTransform
505
        public static readonly DependencyProperty ContentOffsetTransformXProperty =
506
              DependencyProperty.Register("ContentOffsetTransformX", typeof(double), typeof(ZoomAndPanControl), new FrameworkPropertyMetadata(0.0, ContentOffsetTransformXPropertyChanged));
507
508
        public static readonly DependencyProperty ContentOffsetTransformYProperty =
509
                DependencyProperty.Register("ContentOffsetTransformY", typeof(double), typeof(ZoomAndPanControl), new FrameworkPropertyMetadata(0.0, ContentOffsetTransformYPropertyChanged));
510
        #endregion
511
512
        #region ContentZoomFocus X & Y
513
        public static readonly DependencyProperty ContentZoomFocusXProperty =
514
              DependencyProperty.Register("ContentZoomFocusX", typeof(double), typeof(ZoomAndPanControl),
515
                                          new FrameworkPropertyMetadata(0.0));
516
517
        public static readonly DependencyProperty ContentZoomFocusYProperty =
518
                DependencyProperty.Register("ContentZoomFocusY", typeof(double), typeof(ZoomAndPanControl),
519
                                            new FrameworkPropertyMetadata(0.0));
520
        #endregion
521
522
        #region ScaleContentProperty
523
524
        public static readonly DependencyProperty ScaledContentWidthProperty =
525
        DependencyProperty.Register("ScaledContentWidth", typeof(double),
526
        typeof(ZoomAndPanControl), new FrameworkPropertyMetadata(0.0));
527
528
        public static readonly DependencyProperty ScaledContentHeightProperty =
529
        DependencyProperty.Register("ScaledContentHeight", typeof(double),
530
        typeof(ZoomAndPanControl), new FrameworkPropertyMetadata(0.0));
531
        #endregion ScaleContentProperty
532
533
        #region ConstrainedContentViewport Width, Height
534
        public static readonly DependencyProperty ConstrainedContentViewportHeightProperty =
535
      DependencyProperty.Register("ConstrainedContentViewportHeight", typeof(double),
536
      typeof(ZoomAndPanControl), new FrameworkPropertyMetadata(0.0));
537
538
        public static readonly DependencyProperty ConstrainedContentViewportWidthProperty =
539
        DependencyProperty.Register("ConstrainedContentViewportWidth", typeof(double),
540
        typeof(ZoomAndPanControl), new FrameworkPropertyMetadata(0.0)); 
541
        #endregion
542
543
        #region Unscaled Width, Height
544
    //    public static readonly DependencyProperty UnScaledExtentWidthProperty =
545
    //DependencyProperty.Register("UnScaledExtentWidth", typeof(double),
546
    //typeof(ZoomAndPanControl), new FrameworkPropertyMetadata(0.0));
547
548
    //    public static readonly DependencyProperty UnScaledExtentHeightProperty =
549
    //    DependencyProperty.Register("UnScaledExtentHeight", typeof(double),
550
    //    typeof(ZoomAndPanControl), new FrameworkPropertyMetadata(0.0));  
551
        #endregion
552
553
554
        #region RotationAngle
555
        public static readonly DependencyProperty
556
                    RotationAngleProperty =
557
                    DependencyProperty.Register("RotationAngle",
558
                    typeof(double), typeof(ZoomAndPanControl), new FrameworkPropertyMetadata(0.0));
559
        #endregion
560
561
        #region IsMouseWheelScrolling
562
        public static readonly DependencyProperty IsMouseWheelScrollingEnabledProperty =
563
            DependencyProperty.Register("IsMouseWheelScrollingEnabled", typeof(bool), typeof(ZoomAndPanControl), new FrameworkPropertyMetadata(false));
564
        #endregion IsMouseWheelScrolling
565
566
        #region AnimationDuration
567
        public static readonly DependencyProperty AnimationDurationProperty =
568
            DependencyProperty.Register("AnimationDuration", typeof(double), typeof(ZoomAndPanControl),
569
            new FrameworkPropertyMetadata(0.4));
570
        
571
        #endregion
572
573
        #region ViewportZoomFocus
574
        public static readonly DependencyProperty ViewportZoomFocusXProperty =
575
            DependencyProperty.Register("ViewportZoomFocusX", typeof(double), typeof(ZoomAndPanControl),
576
            new FrameworkPropertyMetadata(0.0));
577
578
        public static readonly DependencyProperty ViewportZoomFocusYProperty =
579
            DependencyProperty.Register("ViewportZoomFocusY", typeof(double), typeof(ZoomAndPanControl),
580
            new FrameworkPropertyMetadata(0.0));
581
582
        #endregion
583
584
    
585
        // Using a DependencyProperty as the backing store for BackGroundImage.  This enables animation, styling, binding, etc...
586
        public static readonly DependencyProperty BackgroundImageProperty =
587
            DependencyProperty.Register("BackgroundImage", typeof(ImageBrush), typeof(ZoomAndPanControl), null);
588
589
        
590
        #endregion Dependency Properties
591
592
        #region Dependency PropertyChangedEvent
593
594
        #region TranslateTransform
595
        private static void ContentOffsetTransformXPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
596
        {
597
            var zoomAndPanControl = (ZoomAndPanControl)d;
598
599
            if (zoomAndPanControl != null)
600
                zoomAndPanControl.SetContentOffsetTransformX();
601
        }
602
603
604
        private static void ContentOffsetTransformYPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
605
        {
606
            var zoomAndPanControl = (ZoomAndPanControl)d;
607
608
            if (zoomAndPanControl != null)
609
                zoomAndPanControl.SetContentOffsetTransformY();
610
        } 
611
        #endregion
612
613
        #region ScaleContentProperty
614
        /// <summary>
615
        /// Method called to clamp the 'ContentScale' value to its valid range.
616
        /// </summary>
617
        private static object ContentScale_Coerce(DependencyObject d, object baseValue)
618
        {
619
            ZoomAndPanControl c = (ZoomAndPanControl)d;
620
            double value = (double)baseValue;
621
            value = Math.Min(Math.Max(value, c.MinContentScale), c.MaxContentScale);
622
            return value;
623
        }
624
625
        /// <summary>
626
        /// Event raised when the 'ContentScale' property has changed value.
627
        /// </summary>
628
        private static void ContentScale_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
629
        {
630
            ZoomAndPanControl c = (ZoomAndPanControl)o;
631
632
            if (c.contentScaleTransform != null)
633
            {
634
                c.contentScaleTransform.ScaleX = c.ContentScale;
635
                c.contentScaleTransform.ScaleY = c.ContentScale;
636
            }
637
638
            c.UpdateContentViewportSize();
639
640
            c.UpdateContentZoomFocusX();
641
            c.UpdateContentZoomFocusY();
642
            
643
            
644
            if(c.enableContentOffsetUpdateFromScale)
645
            {
646
                try
647
                {
648
                    double viewportOffsetX = c.ViewportZoomFocusX - (c.ViewportWidth / 2);
649
                    double viewportOffsetY = c.ViewportZoomFocusY - (c.ViewportHeight / 2);
650
                    double contentOffsetX = viewportOffsetX / c.ContentScale;
651
                    double contentOffsetY = viewportOffsetY / c.ContentScale;
652
                    c.ContentOffsetX = (c.ContentZoomFocusX - (c.ContentViewportWidth / 2)) - contentOffsetX;
653
                    c.ContentOffsetY = (c.ContentZoomFocusY - (c.ContentViewportHeight / 2)) - contentOffsetY;
654
                }
655
                finally
656
                {
657
                    c.enableContentOffsetUpdateFromScale = false;
658
                }
659
            }
660
661
            if (c.scrollOwner != null)
662
            {
663
                c.scrollOwner.InvalidateScrollInfo();
664
            }
665
666
        } 
667
        #endregion
668
669
        #region ContentOffset X & Y
670
671
        /// <summary>
672
        /// Method called to clamp the 'ContentOffsetX' value to its valid range.
673
        /// </summary>
674
        private static object ContentOffsetX_Coerce(DependencyObject d, object baseValue)
675
        {
676
            ZoomAndPanControl c = (ZoomAndPanControl)d;
677
            double value = (double)baseValue;
678
            double minOffsetX = 0.0;
679
            double maxOffsetX = Math.Max(0.0, c.unScaledExtent.Width - c.ConstrainedContentViewportWidth);
680
            value = Math.Min(Math.Max(value, minOffsetX), maxOffsetX);
681
            return value;
682
        }
683
684
        /// <summary>
685
        /// Event raised when the 'ContentOffsetX' property has changed value.
686
        /// </summary>
687
        private static void ContentOffsetX_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
688
        {
689
            ZoomAndPanControl c = (ZoomAndPanControl)o;
690
691
            c.UpdateTranslationX();
692
693
            if (c.scrollOwner != null)
694
            {
695
                c.scrollOwner.InvalidateScrollInfo();
696
            }
697
        }
698
699
        /// <summary>
700
        /// Method called to clamp the 'ContentOffsetY' value to its valid range.
701
        /// </summary>
702
        private static object ContentOffsetY_Coerce(DependencyObject d, object baseValue)
703
        {
704
            ZoomAndPanControl c = (ZoomAndPanControl)d;
705
            double value = (double)baseValue;
706
            double minOffsetY = 0.0;
707
            double maxOffsetY = Math.Max(0.0, c.unScaledExtent.Height - c.ConstrainedContentViewportHeight);
708
            value = Math.Min(Math.Max(value, minOffsetY), maxOffsetY);
709
            return value;
710
        }
711
712
        /// <summary>
713
        /// Event raised when the 'ContentOffsetY' property has changed value.
714
        /// </summary>
715
        private static void ContentOffsetY_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
716
        {
717
718
            ZoomAndPanControl c = (ZoomAndPanControl)o;
719
            c.UpdateTranslationY();
720
721
            if (c.scrollOwner != null)
722
            {
723
                c.scrollOwner.InvalidateScrollInfo();
724
            }
725
        } 
726
727
728
        public void AnimatedZoomTo(Rect contentRect)
729
        {
730
            var ScaleX = this.ContentViewportWidth / contentRect.Width;
731
            var ScaleY = this.ContentViewportHeight / contentRect.Height;
732
            var NewContentScale = this.ContentScale * Math.Min(ScaleX, ScaleY);
733
            ContentZoomFocusX = contentRect.X + (contentRect.Width / 2);
734
            ContentZoomFocusY = contentRect.Y + (contentRect.Height/ 2);
735
            AnimatedZoomPointToViewportCenter(NewContentScale, new Point(ContentZoomFocusX, ContentZoomFocusY), null);
736
        }
737
738
        /// <summary>
739
        /// Zoom to the specified scale and move the specified focus point to the center of the viewport.
740
        /// </summary>
741
        private void AnimatedZoomPointToViewportCenter(double newContentScale, Point contentZoomFocus, EventHandler callback)
742
        {
743
            newContentScale = Math.Min(Math.Max(newContentScale, MinContentScale), MaxContentScale);
744
            
745
            ContentZoomFocusX = contentZoomFocus.X;
746
            ContentZoomFocusY = contentZoomFocus.Y;
747
748
            enableContentOffsetUpdateFromScale = true;
749
750
            AnimationHelper.StartAnimation(this, ContentScaleProperty, newContentScale, AnimationDuration,
751
                delegate(object sender, EventArgs e ){
752
                    enableContentOffsetUpdateFromScale = false; 
753
                    if(callback  != null)
754
                    {
755
                        callback(this, EventArgs.Empty);
756
                    }
757
                });
758
        }
759
760
        /// <summary>
761
        /// Do animation that scales the content so that it fits completely in the control.
762
        /// </summary>
763
        public void AnimatedScaleToFit()
764
        {
765
            if (content == null)
766
            {
767
                throw new ApplicationException("PART_Content was not found in the ZoomAndPanControl visual template!");
768
            }
769
770
            AnimatedZoomTo(new Rect(0, 0, content.ActualWidth, content.ActualHeight));
771
        }
772
773
        #endregion Dependency PropertyChangedEvent
774
775
776
        #region Max & Min ContentScale
777
        private static void MinOrMaxContentScale_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
778
        {
779
            ZoomAndPanControl c = (ZoomAndPanControl)o;
780
            c.ContentScale = Math.Min(Math.Max(c.ContentScale, c.MinContentScale), c.MaxContentScale);
781
        }  
782
        #endregion
783
784
        #endregion
785
786
        /// <summary>
787
        /// Set TranslateTransform X Coordinate
788
        /// </summary>
789
        public void SetContentOffsetTransformX()
790
        {
791
            this.contentOffsetTransform.X = ContentOffsetTransformX;
792
        }
793
794
        /// <summary>
795
        /// Set TranslateTransform Y Coordinate
796
        /// </summary>
797
        public void SetContentOffsetTransformY()
798
        {
799
            this.contentOffsetTransform.Y = ContentOffsetTransformY;
800
        }
801
802
        /// <summary>
803
        /// Update the Y coordinate of the translation transformation.
804
        /// </summary>
805
        private void UpdateTranslationY()
806
        {
807
            if(this.contentOffsetTransform != null)
808
            {
809
                ///Canvas의 Height 값 * ContentScaled 수치를 곱하여 scaledContentHeight를 구한다.
810
                ScaledContentHeight = this.unScaledExtent.Height * this.ContentScale;
811
812
                ////Viewport보다 scaledContentHeight 값이 작다면
813
                if (ScaledContentHeight < this.viewport.Height)
814
                {
815
                    this.ContentOffsetTransformY = (ContentViewportHeight - this.unScaledExtent.Height) / 2;
816
                }
817
                else
818
                {
819
                    this.ContentOffsetTransformY = -this.ContentOffsetY;
820
                }
821
822
                this.ContentOffsetY = this.ContentOffsetY;
823
            }
824
        }
825
826
        /// <summary>
827
        /// Update the X coordinate of the translation transformation.
828
        /// </summary>
829
        private void UpdateTranslationX()
830
        {
831
            if (this.contentOffsetTransform != null)
832
            {
833
                ScaledContentWidth = this.unScaledExtent.Width * this.ContentScale;
834
835
                if (ScaledContentWidth < this.viewport.Width)
836
                {
837
                    this.ContentOffsetTransformX = (ContentViewportWidth - this.unScaledExtent.Width) / 2;
838
                }
839
                else
840
                {
841
                    this.ContentOffsetTransformX = -this.ContentOffsetX;
842
                }
843
844
                this.ContentOffsetX = this.ContentOffsetX;
845
            }
846
        }
847
848
        /// <summary>
849
        /// Update the size of the viewport in content coordinates after the viewport size or 'ContentScale' has changed
850
        /// </summary>
851
        private void UpdateContentViewportSize()
852
        {
853
            ContentViewportWidth = viewport.Width / ContentScale;
854
            ContentViewportHeight = viewport.Height / ContentScale;
855
856
            ConstrainedContentViewportWidth = Math.Min(ContentViewportWidth, unScaledExtent.Width);
857
            ConstrainedContentViewportHeight = Math.Min(ContentViewportHeight, unScaledExtent.Height);
858
859
            UpdateTranslationX();
860
            UpdateTranslationY();
861
        }
862
863
        /// <summary>
864
        /// Update the viewport size from the specified size. (If viewport Changed Scale or Size)
865
        /// </summary>
866
        private void UpdateViewportSize(Size newSize)
867
        {
868
            //if (viewport == newSize)
869
            //{
870
            //    UpdateContentViewportSize();
871
            //    return;
872
            //}
873
874
            viewport = newSize;
875
876
            UpdateContentViewportSize();
877
878
            UpdateContentZoomFocusX();
879
            UpdateContentZoomFocusY();
880
881
            //
882
            // Update content offset from itself when the size of the viewport changes.
883
            // This ensures that the content offset remains properly clamped to its valid range.
884
            //
885
            this.ContentOffsetX = this.ContentOffsetX;
886
            this.ContentOffsetY = this.ContentOffsetY;
887
888
            if (scrollOwner != null)
889
            {
890
                scrollOwner.InvalidateScrollInfo();
891
            }
892
        }
893
894
        /// <summary>
895
        /// Zoom in/out centered on the specified point (in content coordinates).
896
        /// The focus point is kept locked to it's on sceen position (ala google maps).
897
        /// </summary>
898
        /// <param name="newContentScale">New Scale</param>
899
        /// <param name="contentZoomFocus">Zoom Focuse Point</param>
900
        public void ZoomAboutPoint(double newContentScale, Point contentZoomFocus)
901
        {
902
            newContentScale = Math.Min(Math.Max(newContentScale, MinContentScale), MaxContentScale);
903
904
            if (contentOffsetTransform !=null)
905
            {
906
                var diffPointAndOffsetX = (contentZoomFocus.X + contentOffsetTransform.X);
907
                var diffPointAndOffsetY = (contentZoomFocus.Y + contentOffsetTransform.Y);
908
909
                ///Scale로 압축
910
                ///ScreenSpace 좌표 내에서 Offset 좌표를 뺀 후 현재 Scale 로 압축.
911
                double screenSpaceZoomOffsetX = diffPointAndOffsetX * ContentScale;
912
                double screenSpaceZoomOffsetY = diffPointAndOffsetY * ContentScale;
913
914
                ///Scale로 늘림
915
                ///Scale로 압축된 좌표를 새로운 스케일로 늘린 contentSpaceZoom 좌표 
916
                double contentSpaceZoomOffsetX = screenSpaceZoomOffsetX / newContentScale;
917
                double contentSpaceZoomOffsetY = screenSpaceZoomOffsetY / newContentScale;
918
919
                ///내가 원하는 좌표 포인트에 contentSpaceZoom 좌표를 뺀 다. 
920
                double newContentOffsetX = contentZoomFocus.X - contentSpaceZoomOffsetX;
921
                double newContentOffsetY = contentZoomFocus.Y - contentSpaceZoomOffsetY;
922
923
                this.ContentScale = newContentScale;
924
                this.ContentOffsetX = newContentOffsetX;
925
                this.ContentOffsetY = newContentOffsetY;
926
            }
927
          
928
        }
929
930
        /// <summary>
931
        /// ContentOffsetX 
932
        /// </summary>
933
        private void UpdateContentZoomFocusX()
934
        {
935
            ContentZoomFocusX = ContentOffsetX + (ConstrainedContentViewportWidth / 2);
936
        }
937
938
        private void UpdateContentZoomFocusY()
939
        {
940
            ContentZoomFocusY = ContentOffsetY + (ConstrainedContentViewportHeight / 2);
941
        }
942
943
        public void AnimatedZoomTo(double contentScale)
944
        {
945
            Point zoomCenter = new Point(ContentOffsetX + (ContentViewportWidth / 2),
946
                ContentOffsetY + (ContentViewportHeight / 2));
947
948
            AnimatedZoomAboutPoint(contentScale, zoomCenter);
949
        }
950
951
        /// <summary>
952
        /// Use animation to center the view on the specified point (in content coordinates).
953
        /// </summary>
954
        public void AnimatedSnapTo(Point contentPoint)
955
        {
956
            double newX = contentPoint.X - (this.ContentViewportWidth / 2);
957
            double newY = contentPoint.Y - (this.ContentViewportHeight / 2);
958
959
            AnimationHelper.StartAnimation(this, ContentOffsetXProperty, newX, AnimationDuration);
960
            AnimationHelper.StartAnimation(this, ContentOffsetYProperty, newY, AnimationDuration);
961
        }
962
963
        public void AnimatedZoomAboutPoint(double newContentScale, Point contentZoomFocus)
964
        {
965
            newContentScale = Math.Min(Math.Max(newContentScale, MinContentScale), MaxContentScale);
966
967
            ContentZoomFocusX = contentZoomFocus.X;
968
            ContentZoomFocusY = contentZoomFocus.Y;
969
970
            enableContentOffsetUpdateFromScale = true;
971
972
            AnimationHelper.StartAnimation(this, ContentScaleProperty, newContentScale, AnimationDuration,
973
                delegate(object sender, EventArgs e)
974
                {
975
                    enableContentOffsetUpdateFromScale = false;
976
                });
977
        }
978
979
        public void ScaleToFit()
980
       {
981
            if(content== null)
982
            {
983
                throw new ApplicationException("PART_Content was not found in the ZoomAndPanControl visual template!");
984
            }
985
986
            ZoomTo(new Rect(0,0, content.ActualWidth, content.ActualHeight));
987
        }
988
989
990
        public void ZoomTo(Rect contentRect)
991
        {
992
            double scaleX = this.ContentViewportWidth / contentRect.Width;
993
            double scaleY = this.ContentViewportHeight / contentRect.Height;
994
            double newScale = this.ContentScale * Math.Min(scaleX, scaleY);
995
996
            ZoomPointToViewportCenter(newScale, new Point(contentRect.X +
997
                (contentRect.Width / 2), contentRect.Y + (contentRect.Height / 2)));
998
        }
999
1000
        public void ZoomPointToViewportCenter(double newContentScale, Point contentZoomFocus)
1001
        {
1002
            newContentScale = Math.Min(Math.Max(newContentScale, MinContentScale), MaxContentScale);
1003
            this.ContentScale = newContentScale;
1004
            this.ContentOffsetX = contentZoomFocus.X - (ContentViewportWidth / 2);
1005
            this.ContentOffsetY = contentZoomFocus.Y - (ContentViewportHeight / 2);
1006
1007
        }
1008
    }
1009
}
클립보드 이미지 추가 (최대 크기: 500 MB)