public override bool Equals(object o) { bool result = false; if (o is ZoomboxView) { ZoomboxView other = (ZoomboxView)o; if (ViewKind == other.ViewKind) { switch (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 ?? base.ConvertTo(typeDescriptorContext, cultureInfo, value, destinationType)); }
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[] { ' ', ';', ',' }, 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 ?? base.ConvertFrom(typeDescriptorContext, cultureInfo, value)); }
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[] { ' ', ';', ',' }, 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 ?? base.ConvertFrom(typeDescriptorContext, cultureInfo, value)); }
public void ZoomTo(ZoomboxView view) { UpdateView(view, true, true); }
private void UpdateView(ZoomboxView view, bool allowAnimation, bool allowStackAddition, int stackIndex) { if (_contentPresenter == null || _content == null || !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)) { ZoomTo(view.Scale, allowStackAddition); return; } // disallow reentrancy if (!IsUpdatingView) { 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) - 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(ContentRect.TopLeft, this), _content.TranslatePoint(ContentRect.BottomRight, this)); // inflate (or deflate) the rect by the appropriate amounts in the x & y directions region = Rect.Inflate(currentContentRect, (RenderSize.Width / _viewboxFactor - currentContentRect.Width) / 2, (RenderSize.Height / _viewboxFactor - currentContentRect.Height) / 2); // now translate the centered rect back to the coordinate space of the content region = new Rect(TranslatePoint(region.TopLeft, _content), TranslatePoint(region.BottomRight, _content)); } break; case ZoomboxViewKind.Fit: region = ContentRect; break; case ZoomboxViewKind.Fill: region = CalculateFillRect(); break; } if (view.ViewKind != ZoomboxViewKind.Empty) { if (!region.IsEmpty) { // ZOOMING TO A REGION 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) { Rect boundsRect = new Rect(new Size(ContentRect.Width * newRelativeScale, 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 (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 (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 (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 (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; if (allowAnimation && IsAnimated) { DoubleAnimation daScale = new DoubleAnimation(currentScale, newRelativeScale / _viewboxFactor, AnimationDuration); daScale.AccelerationRatio = AnimationAccelerationRatio; daScale.DecelerationRatio = AnimationDecelerationRatio; DoubleAnimation daTranslateX = new DoubleAnimation(currentX, newRelativePosition.X, AnimationDuration); daTranslateX.AccelerationRatio = AnimationAccelerationRatio; daTranslateX.DecelerationRatio = AnimationDecelerationRatio; DoubleAnimation daTranslateY = new DoubleAnimation(currentY, newRelativePosition.Y, AnimationDuration); daTranslateY.AccelerationRatio = AnimationAccelerationRatio; daTranslateY.DecelerationRatio = AnimationDecelerationRatio; daTranslateY.CurrentTimeInvalidated += UpdateViewport; daTranslateY.CurrentStateInvalidated += 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); } // 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 Scale = newRelativeScale; _basePosition = newRelativePosition + ContentOffset * newRelativeScale; UpdateViewport(); } // add the current view to the view stack if (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 (ViewStack.Count > 1 && Math.Abs(DateTime.Now.Ticks - _lastStackAddition.Ticks) < TimeSpan.FromMilliseconds(300).Ticks) { ViewStack.RemoveAt(ViewStack.Count - 1); _lastStackAddition = DateTime.Now - TimeSpan.FromMilliseconds(300); } // if the current view is the same as the last view, do nothing if (ViewStack.Count > 0 && view == ViewStack.SelectedView) { // do nothing } else { // otherwise, push the current view on stack ViewStack.PushView(view); ViewStackIndex++; stackIndex = ViewStackIndex; // update the timestamp for the last stack addition _lastStackAddition = DateTime.Now; } } // update the stack indices used by CurrentViewChanged event _lastViewIndex = CurrentViewIndex; SetCurrentViewIndex(stackIndex); // set the new view parameters // NOTE: this is the only place where the CurrentView member should be set SetCurrentView(view); } finally { IsUpdatingView = false; } } }
private void UpdateView(ZoomboxView view, bool allowAnimation, bool allowStackAddition) { UpdateView(view, allowAnimation, allowStackAddition, -1); }
private void SetCurrentView(ZoomboxView value) { this.SetValue(Zoombox.CurrentViewPropertyKey, value); }
private void ContentPresenterFirstArranged(object sender, EventArgs e) { // remove the handler _contentPresenter.LayoutUpdated -= ContentPresenterFirstArranged; // it's now safe to update the view HasArrangedContentPresenter = true; InvalidateVisual(); // temporarily disable animations bool oldAnimated = IsAnimated; IsAnimated = false; try { // set the initial view double scale = Scale; Point position = Position; // if there are items in the ViewStack and a ViewStackIndex is set, use it if (EffectiveViewStackMode != ZoomboxViewStackMode.Disabled) { bool isInitialViewSet = false; if (ViewStack.Count > 0) { if (ViewStackIndex >= 0) { if (ViewStackIndex > ViewStack.Count - 1) { ViewStackIndex = ViewStack.Count - 1; } else { UpdateView(ViewStack[ViewStackIndex], false, false, ViewStackIndex); } } else if (EffectiveViewStackMode != ZoomboxViewStackMode.Auto) { // if this is not an auto-stack, then ensure the index is set to a valid value if (ViewStackIndex < 0) { ViewStackIndex = 0; } } // if a ViewStackIndex has been set, apply the scale and position values, iff different than the default values if (ViewStackIndex >= 0) { isInitialViewSet = true; if (!DoubleHelper.IsNaN(scale) || !PointHelper.IsEmpty(position)) { 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 (EffectiveViewStackMode == ZoomboxViewStackMode.Auto) { ViewStack.PushView(initialView); ViewStackIndex = 0; } else { 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 : Scale, position); UpdateView(initialView, false, false); } } finally { IsAnimated = oldAnimated; } }