C# 在 WPF 中向鼠标旋转图形(如模拟表盘)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/962974/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Rotate graphic towards mouse in WPF (like an analog dial)
提问by Adam Harte
In WPF/C# how would I rotate a "graphic" to face the current mouse position?
在 WPF/C# 中,如何旋转“图形”以面对当前鼠标位置?
Basically what I want is a "wheel" UI Control (like an analog volume dial). I want to be able to click and drag the dial and it will rotate to follow the mouse. Then when I release the mouse it will stop following (obviously!).
基本上我想要的是“滚轮”UI 控件(如模拟音量拨盘)。我希望能够点击并拖动表盘,它会跟随鼠标旋转。然后当我释放鼠标时,它会停止跟随(显然!)。
How would I create one of these? does one already exist somewhere?
我将如何创建其中之一?某处是否已经存在?
采纳答案by Nicholas Armstrong
I haven't seen any controls like this around (though it's been a while since I looked at all of the controls that WPF control vendors were offering), but it's relatively straightforward to create one.
我还没有看到任何这样的控件(尽管我已经有一段时间没有查看 WPF 控件供应商提供的所有控件了),但是创建一个相对简单。
All you'd have to do is create a custom control containing an Image (or XAML drawing) that you can rotate to follow the mouse. Then, bind a RotateTransform to an 'Angle' DependencyProperty on your custom control so that when 'angle' is updated, the image/drawing rotates to match:
您所要做的就是创建一个自定义控件,其中包含可以旋转以跟随鼠标移动的图像(或 XAML 绘图)。然后,将 RotateTransform 绑定到自定义控件上的 'Angle' DependencyProperty,以便在更新 'angle' 时,图像/绘图会旋转以匹配:
<UserControl x:Class="VolumeControlLibrary.VolumeControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:VolumeControlLibrary"
Height="60" Width="60">
<Image Source="/VolumeControl;component/knob.png" RenderTransformOrigin="0.5,0.5" >
<Image.RenderTransform>
<RotateTransform Angle="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:VolumeControl}}, Path=Angle}"/>
</Image.RenderTransform>
</Image>
</UserControl>
Setting RenderTransformOrigin to "0.5, 0.5" ensures that the control rotates around its center, rather than rotating around the top left corner; we'll have to compensate for this in the angle calculation too.
将 RenderTransformOrigin 设置为“0.5, 0.5”可确保控件围绕其中心旋转,而不是围绕左上角旋转;我们也必须在角度计算中对此进行补偿。
In the code behind file for your control, add handlers for the mouse and the Angle DependencyProperty:
在控件的代码隐藏文件中,为鼠标和 Angle DependencyProperty 添加处理程序:
public partial class VolumeControl : UserControl
{
// Using a DependencyProperty backing store for Angle.
public static readonly DependencyProperty AngleProperty =
DependencyProperty.Register("Angle", typeof(double), typeof(VolumeControl), new UIPropertyMetadata(0.0));
public double Angle
{
get { return (double)GetValue(AngleProperty); }
set { SetValue(AngleProperty, value); }
}
public VolumeControl()
{
InitializeComponent();
this.MouseLeftButtonDown += new MouseButtonEventHandler(OnMouseLeftButtonDown);
this.MouseUp += new MouseButtonEventHandler(OnMouseUp);
this.MouseMove += new MouseEventHandler(OnMouseMove);
}
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Mouse.Capture(this);
}
private void OnMouseUp(object sender, MouseButtonEventArgs e)
{
Mouse.Capture(null);
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (Mouse.Captured == this)
{
// Get the current mouse position relative to the volume control
Point currentLocation = Mouse.GetPosition(this);
// We want to rotate around the center of the knob, not the top corner
Point knobCenter = new Point(this.ActualHeight / 2, this.ActualWidth / 2);
// Calculate an angle
double radians = Math.Atan((currentLocation.Y - knobCenter.Y) /
(currentLocation.X - knobCenter.X));
this.Angle = radians * 180 / Math.PI;
// Apply a 180 degree shift when X is negative so that we can rotate
// all of the way around
if (currentLocation.X - knobCenter.X < 0)
{
this.Angle += 180;
}
}
}
}
Capturing the mouse ensures that your control will continue to get mouse updates even when the user mouses off of the control (until they let go of the click), and by getting the position of the mouse relative to the current element (the control), your calculation should always be the same regardless of where the control actually renders on screen.
捕获鼠标确保您的控件将继续获得鼠标更新,即使用户将鼠标从控件上移开(直到他们松开单击),并通过获取鼠标相对于当前元素(控件)的位置,无论控件在屏幕上实际呈现的位置如何,您的计算都应该始终相同。
In this example, when the mouse moves we calculate the angle between it and the center of the control, and then set this angle to the Angle DependencyProperty we created. Since the image we're displaying is bound to this angle property, WPF automatically applies the new value, which results in the knob rotating in combination with the mouse moving.
在这个例子中,当鼠标移动时,我们计算它与控件中心之间的角度,然后将这个角度设置为我们创建的 Angle DependencyProperty。由于我们显示的图像绑定到这个角度属性,WPF 会自动应用新值,这会导致旋钮随着鼠标移动而旋转。
Using the control in your solution is easy; just add:
在您的解决方案中使用控件很容易;只需添加:
<local:VolumeControl />
You would bind to the Angle property on VolumeControl if you wanted to bind the value of the knob to something in your application; that value is currently in degrees, but could add an additional property to convert between an angle in degrees and a value that makes sense to you (say, a value from 0 - 10).
如果要将旋钮的值绑定到应用程序中的某些内容,则可以绑定到 VolumeControl 上的 Angle 属性;该值目前以度为单位,但可以添加一个额外的属性来在以度为单位的角度和对您有意义的值(例如,0 - 10 之间的值)之间进行转换。
回答by Ron Warholic
To add to that post, the angle between the mouse point and the object point is calculated like:
要添加到该帖子,鼠标点和对象点之间的角度计算如下:
dot = currentLocation.X * objectPosition.X + currentLocation.Y * objectPosition.Y;
angle = Math.Acos(dot);
回答by troYman
In my case i have dynamically created shapes which shall rotated toward mouse direction. To solve this I used a lightweight function. All I need is following:
在我的情况下,我动态创建了应向鼠标方向旋转的形状。为了解决这个问题,我使用了一个轻量级的函数。我需要的只是以下内容:
- The centerpoint of the current selected shape
- The point from the last mouseover step
- And the point from the current mouseover step
- 当前选定形状的中心点
- 最后一个鼠标悬停步骤的点
- 以及当前鼠标悬停步骤的点
It is not necessary to use methods from the Math library. I calculate the angle which depends on the difference of the current mouse over point and the previous mouse over point and the position in relation o the center point. Finally I add the angle on the exisitng angle of the current object.
没有必要使用 Math 库中的方法。我计算的角度取决于当前鼠标悬停点与前一个悬停点悬停点的差异以及与中心点相关的位置。最后,我在当前对象的存在角度上添加角度。
private void HandleLeftMouseDown(MouseButtonEventArgs eventargs)
{
//Calculate the center point of selected object
//...
//assuming Point1 is the top left point
var xCenter = (_selectedObject.Point2.X - _selectedObject.Point1.X) / 2 + _selectedObject.Point1.X
var yCenter = (_selectedObject.Point2.Y - _selectedObject.Point1.Y) / 2 + _selectedObject.Point1.Y
_selectedObjectCenterPoint = new Point((double) xCenter, (double) yCenter);
//init set of last mouse over step with the mouse click point
var clickPoint = eventargs.GetPosition(source);
_lastMouseOverPoint = new Point(clickPoint.X,clickPoint.Y);
}
private void HandleMouseMove(MouseEventArgs eventArgs)
{
Point pointMouseOver = eventArgs.GetPosition(_source);
//Get the difference to the last mouse over point
var xd = pointMouseOver.X - _lastMouseOverPoint.X;
var yd = pointMouseOver.Y - _lastMouseOverPoint.Y;
// the direction depends on the current mouse over position in relation to the center point of the shape
if (pointMouseOver.X < _selectedObjectCenterPoint.X)
yd *= -1;
if (pointMouseOver.Y > _selectedObjectCenterPoint.Y)
xd *= -1;
//add to the existing Angle
//not necessary to calculate the degree measure
_selectedObject.Angle += (xd + yd);
//save mouse over point
_lastMouseOverPoint = new Point(pointMouseOver.X, pointMouseOver.Y);
}