프로젝트

일반

사용자정보

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

markus / ZoomAndPan / ZoomAndPanControl.cs @ a0bab669

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

1
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
            //Size childSize = new Size(1024,1754);
422

    
423
            if (childSize != unScaledExtent )
424
            {
425
                unScaledExtent = childSize;
426

    
427
                //unScaledExtent가 결정된 이후 UpdateViewportSize가 일어나야한다.
428
                UpdateViewportSize(constraint);
429

    
430
            }
431

    
432

    
433
            double width = constraint.Width;
434
            double height = constraint.Height;
435

    
436
            if (double.IsPositiveInfinity(width))
437
                width = childSize.Width;
438

    
439
            if (double.IsPositiveInfinity(height))
440
                height = childSize.Height;
441

    
442
            if (scrollOwner != null)
443
                scrollOwner.InvalidateScrollInfo();
444

    
445

    
446
            return new Size(width, height);
447
        }
448

    
449
        protected override Size ArrangeOverride(Size arrangeBounds)
450
        {
451
            Size size = base.ArrangeOverride(arrangeBounds);
452

    
453
            //unScaledExtent가 결정된 이후 UpdateViewportSize가 일어나야한다.
454
            if (content.DesiredSize != unScaledExtent)
455
            {
456
                unScaledExtent = content.DesiredSize;
457

    
458
                if (scrollOwner != null)
459
                    scrollOwner.InvalidateScrollInfo();
460
            }
461
                
462

    
463
            UpdateViewportSize(arrangeBounds);
464

    
465
            return size;
466
        } 
467
        #endregion
468

    
469
        #region Dependency Properties
470

    
471
        #region Max & Min ContentScale
472

    
473
        public static readonly DependencyProperty ContentScaleProperty =
474
                DependencyProperty.Register("ContentScale", typeof(double), typeof(ZoomAndPanControl),
475
                                            new FrameworkPropertyMetadata(1.0, ContentScale_PropertyChanged, ContentScale_Coerce));
476

    
477
        public static readonly DependencyProperty MinContentScaleProperty =
478
          DependencyProperty.Register("MinContentScale", typeof(double), typeof(ZoomAndPanControl),
479
                                      new FrameworkPropertyMetadata(0.01, MinOrMaxContentScale_PropertyChanged));
480

    
481
        public static readonly DependencyProperty MaxContentScaleProperty =
482
                DependencyProperty.Register("MaxContentScale", typeof(double), typeof(ZoomAndPanControl),
483
                                            new FrameworkPropertyMetadata(10.0, MinOrMaxContentScale_PropertyChanged));
484

    
485
        #endregion Max & Min ContentScale
486

    
487
        #region ContentOffset X & Y
488
        public static readonly DependencyProperty ContentOffsetXProperty =
489
              DependencyProperty.Register("ContentOffsetX", typeof(double), typeof(ZoomAndPanControl),
490
                                          new FrameworkPropertyMetadata(0.0, ContentOffsetX_PropertyChanged, ContentOffsetX_Coerce));
491

    
492
        public static readonly DependencyProperty ContentOffsetYProperty =
493
                DependencyProperty.Register("ContentOffsetY", typeof(double), typeof(ZoomAndPanControl),
494
                                        new FrameworkPropertyMetadata(0.0, ContentOffsetY_PropertyChanged, ContentOffsetY_Coerce));
495
        #endregion ContentOffset X & Y
496

    
497
        #region ContentViewportProperty
498
        public static readonly DependencyProperty ContentViewportHeightProperty =
499
                DependencyProperty.Register("ContentViewportHeight", typeof(double), typeof(ZoomAndPanControl),
500
                                        new FrameworkPropertyMetadata(0.0));
501

    
502
        public static readonly DependencyProperty ContentViewportWidthProperty =
503
                DependencyProperty.Register("ContentViewportWidth", typeof(double), typeof(ZoomAndPanControl),
504
                                        new FrameworkPropertyMetadata(0.0));
