프로젝트

일반

사용자정보

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

markus / ZoomAndPan / ZoomAndPanControl.cs @ e1c892f7

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