Control Function Analysis
The circular control can recognize the clockwise and counterclockwise sliding gestures and recognize the sliding speed.
Related events provided by the system
Onmanipulationstarted |
Slide start |
Hand Press |
Onmanipulationdelta |
Moving |
Hold and move |
Onmanipulationcompleted |
Slide complete |
Release |
These three events are necessary events to achieve slide, because eventargs provides the mobile XY coordinates that have been moved at a speed,
However, it is a pity that you need to implement clockwise recognition.
Recognize clockwise sliding
It recognizes clockwise and vice versa, but how can it recognize clockwise sliding,
In fact, this problem has plagued me a lot of time. First, let's look at the default axis structure:
The red brush shows a gesture. It seems that there is no breakthrough. There is no reference for the change of X and Y.
You have to change your mind and move the coordinate axis to the center of the circle without changing the direction (Y is slightly different from mathematics)
Similarly, the red brush is used to represent the gesture. The changes of light from X and Y seem to have no reference, but there are some rules in different ranges.
Let's take a look at the variation of X and Y sliding clockwise.
Interval 1 |
X increase |
Y decrease |
Interval 2 |
X increase |
Y increase |
Interval 3 |
X decrease |
Y decrease |
Interval 4 |
X decrease |
Y increase |
Well, we can basically identify the direction, but it is a bit difficult to write. Is there a better way.
At this time, I lost the geometric structure for N years and suddenly recovered it. I have been entangled in X and Y. Isn't there a concept of angle,
Math, which has been on the bench for a long time, has an atan function in it to find the inverse trigonometric function.
The angle formula is as follows: var angle = math. atan (x/y) * 180.0/Math. Pi;
Next, we can determine the direction of the angle through the interval. These problems are solved. Suddenly I felt the importance of mathematics.
Of course, you can refer to the code implementation for some details.
Control Code Implementation
XAML:
View code
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:VirtualKeyboard.Controls"
mc:Ignorable="d"
x:Class="VirtualKeyboard.Controls.LoopControl"
d:DesignWidth="340" d:DesignHeight="340">
<Canvas>
<Ellipse x:Name="bigEllipse" Height="340" Width="340" Canvas.Left="0" Canvas.Top="0" StrokeThickness="4" Stroke="White" Fill="#FF7A7A7A" />
<local:RoundButton x:Name="centerButton" Height="150" Width="150" Canvas.Left="95" Canvas.Top="95" ImageSource="/VirtualKeyboard.Controls;component/Icons/word.ok.png" Background="#FF1D1D1D"/>
</Canvas>
</UserControl>
To facilitate the addition of a circular OK button in the middle, the implementation of this button will be implemented in the following sections. If you are interested in the exercises, you can directly remove the local: roundbutton
C #
View code
Public partial class loopcontrol
{
# Region const
Constdouble bigradius = 200.0;
Const double smailradius = 70.0;
Private readonly timespan deltaspan = new timespan (0, 0, 0, 0,200 );
# Endregion
# Region private
Private datetime starttime;
Private angle startangle;
# Endregion
Public event routedeventhandler centerclick
{
Add {centerbutton. Click + = value ;}
Remove {centerbutton. Click-= value ;}
}
Public event eventhandler <loopglideeventarg> loopglide;
Public void onloopglide (loopglideeventarg E)
{
Eventhandler <loopglideeventarg> handler = loopglide;
If (handler! = NULL) handler (this, e );
}
Public loopcontrol ()
{
// Required for variable Initialization
Initializecomponent ();
}
Protected override void onmanipulationstarted (manipulationstartedeventargs E)
{
Starttime = datetime. now;
Startangle = toangle (E. manipulationorigin );
Base. onmanipulationstarted (E );
}
Protected override void onmanipulationdelta (manipulationdeltaeventargs E)
{
If (isincenter (E. manipulationorigin) return;
VaR now = datetime. now;
If (now-starttime> deltaspan)
{
Getdirection (E. manipulationorigin );
Starttime = now;
Startangle = toangle (E. manipulationorigin );
}
Base. onmanipulationdelta (E );
}
Private void getdirection (point)
{
VaR angle = toangle (point );
VaR angledistance = startangle-angle;
Onloopglide (New loopglideeventarg (angledistance. Distance ));
}
/// <Summary>
/// Angle
/// </Summary>
/// <Param name = "point"> </param>
/// <Returns> </returns>
Private angle toangle (point)
{
VaR xpoint = toxpoint (point );
VaR x = xpoint. X;
Var y = xpoint. Y;
VaR angle = math. atan (x/y) * 180.0/Math. Pi;
If (! (Y <0.0 ))
{
Angle + = 180.0;
}
Else if (X-> 0.0)
{
Angle + = 360.0;
}
Return new angle (angle );
}
/// <Summary>
/// Changed coordinates
/// </Summary>
/// <Param name = "point"> </param>
/// <Returns> </returns>
Private point toxpoint (point)
{
VaR x = point. X-bigradius;
Var y = point. Y-bigradius;
Return new point (x, y );
}
Private bool isincenter (point)
{
VaR xpoint = toxpoint (point );
VaR radius = math. SQRT (xpoint. x * xpoint. x + xpoint. y * xpoint. y );
Return radius <smailradius;
}
# Region private struct
/// <Summary>
/// Angle
/// </Summary>
Private struct angle: iequatable <angle>
{
Private readonly double value;
Private const double Epsilon = 0.1;
Public double value
{
Get {return value ;}
}
Public angle (double value)
{
If (value> 360.0 | value <0)
{
Throw new argumentoutofrangeexception ("value", "value must between 0 and 360 ");
}
This. value = value;
}
Public override string tostring ()
{
Return string. Format ("{0: N2}", value );
}
Public static angledistance operator-(angle angle1, angle angle2)
{
Double angledistance = angle1.value-angle2.value;
If (angledistance> 180.0)
{
Angledistance-= 360;
}
Else if (angledistance <-180.0)
{
Angledistance ++ = 360;
}
Return new angledistance (angledistance );
}
Public override bool equals (Object OBJ)
{
If (obj is angle)
{
Return equals (angle) OBJ );
}
Return false;
}
Public override int gethashcode ()
{
Return 360 ^ value. gethashcode ();
}
Public bool equals (angle other)
{
Return math. Abs (value-other. Value) <Epsilon;
}
}
/// <Summary>
/// Angle
/// </Summary>
Private struct angledistance
{
Private readonly double angledistance;
Public angledistance (double angledistance)
{
This. angledistance = angledistance;
}
Public double distance
{
Get {return angledistance ;}
}
Public override string tostring ()
{
Return string. Format ("{0: N2}", angledistance );
}
/// <Summary>
/// Clockwise
/// </Summary>
Public bool clockwise
{
Get {return angledistance> 0 ;}
}
}
# Endregion
}