505
        #endregion ContentViewportProperty
506

    
507
        #region TranslateTransform
508
        public static readonly DependencyProperty ContentOffsetTransformXProperty =
509
              DependencyProperty.Register("ContentOffsetTransformX", typeof(double), typeof(ZoomAndPanControl), new FrameworkPropertyMetadata(0.0, ContentOffsetTransformXPropertyChanged));
510

    
511
        public static readonly DependencyProperty ContentOffsetTransformYProperty =
512
                DependencyProperty.Register("ContentOffsetTransformY", typeof(double), typeof(ZoomAndPanControl), new FrameworkPropertyMetadata(0.0, ContentOffsetTransformYPropertyChanged));
513
        #endregion
514

    
515
        #region ContentZoomFocus X & Y
516
        public static readonly DependencyProperty ContentZoomFocusXProperty =
517
              DependencyProperty.Register("ContentZoomFocusX", typeof(double), typeof(ZoomAndPanControl),
518
                                          new FrameworkPropertyMetadata(0.0));
519

    
520
        public static readonly DependencyProperty ContentZoomFocusYProperty =
521
                DependencyProperty.Register("ContentZoomFocusY", typeof(double), typeof(ZoomAndPanControl),
522
                                            new FrameworkPropertyMetadata(0.0));
523
        #endregion
524

    
525
        #region ScaleContentProperty
526

    
527
        public static readonly DependencyProperty ScaledContentWidthProperty =
528
        DependencyProperty.Register("ScaledContentWidth", typeof(double),
529
        typeof(ZoomAndPanControl), new FrameworkPropertyMetadata(0.0));
530

    
531
        public static readonly DependencyProperty ScaledContentHeightProperty =
532
        DependencyProperty.Register("ScaledContentHeight", typeof(double),
533
        typeof(ZoomAndPanControl), new FrameworkPropertyMetadata(0.0));
534
        #endregion ScaleContentProperty
535

    
536
        #region ConstrainedContentViewport Width, Height
537
        public static readonly DependencyProperty ConstrainedContentViewportHeightProperty =
538
      DependencyProperty.Register("ConstrainedContentViewportHeight", typeof(double),
539
      typeof(ZoomAndPanControl), new FrameworkPropertyMetadata(0.0));
540

    
541
        public static readonly DependencyProperty ConstrainedContentViewportWidthProperty =
542
        DependencyProperty.Register("ConstrainedContentViewportWidth", typeof(double),
543
        typeof(ZoomAndPanControl), new FrameworkPropertyMetadata(0.0)); 
544
        #endregion
545

    
546
        #region Unscaled Width, Height
547
    //    public static readonly DependencyProperty UnScaledExtentWidthProperty =
548
    //DependencyProperty.Register("UnScaledExtentWidth", typeof(double),
549
    //typeof(ZoomAndPanControl), new FrameworkPropertyMetadata(0.0));
550

    
551
    //    public static readonly DependencyProperty UnScaledExtentHeightProperty =
552
    //    DependencyProperty.Register("UnScaledExtentHeight", typeof(double),
553
    //    typeof(ZoomAndPanControl), new FrameworkPropertyMetadata(0.0));  
554
        #endregion
555

    
556

    
557
        #region RotationAngle
558
        public static readonly DependencyProperty
559
                    RotationAngleProperty =
560
                    DependencyProperty.Register("RotationAngle",
561
                    typeof(double), typeof(ZoomAndPanControl), new FrameworkPropertyMetadata(0.0));
562
        #endregion
563

    
564
        #region IsMouseWheelScrolling
565
        public static readonly DependencyProperty IsMouseWheelScrollingEnabledProperty =
566
            DependencyProperty.Register("IsMouseWheelScrollingEnabled", typeof(bool), typeof(ZoomAndPanControl), new FrameworkPropertyMetadata(false));
