banner



Mouse Move Rotate Animation Xaml Wpf Not Worked

Mouse Move Rotate Animation Xaml Wpf Not Worked

  • Download demo - 47.64 KB
  • Download source - 54.83 KB

Introduction

This is the third in a serial of manufactures that shows you how to build a powerful WPF custom control to use instead of the standard WPF Viewbox class.

The first article, ZoomBoxPanel implementing a selection of page view zoom modes in WPF: Part 1, introduced the core functionality of the ZoomBoxPanel and its ability to resize its contents into a number of different zoom modes, such equally Fit Page and & Fit Width.

This second commodity, ZoomBoxPanel: add custom commands to a WPF control: Role 2, extends ZoomBoxPanel to back up both standard and custom commands to zoom and rotate its contents.

This article extends ZoomBoxPanel to support the use of the mouse and the mouse wheel in unlike modes, and in addition, animation is added for zoom transitions.

Image 1

The ZoomBoxPanel Mouse Support

The ZoomBoxPanel developed in the previous manufactures has functionality for zooming, rotating, and positioning its content, which can be controlled by dissimilar commands. When adding mouse support, choices have to be fabricated on what functionality to support as a mouse tin perform only a limited number of bones deportment. As ZoomBoxPanel is intended to be used in a variety of different situations, several unlike modes of mouse operations are supported.

In the sit-in window, the mouse modes accept been added as a combo boxes on the toolbar and to the carte, allowing yous to play with each mode.

None Mouse clicks are ignored. Useful if you just desire the contents of the control to handle the mouse.
Pan Supports elevate and drop of the mouse to movement the content within the control.
Zoom Left click zooms in and right click zooms out.

The WheelMode specifies how the mouse wheel is used:

None Mouse bike is ignored.
Zoom Zoom in and out.
Scroll Pans the content up and downwardly.
Rotate Rotates the content clockwise and counterclockwise.

Image 2

The modes are dependency backdrop of the control, and can be set at design time from the Properties window.

          <          zb:ZoomBoxPanel                              ZoomMode          ="          FitPage"                              x:Name          ="          zoomBox"                              Filigree.Row          ="          2"                              MinZoom          ="          20"                              MaxZoom          ="          300"                              MouseMode          ="          Pan"                              WheelMode          ="          Zoom"          >          <          Edge                              BorderThickness          ="          three"                              BorderBrush          ="          Black"          >          <          Image                              Elevation          ="          525"                              Name          ="          image1"                              Stretch          ="          Compatible"                              Width          ="          700"                              Source          ="          /ZoomBox3;component/Images/marinbikeride.jpg"                              /          >          <          /Border          >          <          /zb:ZoomBoxPanel          >        

Animation is not appropriate in every situation, and can be abrasive to some people. The boolean dependency holding Animations switches animations on and off. It is linked to the Animations check box on the tool bar.

          <          CheckBox                              Margin          ="          10,1"                              IsChecked          ="          {Binding ElementName=zoomBox, Path=Animations}"          >Annimations<          /CheckBox          >          <          CheckBox                              Margin          ="          10,1"                              IsChecked          ="          {Bounden ElementName=zoomBox, Path=LockContent}"          >Lock<          /CheckBox          >        

The LockContent property is a way of turning off the mouse support at sure times, for instance, when doing a impress preview.

The ZoomBoxPanel Code

The control contains over 1200 lines of C# code, only some of which is described below. It is all there in the attached source code though.

The mouse and wheel modes are defined equally enums within the control class:

          public          enum          eMouseMode {     None,     Zoom,     Pan }          public          enum          eWheelMode {     None,     Zoom,     Gyre,     Rotate }

In order to support the new functionality, v extra dependency properties need to be divers:

          private          static          DependencyProperty MouseModeProperty;          private          static          DependencyProperty WheelModeProperty;

The way properties are of the type of their matching enumdue south. These are defined in the same style as the ZoomModeProperty defined in the previous commodity.

          individual          static          DependencyProperty WheelZoomDeltaProperty;

