public override bool Equals(object o) { bool result = false; if (o is ZoomboxView) { ZoomboxView other = (ZoomboxView)o; if (this.ViewKind == other.ViewKind) { switch (this.ViewKind) { case ZoomboxViewKind.Absolute: result = (DoubleHelper.AreVirtuallyEqual(_scaleWidth, other._scaleWidth)) && (DoubleHelper.AreVirtuallyEqual(Position, other.Position)); break; case ZoomboxViewKind.Region: result = DoubleHelper.AreVirtuallyEqual(Region, other.Region); break; default: result = true; break; } } } return(result); }
public override object ConvertTo( ITypeDescriptorContext typeDescriptorContext, CultureInfo cultureInfo, object value, Type destinationType) { object result = null; ZoomboxView view = value as ZoomboxView; if (view != null) { if (destinationType == typeof(string)) { result = "Empty"; switch (view.ViewKind) { case ZoomboxViewKind.Absolute: if (PointHelper.IsEmpty(view.Position)) { if (!DoubleHelper.IsNaN(view.Scale)) { result = view.Scale.ToString(); } } else if (DoubleHelper.IsNaN(view.Scale)) { result = view.Position.X.ToString() + "," + view.Position.Y.ToString(); } else { result = view.Scale.ToString() + "," + view.Position.X.ToString() + "," + view.Position.Y.ToString(); } break; case ZoomboxViewKind.Center: result = "Center"; break; case ZoomboxViewKind.Fill: result = "Fill"; break; case ZoomboxViewKind.Fit: result = "Fit"; break; case ZoomboxViewKind.Region: result = view.Region.X.ToString() + "," + view.Region.Y.ToString() + "," + view.Region.Width.ToString() + "," + view.Region.Height.ToString(); break; } } } return(result == null ? base.ConvertTo(typeDescriptorContext, cultureInfo, value, destinationType) : result); }
private void SetCurrentView( ZoomboxView value ) { this.SetValue( Zoombox.CurrentViewPropertyKey, value ); }
private void UpdateView( ZoomboxView view, bool allowAnimation, bool allowStackAddition, int stackIndex ) { if( _contentPresenter == null || _content == null || !this.HasArrangedContentPresenter ) return; // if an absolute view is being used and only a Scale value has been specified, // use the ZoomTo() function to perform a relative zoom if( view.ViewKind == ZoomboxViewKind.Absolute && PointHelper.IsEmpty( view.Position ) ) { this.ZoomTo( view.Scale, allowStackAddition ); return; } // disallow reentrancy if( !this.IsUpdatingView ) { this.IsUpdatingView = true; try { // determine the new scale and position double newRelativeScale = _viewboxFactor; Point newRelativePosition = new Point(); Rect region = Rect.Empty; switch( view.ViewKind ) { case ZoomboxViewKind.Empty: break; case ZoomboxViewKind.Absolute: newRelativeScale = DoubleHelper.IsNaN( view.Scale ) ? _relativeScale : view.Scale; newRelativePosition = PointHelper.IsEmpty( view.Position ) ? _relativePosition : new Point( view.Position.X, view.Position.Y ) - this.ContentOffset * newRelativeScale; break; case ZoomboxViewKind.Region: region = view.Region; break; case ZoomboxViewKind.Center: { // get the current ContentRect in the coordinate space of the Zoombox control Rect currentContentRect = new Rect( _content.TranslatePoint( this.ContentRect.TopLeft, this ), _content.TranslatePoint( this.ContentRect.BottomRight, this ) ); // inflate (or deflate) the rect by the appropriate amounts in the x & y directions region = Rect.Inflate( currentContentRect, ( _content.DesiredSize.Width / _viewboxFactor - currentContentRect.Width ) / 2, ( _content.DesiredSize.Height / _viewboxFactor - currentContentRect.Height ) / 2 ); // now translate the centered rect back to the coordinate space of the content region = new Rect( this.TranslatePoint( region.TopLeft, _content ), this.TranslatePoint( region.BottomRight, _content ) ); } break; case ZoomboxViewKind.Fit: region = this.ContentRect; break; case ZoomboxViewKind.Fill: region = this.CalculateFillRect(); break; } if( view.ViewKind != ZoomboxViewKind.Empty ) { if( !region.IsEmpty ) { // ZOOMING TO A REGION this.CalculatePositionAndScale( region, ref newRelativePosition, ref newRelativeScale ); } else if( view != ZoomboxView.Empty ) { // USING ABSOLUTE POSITION AND SCALE VALUES // ensure that the scale value falls within the valid range if( newRelativeScale > MaxScale ) { newRelativeScale = MaxScale; } else if( newRelativeScale < MinScale ) { newRelativeScale = MinScale; } } double currentScale = _relativeScale; double currentX = _relativePosition.X; double currentY = _relativePosition.Y; ScaleTransform st = null; TranslateTransform tt = null; TransformGroup tg = null; if( _contentPresenter.RenderTransform != Transform.Identity ) { tg = _contentPresenter.RenderTransform as TransformGroup; st = tg.Children[ 0 ] as ScaleTransform; tt = tg.Children[ 1 ] as TranslateTransform; currentScale = st.ScaleX; currentX = tt.X; currentY = tt.Y; } if( KeepContentInBounds == true ) { Rect boundsRect = new Rect( new Size( this.ContentRect.Width * newRelativeScale, this.ContentRect.Height * newRelativeScale ) ); // Calc viewport rect (should be inside bounds content rect) Point viewportPosition = new Point( -newRelativePosition.X, -newRelativePosition.Y ); Rect viewportRect = new Rect( viewportPosition, _contentPresenter.RenderSize ); if( DoubleHelper.AreVirtuallyEqual( _relativeScale, newRelativeScale ) ) // we are positioning the content, not scaling { // Handle the width and height seperately since the content extent // could be contained only partially in the viewport. Also if the viewport is only // partially contained within the content extent. // // Content extent width is greater than the viewport's width (Zoomed in). Make sure we restrict // the viewport X inside the content. // if( this.IsGreaterThanOrClose( boundsRect.Width, viewportRect.Width ) ) { if( boundsRect.Right < viewportRect.Right ) { newRelativePosition.X = -( boundsRect.Width - viewportRect.Width ); } if( boundsRect.Left > viewportRect.Left ) { newRelativePosition.X = 0; } } // // Viewport width is greater than the content extent's width (Zoomed out). Make sure we restrict // the content X inside the viewport. // else if( this.IsGreaterThanOrClose( viewportRect.Width, boundsRect.Width ) ) { if( viewportRect.Right < boundsRect.Right ) { newRelativePosition.X = viewportRect.Width - boundsRect.Width; } if( viewportRect.Left > boundsRect.Left ) { newRelativePosition.X = 0; } } // // Content extent height is greater than the viewport's height (Zoomed in). Make sure we restrict // the viewport Y inside the content. // if( this.IsGreaterThanOrClose( boundsRect.Height, viewportRect.Height ) ) { if( boundsRect.Bottom < viewportRect.Bottom ) { newRelativePosition.Y = -( boundsRect.Height - viewportRect.Height ); } if( boundsRect.Top > viewportRect.Top ) { newRelativePosition.Y = 0; } } // // Viewport height is greater than the content extent's height (Zoomed out). Make sure we restrict // the content Y inside the viewport. // else if( this.IsGreaterThanOrClose( viewportRect.Height, boundsRect.Height ) ) { if( viewportRect.Bottom < boundsRect.Bottom ) { newRelativePosition.Y = viewportRect.Height - boundsRect.Height; } if( viewportRect.Top > boundsRect.Top ) { newRelativePosition.Y = 0; } } } } st = new ScaleTransform( newRelativeScale / _viewboxFactor, newRelativeScale / _viewboxFactor ); tt = new TranslateTransform( newRelativePosition.X, newRelativePosition.Y ); tg = new TransformGroup(); tg.Children.Add( st ); tg.Children.Add( tt ); _contentPresenter.RenderTransform = tg; var initialContentSize = ( _content is Viewbox ) ? ( ( Viewbox )_content ).Child.DesiredSize : _content.DesiredSize; var scaledContentSize = new Size( initialContentSize.Width * newRelativeScale, initialContentSize.Height * newRelativeScale ); if( allowAnimation && IsAnimated ) { DoubleAnimation daScale = new DoubleAnimation( currentScale, newRelativeScale / _viewboxFactor, AnimationDuration ); daScale.AccelerationRatio = this.AnimationAccelerationRatio; daScale.DecelerationRatio = this.AnimationDecelerationRatio; DoubleAnimation daTranslateX = new DoubleAnimation( currentX, newRelativePosition.X, AnimationDuration ); daTranslateX.AccelerationRatio = this.AnimationAccelerationRatio; daTranslateX.DecelerationRatio = this.AnimationDecelerationRatio; DoubleAnimation daTranslateY = new DoubleAnimation( currentY, newRelativePosition.Y, AnimationDuration ); daTranslateY.AccelerationRatio = this.AnimationAccelerationRatio; daTranslateY.DecelerationRatio = this.AnimationDecelerationRatio; daTranslateY.CurrentTimeInvalidated += new EventHandler( this.UpdateViewport ); daTranslateY.CurrentStateInvalidated += new EventHandler( this.ZoomAnimationCompleted ); // raise animation beginning event before beginning the animations RaiseEvent( new RoutedEventArgs( AnimationBeginningEvent, this ) ); st.BeginAnimation( ScaleTransform.ScaleXProperty, daScale ); st.BeginAnimation( ScaleTransform.ScaleYProperty, daScale ); tt.BeginAnimation( TranslateTransform.XProperty, daTranslateX ); tt.BeginAnimation( TranslateTransform.YProperty, daTranslateY ); if( this.IsUsingScrollBars ) { //Vertical scrollBar animations DoubleAnimation verticalMaxAnimation = new DoubleAnimation(); verticalMaxAnimation.From = _verticalScrollBar.Maximum; verticalMaxAnimation.To = scaledContentSize.Height - _verticalScrollBar.ViewportSize; verticalMaxAnimation.Duration = AnimationDuration; _verticalScrollBar.BeginAnimation( ScrollBar.MaximumProperty, verticalMaxAnimation ); DoubleAnimation verticalValueAnimation = new DoubleAnimation(); verticalValueAnimation.From = _verticalScrollBar.Value; verticalValueAnimation.To = -newRelativePosition.Y; verticalValueAnimation.Duration = AnimationDuration; verticalValueAnimation.Completed += this.VerticalValueAnimation_Completed; _verticalScrollBar.BeginAnimation( ScrollBar.ValueProperty, verticalValueAnimation ); //Horizontal scrollBar animations DoubleAnimation horizontalMaxAnimation = new DoubleAnimation(); horizontalMaxAnimation.From = _horizontalScrollBar.Maximum; horizontalMaxAnimation.To = scaledContentSize.Width - _horizontalScrollBar.ViewportSize; horizontalMaxAnimation.Duration = AnimationDuration; _horizontalScrollBar.BeginAnimation( ScrollBar.MaximumProperty, horizontalMaxAnimation ); DoubleAnimation horizontalValueAnimation = new DoubleAnimation(); horizontalValueAnimation.From = _horizontalScrollBar.Value; horizontalValueAnimation.To = -newRelativePosition.X; horizontalValueAnimation.Duration = AnimationDuration; horizontalValueAnimation.Completed += this.HorizontalValueAnimation_Completed; _horizontalScrollBar.BeginAnimation( ScrollBar.ValueProperty, horizontalValueAnimation ); } } else if( this.IsUsingScrollBars ) { //Vertical scrollBar _verticalScrollBar.Maximum = scaledContentSize.Height - _verticalScrollBar.ViewportSize; _verticalScrollBar.Value = -newRelativePosition.Y; //Horizontal scrollBar _horizontalScrollBar.Maximum = scaledContentSize.Width - _horizontalScrollBar.ViewportSize; _horizontalScrollBar.Value = -newRelativePosition.X; } // maintain the relative scale and position for dragging and animating purposes _relativePosition = newRelativePosition; _relativeScale = newRelativeScale; // update the Scale and Position properties to keep them in sync with the current view this.Scale = newRelativeScale; _basePosition = newRelativePosition + this.ContentOffset * newRelativeScale; this.UpdateViewport(); } // add the current view to the view stack if( this.EffectiveViewStackMode == ZoomboxViewStackMode.Auto && allowStackAddition ) { // if the last view was pushed on the stack within the last 300 milliseconds, discard it // since proximally close views are probably the result of a mouse wheel scroll if( this.ViewStack.Count > 1 && Math.Abs( DateTime.Now.Ticks - _lastStackAddition.Ticks ) < TimeSpan.FromMilliseconds( 300 ).Ticks ) { this.ViewStack.RemoveAt( this.ViewStack.Count - 1 ); _lastStackAddition = DateTime.Now - TimeSpan.FromMilliseconds( 300 ); } // if the current view is the same as the last view, do nothing if( this.ViewStack.Count > 0 && view == this.ViewStack.SelectedView ) { // do nothing } else { // otherwise, push the current view on stack this.ViewStack.PushView( view ); this.ViewStackIndex++; stackIndex = this.ViewStackIndex; // update the timestamp for the last stack addition _lastStackAddition = DateTime.Now; } } // update the stack indices used by CurrentViewChanged event _lastViewIndex = this.CurrentViewIndex; this.SetCurrentViewIndex( stackIndex ); // set the new view parameters // NOTE: this is the only place where the CurrentView member should be set this.SetCurrentView( view ); } finally { this.IsUpdatingView = false; } } }
private void UpdateView( ZoomboxView view, bool allowAnimation, bool allowStackAddition ) { this.UpdateView( view, allowAnimation, allowStackAddition, -1 ); }
private void ContentPresenterFirstArranged( object sender, EventArgs e ) { // remove the handler _contentPresenter.LayoutUpdated -= new EventHandler( this.ContentPresenterFirstArranged ); // it's now safe to update the view this.HasArrangedContentPresenter = true; this.InvalidateVisual(); // temporarily disable animations bool oldAnimated = IsAnimated; this.IsAnimated = false; try { // set the initial view double scale = this.Scale; Point position = this.Position; // if there are items in the ViewStack and a ViewStackIndex is set, use it if( this.EffectiveViewStackMode != ZoomboxViewStackMode.Disabled ) { bool isInitialViewSet = false; if( this.ViewStack.Count > 0 ) { if( this.ViewStackIndex >= 0 ) { if( this.ViewStackIndex > this.ViewStack.Count - 1 ) { this.ViewStackIndex = this.ViewStack.Count - 1; } else { this.UpdateView( this.ViewStack[ this.ViewStackIndex ], false, false, this.ViewStackIndex ); } } else if( this.EffectiveViewStackMode != ZoomboxViewStackMode.Auto ) { // if this is not an auto-stack, then ensure the index is set to a valid value if( this.ViewStackIndex < 0 ) { this.ViewStackIndex = 0; } } // if a ViewStackIndex has been set, apply the scale and position values, iff different than the default values if( this.ViewStackIndex >= 0 ) { isInitialViewSet = true; if( !DoubleHelper.IsNaN( scale ) || !PointHelper.IsEmpty( position ) ) { this.UpdateView( new ZoomboxView( scale, position ), false, false ); } } } if( !isInitialViewSet ) { // set initial view according to the scale and position values and push it on the stack, as necessary ZoomboxView initialView = new ZoomboxView( DoubleHelper.IsNaN( Scale ) ? 1.0 : Scale, PointHelper.IsEmpty( position ) ? new Point() : position ); if( this.EffectiveViewStackMode == ZoomboxViewStackMode.Auto ) { this.ViewStack.PushView( initialView ); this.ViewStackIndex = 0; } else { this.UpdateView( initialView, false, false ); } } } else { // just set initial view according to the scale and position values ZoomboxView initialView = new ZoomboxView( DoubleHelper.IsNaN( Scale ) ? 1.0 : this.Scale, position ); this.UpdateView( initialView, false, false ); } } finally { IsAnimated = oldAnimated; } }
public void ZoomTo( ZoomboxView view ) { this.UpdateView( view, true, true ); }
public override object ConvertFrom( ITypeDescriptorContext typeDescriptorContext, CultureInfo cultureInfo, object value ) { ZoomboxView result = null; if( value is double ) { result = new ZoomboxView( ( double )value ); } else if( value is Point ) { result = new ZoomboxView( ( Point )value ); } else if( value is Rect ) { result = new ZoomboxView( ( Rect )value ); } else if( value is string ) { if( string.IsNullOrEmpty( ( value as string ).Trim() ) ) { result = ZoomboxView.Empty; } else { switch( ( value as string ).Trim().ToLower() ) { case "center": result = ZoomboxView.Center; break; case "empty": result = ZoomboxView.Empty; break; case "fill": result = ZoomboxView.Fill; break; case "fit": result = ZoomboxView.Fit; break; default: // parse double values; respect the following separators: ' ', ';', or ',' List<double> values = new List<double>(); foreach( string token in ( value as string ).Split( new char[] { ' ', ';', ',' }, StringSplitOptions.RemoveEmptyEntries ) ) { double d; if( double.TryParse( token, out d ) ) { values.Add( d ); } if( values.Count >= 4 ) { // disregard additional values break; } } switch( values.Count ) { case 1: // scale result = new ZoomboxView( values[ 0 ] ); break; case 2: // x, y result = new ZoomboxView( values[ 0 ], values[ 1 ] ); break; case 3: // scale, x, y result = new ZoomboxView( values[ 0 ], values[ 1 ], values[ 2 ] ); break; case 4: // x, y, width, height result = new ZoomboxView( values[ 0 ], values[ 1 ], values[ 2 ], values[ 3 ] ); break; } break; } } } return ( result == null ? base.ConvertFrom( typeDescriptorContext, cultureInfo, value ) : result ); }
public override object ConvertFrom( ITypeDescriptorContext typeDescriptorContext, CultureInfo cultureInfo, object value) { ZoomboxView result = null; if (value is double) { result = new ZoomboxView((double)value); } else if (value is Point) { result = new ZoomboxView((Point)value); } else if (value is Rect) { result = new ZoomboxView((Rect)value); } else if (value is string) { if (string.IsNullOrEmpty((value as string).Trim())) { result = ZoomboxView.Empty; } else { switch ((value as string).Trim().ToLower()) { case "center": result = ZoomboxView.Center; break; case "empty": result = ZoomboxView.Empty; break; case "fill": result = ZoomboxView.Fill; break; case "fit": result = ZoomboxView.Fit; break; default: // parse double values; respect the following separators: ' ', ';', or ',' List <double> values = new List <double>(); foreach (string token in (value as string).Split(new char[] { ' ', ';', ',' }, StringSplitOptions.RemoveEmptyEntries)) { double d; if (double.TryParse(token, out d)) { values.Add(d); } if (values.Count >= 4) { // disregard additional values break; } } switch (values.Count) { case 1: // scale result = new ZoomboxView(values[0]); break; case 2: // x, y result = new ZoomboxView(values[0], values[1]); break; case 3: // scale, x, y result = new ZoomboxView(values[0], values[1], values[2]); break; case 4: // x, y, width, height result = new ZoomboxView(values[0], values[1], values[2], values[3]); break; } break; } } } return(result == null ? base.ConvertFrom(typeDescriptorContext, cultureInfo, value) : result); }