567
        #endregion IsMouseWheelScrolling
568

    
569
        #region AnimationDuration
570
        public static readonly DependencyProperty AnimationDurationProperty =
571
            DependencyProperty.Register("AnimationDuration", typeof(double), typeof(ZoomAndPanControl),
572
            new FrameworkPropertyMetadata(0.4));
573
        
574
        #endregion
575

    
576
        #region ViewportZoomFocus
577
        public static readonly DependencyProperty ViewportZoomFocusXProperty =
578
            DependencyProperty.Register("ViewportZoomFocusX", typeof(double), typeof(ZoomAndPanControl),
579
            new FrameworkPropertyMetadata(0.0));
580

    
581
        public static readonly DependencyProperty ViewportZoomFocusYProperty =
582
            DependencyProperty.Register("ViewportZoomFocusY", typeof(double), typeof(ZoomAndPanControl),
583
            new FrameworkPropertyMetadata(0.0));
584

    
585
        #endregion
586

    
587
    
588
        // Using a DependencyProperty as the backing store for BackGroundImage.  This enables animation, styling, binding, etc...
589
        public static readonly DependencyProperty BackgroundImageProperty =
590
            DependencyProperty.Register("BackgroundImage", typeof(ImageBrush), typeof(ZoomAndPanControl), null);
591

    
592
        
593
        #endregion Dependency Properties
594

    
595
        #region Dependency PropertyChangedEvent
596

    
597
        #region TranslateTransform
598
        private static void ContentOffsetTransformXPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
599
        {
600
            var zoomAndPanControl = (ZoomAndPanControl)d;
601

    
602
            if (zoomAndPanControl != null)
603
                zoomAndPanControl.SetContentOffsetTransformX();
604
        }
605

    
606

    
607
        private static void ContentOffsetTransformYPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
608
        {
609
            var zoomAndPanControl = (ZoomAndPanControl)d;
610

    
611
            if (zoomAndPanControl != null)
612
                zoomAndPanControl.SetContentOffsetTransformY();
613
        } 
614
        #endregion
615

    
616
        #region ScaleContentProperty
617
        /// <summary>
618
        /// Method called to clamp the 'ContentScale' value to its valid range.
619
        /// </summary>
620
        private static object ContentScale_Coerce(DependencyObject d, object baseValue)
621
        {
622
            ZoomAndPanControl c = (ZoomAndPanControl)d;
623
            double value = (double)baseValue;
624
            value = Math.Min(Math.Max(value, c.MinContentScale), c.MaxContentScale);
625
            return value;
626
        }
627

    
628
        /// <summary>
629
        /// Event raised when the 'ContentScale' property has changed value.
630
        /// </summary>
631
        private static void ContentScale_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
632
        {
633
            ZoomAndPanControl c = (ZoomAndPanControl)o;
634

    
635
            if (c.contentScaleTransform != null)
636
            {
637
                c.contentScaleTransform.ScaleX = c.ContentScale;
638
                c.contentScaleTransform.ScaleY = c.ContentScale;
639
            }
640

    
641
            c.UpdateContentViewportSize();
642

    
643
            c.UpdateContentZoomFocusX();
644
            c.UpdateContentZoomFocusY();
645
            
646
            
647
            if(c.enableContentOffsetUpdateFromScale)
648
            {
649
                try
650
                {
651
                    double viewportOffsetX = c.ViewportZoomFocusX - (c.ViewportWidth / 2);
652
                    double viewportOffsetY = c.ViewportZoomFocusY - (c.ViewportHeight / 2);
653
                    double contentOffsetX = viewportOffsetX / c.ContentScale;
654
                    double contentOffsetY = viewportOffsetY / c.ContentScale;
655
                    c.ContentOffsetX = (c.ContentZoomFocusX - (c.ContentViewportWidth / 2)) - contentOffsetX;
656
                    c.ContentOffsetY = (c.ContentZoomFocusY - (c.ContentViewportHeight / 2)) - contentOffsetY;
657
                }
658
                finally
659
                {
660
                    c.enableContentOffsetUpdateFromScale = false;
661
                }
662
            }
663

    
664
            if (c.scrollOwner != null)
665
            {
666
                c.scrollOwner.InvalidateScrollInfo();
667
            }
668

    
669
        } 