The WheelZoomDeltaProperty is the percentage by which the zoom should be increased or decreased on each movement of the mouse wheel.

          individual          static          DependencyProperty AnimationsProperty;          private          static          DependencyProperty LockContentProperty;

The final ii boolean dependency properties command the animation and lock functionality.

In gild to deal with the events generated by the mouse, the control defines seven different event handlers, which it adds to its own inherited mouse events.

          protected          override          void          OnInitialized(EventArgs e) {          base.OnInitialized(eastward);          this.MouseWheel += process_MouseWheel;          this.MouseDown += process_MouseDown;          this.MouseUp += process_MouseUp;          this.MouseMove += process_MouseMove;          this.PreviewMouseDown += process_PreviewMouseDown;          this.PreviewMouseUp += process_PreviewMouseUp;          this.PreviewMouseMove += process_PreviewMouseMove;

The higher up code uses a simplification feature in the latest compiler. This is the equivalent of defining a handler the one-time fashioned fashion.

          this.MouseWheel +=          new          MouseWheelEventHandler(process_MouseWheel);

The LockContent feature is implemented by the post-obit methods, which are used in demonstrating how the WPF event handling works:

          void          process_PreviewMouseDown(object          sender, MouseButtonEventArgs e) {          if          (LockContent)         e.Handled =          true; }          void          process_PreviewMouseUp(object          sender, MouseButtonEventArgs e) {          if          (LockContent)         e.Handled =          truthful; }          void          process_PreviewMouseMove(object          sender, MouseEventArgs east) {          if          (LockContent)         east.Handled =          true; }

Mouse events are first sent to the command nether the mouse. In the case of the demo, this volition be the epitome object. If that object does not handle the outcome, and so it is passed to its parent object, which then has the run a risk to process the result. Nonetheless, before sending the event itself, WPF sends a Preview event with the aforementioned parameters as the actual consequence. The preview event is sent in the reverse guild of the actual event, so it is starting time passed to the parent and and so to the child. So to implement the LockContent part, we simply need to set up the Handled property to true, and this volition forbid the post-obit mouse event existence generated, and so effectively turns off the mouse, both for this command and its contents.

Mouse Clicks

Provided the LockContent belongings is fix to imitation, the first mouse effect we are interested in volition be triggered.

          void          process_MouseDown(object          sender, MouseButtonEventArgs east) {          switch          (MouseMode)     {          instance          eMouseMode.Pan:          if          (e.ChangedButton == MouseButton.Left)             {          if          (((Keyboard.GetKeyStates(Key.LeftAlt) & KeyStates.Downwards) ==          0) &&                      ((Keyboard.GetKeyStates(Key.RightAlt) & KeyStates.Down) ==          0))                 {                     prevCursor =          this.Cursor;                     startMouseCapturePanel = e.GetPosition(this);          this.CaptureMouse();          this.Cursor = Cursors.ScrollAll;                 }             }          intermission;     } }

The drag and drib operation of the Pan mouse mode is started only if the Pan way is enabled, and it is the left mouse push that has been clicked, and the Ctrl and Alt keys are not pressed. The lawmaking saves the onetime cursor for the end of the operation, and the location in the control where the operation started. The CaptureMouse method ensures all future mouse events are sent to this command fifty-fifty if they occur exterior the command. Lastly, the cursor is changed to give the user an indicator that the drag and drib operation has started.

Once the mouse has been captured, it is a just thing of moving the contents every time the mouse is moved.

          void          process_MouseMove(object          sender, MouseEventArgs e) {     Point dcurrentMousePanel = e.GetPosition(this);          switch          (MouseMode)     {          case          eMouseMode.Pan:          if          (this.IsMouseCaptured)             {                 Point currentMousePanel = e.GetPosition(this);          double          deltaX = currentMousePanel.Ten - startMouseCapturePanel.X;          double          deltaY = currentMousePanel.Y - startMouseCapturePanel.Y;          if          ((deltaX !=          0) || (deltaY !=          0))                 {                     startMouseCapturePanel.X = currentMousePanel.X;                     startMouseCapturePanel.Y = currentMousePanel.Y;          if          (ApplyPanDelta(deltaX, deltaY))                         ApplyZoom(imitation);                  }             }          suspension;     } }

The method calculates the amount the mouse has moved and then calls the ApplyPanDelta method to practice the piece of work. The commencement position is then updated so that the next mouse movement event is relative to the current position.

          private          bool          ApplyPanDelta(double          deltaX,          double          deltaY) {          double          10 = panX + deltaX;          double          y = panY + deltaY;          switch          (ZoomMode)     {          instance          eZoomMode.CustomSize:          pause;          case          eZoomMode.ActualSize:          break;          example          eZoomMode.FitWidth:                          x = panX;          intermission;          instance          eZoomMode.FitHeight:                          y = panY;          break;          case          eZoomMode.FitPage:             10 = panX;             y = panY;          break;          case          eZoomMode.FitVisible:          intermission;     }          if          ((x != panX) || (y != panY))     {         panX = 10;         panY = y;          return          truthful;     }          return          simulated; }

The coordinates of the contents are held in the member variables panX and panY. So, making the motion is simply a matter of calculation the delta to the coordinates. Nevertheless, in certain zoom modes, motion is restricted in ane or both directions. For instance, in FitWidth mode, the contents can be moved upward or downwards, but non side to side.

When the mouse push button is released, the drag and driblet functioning is ended and the cursor is restored to its previous shape.

          void          process_MouseUp(object          sender, MouseButtonEventArgs e) {          switch          (MouseMode)     {          case          eMouseMode.Zoom:             {                 Point panelPoint = e.GetPosition(this);          int          factor =          0;          double          delta = (1          + (ZoomIncrement /          100));          switch          (east.ChangedButton)                 {          example          MouseButton.Left:                         factor =          ane;          break;          case          MouseButton.Right:                         factor =          i;                         delta =          one          / delta;          break;                 }                 ApplyZoomCommand(delta, factor, panelPoint);             }          intermission;     }          if          (IsMouseCaptured)     {         ReleaseMouseCapture();          this.Cursor = prevCursor;         prevCursor =          null;     } }

When in Zoom way, each mouse click will result in the zoom being increased on a left, or decreased on a correct click. The ApplyZoomCommand is described in the previous article.

Mouse Bicycle

Handling mouse wheel events is not straightforward like mouse clicks, and needs a fuller caption.

A mouse bike can exist rotated in either direction, and it contains notches restricting small changes in rotation, to about 15 degrees per notch. The problem is that there is no standard to the values produced by a mouse bike, it is all hardware specific. Unlike mice will provide different delta values for the aforementioned physical movement by the same user. So, the well-nigh accurate way to handle a mouse bike event is to write a hardware specific code, which is clearly not a viable choice. An culling is to treat all mouse wheel events every bit the same, all equalling one notch. This is not besides bad, as Windows fires mouse wheel events very speedily. Even so, if the user moves the wheel chop-chop, so only one event volition be generated for multiple notches.

The solution I came up with calibrates the value of a notch as it receives mouse wheel events, and is therefore not dependent on specific values. It has proved to be reliable in practice.

          private          int          minMouseDelta =          1000000;

An instance variable stores the everyman delta of the mouse; this is initially set to extremely loftier.

          void          process_MouseWheel(object          sender, MouseWheelEventArgs e) {     Point panelPoint = e.GetPosition(this);          double          delta =          0;          int          absDelta = Math.Abs(e.Delta);

The mouse wheel event contains an int delta value that indicates how much the bike has been rotated. The sign of the value indicates the direction of the rotation, simply the value is hardware specific. The starting time thing we do is to discard the sign, so we only need to work with positive values.

          if          ((minMouseDelta > absDelta) && (absDelta >          0))     minMouseDelta = absDelta;

The delta is compared with the minimum delta and so far observed, and if it is smaller, then the minimum value is updated. After several events, usually just one, we tin can then be certain of the smallest delta change made by the bicycle. This is treated as i notch.

          int          factor = absDelta / minMouseDelta;          if          (factor <          1)     factor =          1;

We can then calculate how many notches the current events delta contains. This is stored in the variable gene. How we use this data is then dependent on the selected wheel mode.

          switch          (WheelMode) {          case          eWheelMode.Rotate:         {             delta = RotateIncrement;          if          (eastward.Delta <=          0)                 delta *= -ane;             ApplyRotateCommand(delta, factor, panelPoint);         }          intermission;          case          eWheelMode.Zoom:         {             delta = (1          + (WheelZoomDelta /          100));          if          ((e.Delta <=          0) && (delta !=          0))                 delta =          1          / delta;             ApplyZoomCommand(delta, factor, panelPoint);         }          suspension;

The rotate and zoom command handling is described in full in the previous article.

          case          eWheelMode.Roll:             {                 delta = scrollDelta;          if          (east.Delta <=          0)                     delta *= -1;                 ApplyScrollCommand(delta, factor, panelPoint);             }          break;     } }

The curl command moves the content upward or down past the scrollDelta value. This is currently a private variable, but could benefit from beingness exposed as a dependency belongings.

          protected          void          ApplyScrollCommand(double          delta,          int          cistron, Point panelPoint) {          if          (gene >          0)     {          double          deltaY = delta * cistron;          if          (ApplyPanDelta(0, deltaY))             ApplyZoom(truthful);     } }

The corporeality the content is panned is calculated by multiplying the number of notches past the scrollDelta amount. This value is passed to the ApplyPanDelta method used by the drag and driblet functioning, described before.

Blitheness

One of the great things about WPF is that it is built on the aforementioned technology that powers video games, and makes full use of a machine's graphics card. This makes information technology extremely fast, and when zooming out the content of the ZoomBoxPanel, the visual effect is instantaneous. To make a smoother transition between zoom settings, it is possible to employ the animation back up built into WPF. Animation back up has been added for both scale/zoom and for rotation transformations. I tried adding animation for pan operations, but it was as well abrasive, then I turned it off.

The ApplyZoom method is called to apply the newly calculated values to the content. The method handles both animated and not-blithe changes.

          protected          void          ApplyZoom(bool          animate) {          if          ((!animate) || (!Animations))     {         translateTransform.BeginAnimation(TranslateTransform.XProperty,          nil);         translateTransform.BeginAnimation(TranslateTransform.YProperty,          null);          zoomTransform.BeginAnimation(ScaleTransform.ScaleXProperty,          null);         zoomTransform.BeginAnimation(ScaleTransform.ScaleYProperty,          null);          rotateTransform.BeginAnimation(RotateTransform.AngleProperty,          nix);

The kickoff half of the method sets the new values directly without whatsoever animation. It is necessary, notwithstanding, to phone call BeginAnimation with a null value beginning, in order to delete any existing animation from a previous performance. An animation volition over-ride a set value, even if the value is prepare after the blitheness.

          translateTransform.X = panX;     translateTransform.Y = panY;      zoomTransform.ScaleX = ZoomFactor;     zoomTransform.ScaleY = ZoomFactor;      rotateTransform.Bending = rotateAngle;     rotateTransform.CenterX = rotateCenterX;     rotateTransform.CenterY = rotateCenterY; }          else          {     DoubleAnimation XPropertyAnimation = MakeZoomAnimation(panX);     DoubleAnimation YPropertyAnimation = MakeZoomAnimation(panY);     DoubleAnimation ScaleXPropertyAnimation = MakeZoomAnimation(ZoomFactor);     DoubleAnimation ScaleYPropertyAnimation = MakeZoomAnimation(ZoomFactor);     DoubleAnimation AngleAnimation =         MakeRotateAnimation(rotateTransform.Angle, rotateAngle);

In that location are many different blitheness classes in WPF, simply we only need the most common 1, DoubleAnimation. We create five of these objects for each of the transformation properties that nosotros desire to change. How these are created is explained below.

          translateTransform.BeginAnimation(TranslateTransform.XProperty, XPropertyAnimation);     translateTransform.BeginAnimation(TranslateTransform.YProperty, YPropertyAnimation);      zoomTransform.BeginAnimation(ScaleTransform.ScaleXProperty, ScaleXPropertyAnimation);     zoomTransform.BeginAnimation(ScaleTransform.ScaleYProperty, ScaleYPropertyAnimation);      rotateTransform.CenterX = rotateCenterX;     rotateTransform.CenterY = rotateCenterY;     rotateTransform.BeginAnimation(RotateTransform.AngleProperty, AngleAnimation);   } }

Each animation is started asynchronously using the BeginAnimation method of the transform. This does the aforementioned equally assigning the new values straight equally shown in the not-animated version above.

          private          double          animationDuration =          300;          protected          DoubleAnimation MakeZoomAnimation(double          value) {     DoubleAnimation ani =          new          DoubleAnimation(value,          new          Elapsing(TimeSpan.FromMilliseconds(animationDuration)));     ani.FillBehavior = FillBehavior.HoldEnd;          return          ani; }

Creating animations for the zoom and interpret transforms is very straightforward. The DoubleAnimation constructor takes the terminal value and the fourth dimension that the animation should take. The elapsing is currently hard coded to 300 milliseconds, and this is something that could exist exposed as a dependency holding. It is important to set up the FillBehavior property; otherwise, the animation volition reset the property back to its original value after the animation is completed.

Making an animation for the rotation is more complicated because the DoubleAnimation will always move directly between the start and the end values. With rotation, we are dealing in angles, and there is a choice of which way to rotate. For example, if an object starts at 330° and ends at 30°, the best way is to rotate it clockwise by 60°. A DoubleAnimation will rotate it counterclockwise past 300°. This looks crazy to the user. An additional issue is that the speed of rotation should be constant. Thus, it should take six times longer to rotate ninety° than it should xv°.

          protected          DoubleAnimation MakeRotateAnimation(double          from,          double          to) {     DoubleAnimation ani =          new          DoubleAnimation();     ani.FillBehavior = FillBehavior.HoldEnd;     ani.To = to;     ani.From = calcStartFromAngle(from, to);     ani.Elapsing =          new          Duration(TimeSpan.FromMilliseconds(animationDuration *                                           calcAngleBetweenAngles(from, to) /          thirty));          return          ani; }

In the rotation animation creation, we specify the commencement position in the From belongings. It is not normally necessary to practise that, but this is a special case. The duration is calculated every bit the set up time taken to rotate 30° multiplied by the angle rotated.

          protected          double          calcStartFromAngle(double          from,          double          to) {          if          (from          < to)     {          if          (to -          from          >          180.0)          return          from          +          360.0;     }          else          {          if          (from          - to >          180.0)          return          from          -          360.0;     }          return          from; }

The trick to calculating the start angle is knowing that the rotate transform is happy to handle angles outside the 0°-360° range. Then, for the case higher up, the start position is 330°-360° = -xxx° to 30°. Which gives us a clockwise rotation of lx°.

          protected          double          calcAngleBetweenAngles(double          from,          double          to) {               while          (from          <          0)          from          +=          360;          while          (to <          0) to +=          360;          while          (from          >=          360)          from          -=          360;          while          (to >=          360) to -=          360;          double          a = (from          <= to) ? to -          from          :          from          - to;          if          (a >          180)         a =          360          - a;          return          a; }

In calculating the angle for the duration calculation, we need the absolute value, which this adding gives us.

Determination

This article covered 2 ways of adding to a custom control's user feel: mouse support and animations. As it stands now, ZoomBoxPanel is a useful and complete command that could be used in many dissimilar projects. The final article in this series will introduce an associated custom control that allows the user to manipulate the zoom level and zoom mode of this control.

History

  • 3rd Nov, 2009: Initial mail service
  • xxthursday Nov, 2009: Article updated

DOWNLOAD HERE


Mouse Move Rotate Animation Xaml Wpf Not Worked

Posted by: biermanwakepten.blogspot.com

0 Response to "Mouse Move Rotate Animation Xaml Wpf Not Worked"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel