프로젝트

일반

사용자정보

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

markus / ZoomAndPan / ZoomAndPanControl.cs.bak @ c7b02506

이력 | 보기 | 이력해설 | 다운로드 (36 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

    
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)