670
        #endregion
671

    
672
        #region ContentOffset X & Y
673

    
674
        /// <summary>
675
        /// Method called to clamp the 'ContentOffsetX' value to its valid range.
676
        /// </summary>
677
        private static object ContentOffsetX_Coerce(DependencyObject d, object baseValue)
678
        {
679
            ZoomAndPanControl c = (ZoomAndPanControl)d;
680
            double value = (double)baseValue;
681
            double minOffsetX = 0.0;
682
            double maxOffsetX = Math.Max(0.0, c.unScaledExtent.Width - c.ConstrainedContentViewportWidth);
683
            value = Math.Min(Math.Max(value, minOffsetX), maxOffsetX);
684
            return value;
685
        }
686

    
687
        /// <summary>
688
        /// Event raised when the 'ContentOffsetX' property has changed value.
689
        /// </summary>
690
        private static void ContentOffsetX_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
691
        {
692
            ZoomAndPanControl c = (ZoomAndPanControl)o;
693

    
694
            c.UpdateTranslationX();
695

    
696
            if (c.scrollOwner != null)
697
            {
698
                c.scrollOwner.InvalidateScrollInfo();
699
            }
700
        }
701

    
702
        /// <summary>
703
        /// Method called to clamp the 'ContentOffsetY' value to its valid range.
704
        /// </summary>
705
        private static object ContentOffsetY_Coerce(DependencyObject d, object baseValue)
706
        {
707
            ZoomAndPanControl c = (ZoomAndPanControl)d;
708
            double value = (double)baseValue;
709
            double minOffsetY = 0.0;
710
            double maxOffsetY = Math.Max(0.0, c.unScaledExtent.Height - c.ConstrainedContentViewportHeight);
711
            value = Math.Min(Math.Max(value, minOffsetY), maxOffsetY);
712
            return value;
713
        }
714

    
715
        /// <summary>
716
        /// Event raised when the 'ContentOffsetY' property has changed value.
717
        /// </summary>
718
        private static void ContentOffsetY_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
719
        {
720

    
721
            ZoomAndPanControl c = (ZoomAndPanControl)o;
722
            c.UpdateTranslationY();
723

    
724
            if (c.scrollOwner != null)
725
            {
726
                c.scrollOwner.InvalidateScrollInfo();
727
            }
728
        } 
729

    
730

    
731
        public void AnimatedZoomTo(Rect contentRect)
732
        {
733
            var ScaleX = this.ContentViewportWidth / contentRect.Width;
734
            var ScaleY = this.ContentViewportHeight / contentRect.Height;
735
            var NewContentScale = this.ContentScale * Math.Min(ScaleX, ScaleY);
736
            ContentZoomFocusX = contentRect.X + (contentRect.Width / 2);
737
            ContentZoomFocusY = contentRect.Y + (contentRect.Height/ 2);
738
            AnimatedZoomPointToViewportCenter(NewContentScale, new Point(ContentZoomFocusX, ContentZoomFocusY), null);
739
        }
740

    
741
        /// <summary>
742
        /// Zoom to the specified scale and move the specified focus point to the center of the viewport.
743
        /// </summary>
744
        private void AnimatedZoomPointToViewportCenter(double newContentScale, Point contentZoomFocus, EventHandler callback)
745
        {
746
            newContentScale = Math.Min(Math.Max(newContentScale, MinContentScale), MaxContentScale);
747
            
748
            ContentZoomFocusX = contentZoomFocus.X;
749
            ContentZoomFocusY = contentZoomFocus.Y;
750

    
751
            enableContentOffsetUpdateFromScale = true;
752

    
753
            AnimationHelper.StartAnimation(this, ContentScaleProperty, newContentScale, AnimationDuration,
754
                delegate(object sender, EventArgs e ){
755
                    enableContentOffsetUpdateFromScale = false; 
756
                    if(callback  != null)
757
                    {
758
                        callback(this, EventArgs.Empty);
759
                    }
760
                });
761
        }
762

    
763
        /// <summary>
764
        /// Do animation that scales the content so that it fits completely in the control.
765
        /// </summary>
766
        public void AnimatedScaleToFit()
767
        {
768
            if (content == null)
769
            {
770
                throw new ApplicationException("PART_Content was not found in the ZoomAndPanControl visual template!");
771
            }
772

    
773
            AnimatedZoomTo(new Rect(0, 0, content.ActualWidth, content.ActualHeight));
774
        }
775

    
776
        #endregion Dependency PropertyChangedEvent
777

    
778

    
779
        #region Max & Min ContentScale
780
        private static void MinOrMaxContentScale_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
781
        {
782
            ZoomAndPanControl c = (ZoomAndPanControl)o;
783
            c.ContentScale = Math.Min(Math.Max(c.ContentScale, c.MinContentScale), c.MaxContentScale);
784
        }  
785
        #endregion
786

    
787
        #endregion
788

    
789
        /// <summary>
790
        /// Set TranslateTransform X Coordinate
791
        /// </summary>
792
        public void SetContentOffsetTransformX()
793
        {
794
            this.contentOffsetTransform.X = ContentOffsetTransformX;
795
        }
796

    
797
        /// <summary>
798
        /// Set TranslateTransform Y Coordinate
799
        /// </summary>
800
        public void SetContentOffsetTransformY()
801
        {
802
            this.contentOffsetTransform.Y = ContentOffsetTransformY;
803
        }
804

    
805
        /// <summary>
806
        /// Update the Y coordinate of the translation transformation.
807
        /// </summary>
808
        private void UpdateTranslationY()
809
        {
810
            if(this.contentOffsetTransform != null)
811
            {
812
                ///Canvas의 Height 값 * ContentScaled 수치를 곱하여 scaledContentHeight를 구한다.
813
                ScaledContentHeight = this.unScaledExtent.Height * this.ContentScale;
814

    
815
                ////Viewport보다 scaledContentHeight 값이 작다면
816
                if (ScaledContentHeight < this.viewport.Height)
817
                {
818
                    this.ContentOffsetTransformY = (ContentViewportHeight - this.unScaledExtent.Height) / 2;
819
                }
820
                else
821
                {
822
                    this.ContentOffsetTransformY = -this.ContentOffsetY;
823
                }
824

    
825
                this.ContentOffsetY = this.ContentOffsetY;
826
            }
827
        }
828

    
829
        /// <summary>
830
        /// Update the X coordinate of the translation transformation.
831
        /// </summary>
832
        private void UpdateTranslationX()
833
        {
834
            if (this.contentOffsetTransform != null)
835
            {
836
                ScaledContentWidth = this.unScaledExtent.Width * this.ContentScale;
837

    
838
                if (ScaledContentWidth < this.viewport.Width)
839
                {
840
                    this.ContentOffsetTransformX = (ContentViewportWidth - this.unScaledExtent.Width) / 2;
841
                }
842
                else
843
                {
844
                    this.ContentOffsetTransformX = -this.ContentOffsetX;
845
                }
846

    
847
                this.ContentOffsetX = this.ContentOffsetX;
848
            }
849
        }
850

    
851
        /// <summary>
852
        /// Update the size of the viewport in content coordinates after the viewport size or 'ContentScale' has changed
853
        /// </summary>
854
        private void UpdateContentViewportSize()
