如何创建带指定刻度的WPF拖拽点控件并获取坐标?
Hey there! Let's tackle your two main challenges: adding the custom scale markings and mapping the dot's position to your desired coordinate range. Here's a step-by-step breakdown:
1. Adding Horizontal (-180 to +180) and Vertical (-30 to +30) Scale Markings
You can add scale lines and labels either directly in XAML (for static, fixed-size setups) or generate them dynamically in code-behind (great if your Canvas size might change later). Let's cover both approaches:
Option 1: Static Scale in XAML
Update your Canvas with pre-defined ticks and labels:
<Grid Background="White" MouseUp="ParentOnMouseUp"> <Canvas x:Name="canvas" Background="Green" Width="200" Height="200" HorizontalAlignment="Center" VerticalAlignment="Center" MouseMove="CanvasOnMouseMove"> <!-- Draggable Dot --> <Ellipse x:Name="dot" Width="20" Height="20" Fill="Blue" Loaded="DotOnLoaded" MouseDown="DotOnMouseDown"/> <!-- Horizontal X-axis: -180 to +180 --> <Line X1="0" Y1="190" X2="200" Y2="190" Stroke="Black" StrokeThickness="2"/> <!-- Major Ticks & Labels --> <Line X1="0" Y1="185" X2="0" Y2="195" Stroke="Black" StrokeThickness="2"/> <TextBlock Text="-180" Canvas.Left="-15" Canvas.Top="195" Foreground="Black"/> <Line X1="50" Y1="185" X2="50" Y2="195" Stroke="Black" StrokeThickness="2"/> <TextBlock Text="-90" Canvas.Left="42" Canvas.Top="195" Foreground="Black"/> <Line X1="100" Y1="185" X2="100" Y2="195" Stroke="Black" StrokeThickness="2"/> <TextBlock Text="0" Canvas.Left="97" Canvas.Top="195" Foreground="Black"/> <Line X1="150" Y1="185" X2="150" Y2="195" Stroke="Black" StrokeThickness="2"/> <TextBlock Text="+90" Canvas.Left="142" Canvas.Top="195" Foreground="Black"/> <Line X1="200" Y1="185" X2="200" Y2="195" Stroke="Black" StrokeThickness="2"/> <TextBlock Text="+180" Canvas.Left="182" Canvas.Top="195" Foreground="Black"/> <!-- Vertical Y-axis: -30 to +30 --> <Line X1="10" Y1="0" X2="10" Y2="200" Stroke="Black" StrokeThickness="2"/> <!-- Major Ticks & Labels --> <Line X1="5" Y1="0" X2="15" Y2="0" Stroke="Black" StrokeThickness="2"/> <TextBlock Text="+30" Canvas.Left="20" Canvas.Top="-5" Foreground="Black"/> <Line X1="5" Y1="50" X2="15" Y2="50" Stroke="Black" StrokeThickness="2"/> <TextBlock Text="+15" Canvas.Left="20" Canvas.Top="45" Foreground="Black"/> <Line X1="5" Y1="100" X2="15" Y2="100" Stroke="Black" StrokeThickness="2"/> <TextBlock Text="0" Canvas.Left="20" Canvas.Top="95" Foreground="Black"/> <Line X1="5" Y1="150" X2="15" Y2="150" Stroke="Black" StrokeThickness="2"/> <TextBlock Text="-15" Canvas.Left="20" Canvas.Top="145" Foreground="Black"/> <Line X1="5" Y1="200" X2="15" Y2="200" Stroke="Black" StrokeThickness="2"/> <TextBlock Text="-30" Canvas.Left="20" Canvas.Top="195" Foreground="Black"/> </Canvas> </Grid>
Option 2: Dynamic Scale Generation (Code-Behind)
If you want the scale to adapt to Canvas size changes, add this method to your code-behind:
private void GenerateScaleMarkings() { // Horizontal Scale (-180 to +180) double horizontalTickStep = canvas.Width / 4; for (int i = 0; i <= 4; i++) { double xPos = i * horizontalTickStep; int labelVal = -180 + (i * 90); // Add tick line canvas.Children.Add(new Line { X1 = xPos, Y1 = canvas.Height - 10, X2 = xPos, Y2 = canvas.Height, Stroke = Brushes.Black, StrokeThickness = 2 }); // Add label var label = new TextBlock { Text = labelVal.ToString(), Foreground = Brushes.Black }; Canvas.SetLeft(label, xPos - label.ActualWidth / 2); Canvas.SetTop(label, canvas.Height + 5); canvas.Children.Add(label); } // Vertical Scale (-30 to +30) double verticalTickStep = canvas.Height / 4; for (int i = 0; i <= 4; i++) { double yPos = i * verticalTickStep; int labelVal = 30 - (i * 15); // Top = +30, Bottom = -30 // Add tick line canvas.Children.Add(new Line { X1 = 0, Y1 = yPos, X2 = 10, Y2 = yPos, Stroke = Brushes.Black, StrokeThickness = 2 }); // Add label var label = new TextBlock { Text = labelVal.ToString(), Foreground = Brushes.Black }; Canvas.SetLeft(label, 15); Canvas.SetTop(label, yPos - label.ActualHeight / 2); canvas.Children.Add(label); } }
Call this method in your DotOnLoaded event:
private void DotOnLoaded(object sender, RoutedEventArgs e) { CentreDot(); GenerateScaleMarkings(); }
2. Mapping Dot Position to Your Desired Coordinates
To convert the dot's Canvas position to your target range (-180/+180 horizontal, -30/+30 vertical), add this mapping method:
private (double X, double Y) GetDotCoordinates() { // Get the dot's center position on the Canvas double dotCenterX = (double)dot.GetValue(Canvas.LeftProperty) + dot.Width / 2; double dotCenterY = (double)dot.GetValue(Canvas.TopProperty) + dot.Height / 2; // Map X to -180 to +180 double scaledX = ((dotCenterX / canvas.Width) * 360) - 180; // Map Y to +30 to -30 (WPF Y increases downward, so we invert the range) double scaledY = 30 - ((dotCenterY / canvas.Height) * 60); // Round for readability (adjust precision as needed) return (Math.Round(scaledX, 2), Math.Round(scaledY, 2)); }
Use this method whenever you need the coordinates—for example, in the ParentOnMouseUp event to get the final position after dragging:
private void ParentOnMouseUp(object sender, MouseButtonEventArgs e) { _isDraggingDot = false; // Retrieve and use the coordinates var coords = GetDotCoordinates(); Debug.WriteLine($"Dot Coordinates: X = {coords.X}, Y = {coords.Y}"); // CentreDot(); }
Quick Note:
- WPF's Y-axis increases downward, so we flip the vertical mapping to make the top of the Canvas correspond to +30 and the bottom to -30.
内容的提问来源于stack exchange,提问作者Ayan