855
        {
856
            ContentViewportWidth = viewport.Width / ContentScale;
857
            ContentViewportHeight = viewport.Height / ContentScale;
858

    
859
            ConstrainedContentViewportWidth = Math.Min(ContentViewportWidth, unScaledExtent.Width);
860
            ConstrainedContentViewportHeight = Math.Min(ContentViewportHeight, unScaledExtent.Height);
861

    
862
            UpdateTranslationX();
863
            UpdateTranslationY();
864
        }
865

    
866
        /// <summary>
867
        /// Update the viewport size from the specified size. (If viewport Changed Scale or Size)
868
        /// </summary>
869
        private void UpdateViewportSize(Size newSize)
870
        {
871
            //if (viewport == newSize)
872
            //{
873
            //    UpdateContentViewportSize();
874
            //    return;
875
            //}
876

    
877
            viewport = newSize;
878

    
879
            UpdateContentViewportSize();
880

    
881
            UpdateContentZoomFocusX();
882
            UpdateContentZoomFocusY();
883

    
884
            //
885
            // Update content offset from itself when the size of the viewport changes.
886
            // This ensures that the content offset remains properly clamped to its valid range.
887
            //
888
            this.ContentOffsetX = this.ContentOffsetX;
889
            this.ContentOffsetY = this.ContentOffsetY;
890

    
891
            if (scrollOwner != null)
892
            {
893
                scrollOwner.InvalidateScrollInfo();
894
            }
895
        }
896

    
897
        /// <summary>
898
        /// Zoom in/out centered on the specified point (in content coordinates).
899
        /// The focus point is kept locked to it's on sceen position (ala google maps).
900
        /// </summary>
901
        /// <param name="newContentScale">New Scale</param>
902
        /// <param name="contentZoomFocus">Zoom Focuse Point</param>
903
        public void ZoomAboutPoint(double newContentScale, Point contentZoomFocus)
904
        {
905
            newContentScale = Math.Min(Math.Max(newContentScale, MinContentScale), MaxContentScale);
906

    
907
            if (contentOffsetTransform !=null)
908
            {
909
                var diffPointAndOffsetX = (contentZoomFocus.X + contentOffsetTransform.X);
910
                var diffPointAndOffsetY = (contentZoomFocus.Y + contentOffsetTransform.Y);
911

    
912
                ///Scale로 압축
913
                ///ScreenSpace 좌표 내에서 Offset 좌표를 뺀 후 현재 Scale 로 압축.
914
                double screenSpaceZoomOffsetX = diffPointAndOffsetX * ContentScale;
915
                double screenSpaceZoomOffsetY = diffPointAndOffsetY * ContentScale;
916

    
917
                ///Scale로 늘림
918
                ///Scale로 압축된 좌표를 새로운 스케일로 늘린 contentSpaceZoom 좌표 
919
                double contentSpaceZoomOffsetX = screenSpaceZoomOffsetX / newContentScale;
920
                double contentSpaceZoomOffsetY = screenSpaceZoomOffsetY / newContentScale;
921

    
922
                ///내가 원하는 좌표 포인트에 contentSpaceZoom 좌표를 뺀 다. 
923
                double newContentOffsetX = contentZoomFocus.X - contentSpaceZoomOffsetX;
924
                double newContentOffsetY = contentZoomFocus.Y - contentSpaceZoomOffsetY;
925

    
926
                this.ContentScale = newContentScale;
927
                this.ContentOffsetX = newContentOffsetX;
928
                this.ContentOffsetY = newContentOffsetY;
929
            }
930
          
931
        }
932

    
933
        /// <summary>
934
        /// ContentOffsetX 
935
        /// </summary>
936
        private void UpdateContentZoomFocusX()
937
        {
938
            ContentZoomFocusX = ContentOffsetX + (ConstrainedContentViewportWidth / 2);
939
        }
940

    
941
        private void UpdateContentZoomFocusY()
942
        {
943
            ContentZoomFocusY = ContentOffsetY + (ConstrainedContentViewportHeight / 2);
944
        }
945

    
946
        public void AnimatedZoomTo(double contentScale)
947
        {
948
            Point zoomCenter = new Point(ContentOffsetX + (ContentViewportWidth / 2),
949
                ContentOffsetY + (ContentViewportHeight / 2));
950

    
951
            AnimatedZoomAboutPoint(contentScale, zoomCenter);
952
        }
953

    
954
        /// <summary>
955
        /// Use animation to center the view on the specified point (in content coordinates).
956
        /// </summary>
957
        public void AnimatedSnapTo(Point contentPoint)
958
        {
959
            double newX = contentPoint.X - (this.ContentViewportWidth / 2);
960
            double newY = contentPoint.Y - (this.ContentViewportHeight / 2);
961

    
962
            AnimationHelper.StartAnimation(this, ContentOffsetXProperty, newX, AnimationDuration);
963
            AnimationHelper.StartAnimation(this, ContentOffsetYProperty, newY, AnimationDuration);
964
        }
965

    
966
        public void AnimatedZoomAboutPoint(double newContentScale, Point contentZoomFocus)
967
        {
968
            newContentScale = Math.Min(Math.Max(newContentScale, MinContentScale), MaxContentScale);
969

    
970
            ContentZoomFocusX = contentZoomFocus.X;
971
            ContentZoomFocusY = contentZoomFocus.Y;
972

    
973
            enableContentOffsetUpdateFromScale = true;
974

    
975
            AnimationHelper.StartAnimation(this, ContentScaleProperty, newContentScale, AnimationDuration,
976
                delegate(object sender, EventArgs e)
977
                {
978
                    enableContentOffsetUpdateFromScale = false;
979
                });
980
        }
981

    
982
        public void ScaleToFit()
983
       {
984
            if(content== null)
985
            {
986
                throw new ApplicationException("PART_Content was not found in the ZoomAndPanControl visual template!");
987
            }
988

    
989
            ZoomTo(new Rect(0,0, content.ActualWidth, content.ActualHeight));
990
        }
991

    
992

    
993
        public void Sync_ZoomTo(Rect contentRect)
994
        {
995
            double scaleX = this.ContentViewportWidth / contentRect.Width;
996
            double scaleY = this.ContentViewportHeight / contentRect.Height;
997
            double newScale = this.ContentScale * Math.Min(scaleX, scaleY);
998

    
999
            newScale /= 2;
1000

    
1001
            ZoomPointToViewportCenter(newScale, new Point(contentRect.X +
1002
                (contentRect.Width), contentRect.Y + (contentRect.Height / 2)));
1003
        }
1004

    
1005
        public void ZoomTo(Rect contentRect)
1006
        {
1007
            double scaleX = this.ContentViewportWidth / contentRect.Width;
1008
            double scaleY = this.ContentViewportHeight / contentRect.Height;
1009
            double newScale = this.ContentScale * Math.Min(scaleX, scaleY);
1010

    
1011
            ZoomPointToViewportCenter(newScale, new Point(contentRect.X +
1012
                (contentRect.Width / 2), contentRect.Y + (contentRect.Height / 2)));
1013
        }
1014

    
1015
        public void ZoomPointToViewportCenter(double newContentScale, Point contentZoomFocus)
1016
        {
1017
            newContentScale = Math.Min(Math.Max(newContentScale, MinContentScale), MaxContentScale);
1018
            this.ContentScale = newContentScale;
1019
            this.ContentOffsetX = contentZoomFocus.X - (ContentViewportWidth / 2);
1020
            this.ContentOffsetY = contentZoomFocus.Y - (ContentViewportHeight / 2);
1021

    
1022
        }
1023
    }
1024
}
클립보드 이미지 추가 (최대 크기: 500 MB)