我正在寻找这样的东西。
我应该能够用鼠标在XY图形内部拖动坐标。坐标的位置决定了X和Y值。
我可以重复使用随时可用的控件吗?如果没有,我该如何写一个?
答案 0 :(得分:4)
我没有看到这样的任何控制,我想你必须自己编码。 这里有一些要实现的东西,我将仅讨论图形部分。 首先,你应该定义一个关于这个控件应该如何表现的清单(即只有当鼠标按钮关闭时才用光标移动线条),之后就完成了......好吧,那就是有趣的部分!
编辑:好的,现在这是一个粗略的版本,当我说粗糙时,我的意思是。我把它放在一个窗口而不是用户控件中,你可以将它粘贴到你的控件中。这有很多缺陷,只有在解决了所有出现的问题后才能有效地使用。另外,在将像素设计与弹性对齐等灵活/相对设计混合时必须要小心。我通过使窗口不可调整大小来限制它的像素精度。
<Window x:Class="graphedit.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
x:Name="window"
MouseMove="Window_MouseMove"
Height="400" Width="400"
ResizeMode="NoResize">
<Canvas x:Name="canvas"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Canvas.Background>
<RadialGradientBrush>
<GradientStop Color="#333333" Offset="1"></GradientStop>
<GradientStop Color="#666666" Offset="0"></GradientStop>
</RadialGradientBrush>
</Canvas.Background>
<Border BorderThickness="0,0,1,1"
BorderBrush="White"
Margin="0,0,0,0"
Width="{Binding Path=Point.X}"
Height="{Binding Path=Point.Y}"></Border>
<Border BorderThickness="1,1,0,0"
BorderBrush="White"
Margin="{Binding Path=BottomRightBoxMargin}"
Width="{Binding Path=BottomRightBoxDimensions.X}"
Height="{Binding Path=BottomRightBoxDimensions.Y}"></Border>
<Border BorderThickness="1"
BorderBrush="White"
Margin="{Binding Path=GripperMargin}"
Background="DimGray"
CornerRadius="4"
Width="10"
x:Name="gripper"
MouseDown="gripper_MouseDown"
MouseUp="gripper_MouseUp"
Height="10"></Border>
<TextBox Text="{Binding Path=Point.X}" Canvas.Left="174" Canvas.Top="333" Width="42"></TextBox>
<TextBox Text="{Binding Path=Point.Y}" Canvas.Left="232" Canvas.Top="333" Width="45"></TextBox>
<TextBlock Foreground="White" Canvas.Left="162" Canvas.Top="336">X</TextBlock>
<TextBlock Canvas.Left="222" Canvas.Top="336" Foreground="White" Width="13">Y</TextBlock>
</Canvas>
代码隐藏如下:
using System.ComponentModel;
using System.Windows;
namespace graphedit
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
private bool isDragging;
private Point mousePositionBeforeMove;
private Point point;
public Point Point
{
get { return this.point; }
set
{
this.point = value;
this.InvokePropertyChanged(null);
}
}
public Thickness BottomRightBoxMargin
{
get
{
Thickness t = new Thickness()
{
Left = this.Point.X,
Top = this.Point.Y
};
return t;
}
}
public Thickness GripperMargin
{
get
{
Thickness t = new Thickness()
{
Left = this.Point.X - 5,
Top = this.Point.Y - 5
};
return t;
}
}
public Point BottomRightBoxDimensions
{
get
{
return new Point(this.Width - this.Point.X,
this.Height - this.Point.Y);
}
}
public MainWindow()
{
InitializeComponent();
this.Point = new Point(100, 80);
this.DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public void InvokePropertyChanged(string name)
{
PropertyChangedEventArgs args = new PropertyChangedEventArgs(name);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, args);
}
}
private void gripper_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
this.isDragging = true;
this.mousePositionBeforeMove = e.GetPosition( this.canvas );
}
private void gripper_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
this.isDragging = false;
}
private void Window_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
if(this.isDragging)
{
Point currentMousePosition = e.GetPosition( this.canvas );
double deltaX = currentMousePosition.X - this.mousePositionBeforeMove.X;
double deltaY = currentMousePosition.Y - this.mousePositionBeforeMove.Y;
double newPointX = (this.Point.X + deltaX < 0 ? 0 : (this.Point.X + deltaX > this.Width ? this.Width : this.Point.X + deltaX)) ;
double newPointY = (this.Point.Y + deltaY < 0 ? 0 : (this.Point.Y + deltaY > this.Width ? this.Width : this.Point.Y + deltaY)) ;
this.Point = new Point(newPointX,newPointY);
this.mousePositionBeforeMove = currentMousePosition;
}
}
}
}
答案 1 :(得分:1)
您可以查看图表库DynamicDataDisplay。它是由Microsoft(afaik)作为研究项目创建的库,可能会提供您正在寻找的功能。
首先,在项目中引用DynamicDataDisplay dll,然后在xaml中创建以下命名空间:
xmlns:d3="http://research.microsoft.com/DynamicDataDisplay/1.0"
然后你可以在xaml中添加一个ChartPlotter
对象,并从中删除你不需要的所有东西(轴,图例......)。您可以使用CursorCoordinateGraph
跟踪鼠标。如果要更改布局等,可以使用VerticalRange
对象。
<d3:ChartPlotter Width="500" Height="300"
MainHorizontalAxisVisibility="Collapsed"
MainVerticalAxisVisibility="Collapsed"
LegendVisibility="Collapsed" NewLegendVisible="False">
<!--This allows you to track the mouse-->
<d3:CursorCoordinateGraph x:Name="cursorGraph"/>
<!-- this range does nothing more then make the background gray,
there are other ways to achieve this too-->
<d3:VerticalRange Value1="-300" Value2="300" Fill="Gray"/>
</d3:ChartPlotter>
如果要跟踪鼠标的位置,可以使用代码隐藏:
Point current = cursorGraph.Position;
或将Position
属性绑定到您的viewmodel:
<d3:CursorCoordinateGraph x:Name="cursorGraph" Position="{Binding CurrentMousePosition}"/>
如果您想在单击时实际修复位置,我猜您必须在CursorCoordinateGraph
的OnClick或MouseClick事件处理程序中创建新的ChartPlotter
并计算Point并提供它适用于新图表:
//pseudo code!!!
mainGraph.DoubleClick += HandleDoubleClick;
private void HandleDoubleClick(object sender, MouseButtonEventArgs e){
//get position from current graph:
var position = cursor.Position;
//you have to calculate the "real" position in the chart because
//the Point provided by Position is not the real point, but screen coordinates
//something like cursor.TranslatePoint(cursor.Position, mainGraph);
var newCoord = new CursorCoordinateGraph { Position = position };
mainGraph.Children.Add(newCoord);
}
您可能需要做一些工作才能让它看起来像您想要的那样。我建议您浏览codeplex页面上提供的示例,并查看讨论页面。这个库是巨大的,有很多可能性,但提供的实际文档很少......
希望这能指出你正确的方向!
答案 2 :(得分:1)
我已经演示了我的简单可重用ControllerCanvas控件。希望它会对你有所帮助。您可以在XAML中设置所有属性。
<强> ControllerCanvas.cs 强>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace XYControllerDemo
{
public class ControllerCanvas : Canvas
{
#region Dependency Properties
public Point Point
{
get { return (Point)GetValue(PointProperty); }
set
{
if (value.X < 0.0)
{
value.X = 0.0;
}
if (value.Y < 0.0)
{
value.Y = 0.0;
}
if (value.X > this.ActualWidth)
{
value.X = this.ActualWidth;
}
if (value.Y > this.ActualHeight)
{
value.Y = this.ActualHeight;
}
SetValue(PointProperty, value);
}
}
public static readonly DependencyProperty PointProperty =
DependencyProperty.Register("Point", typeof(Point), typeof(ControllerCanvas), new FrameworkPropertyMetadata(new Point(),
FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender));
public Brush ControllerStroke
{
get { return (Brush)GetValue(ControllerStrokeProperty); }
set { SetValue(ControllerStrokeProperty, value); }
}
public static readonly DependencyProperty ControllerStrokeProperty =
DependencyProperty.Register("ControllerStroke", typeof(Brush), typeof(ControllerCanvas), new FrameworkPropertyMetadata(Brushes.Red,
FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender));
public double ControllerStrokeThickness
{
get { return (double)GetValue(ControllerStrokeThicknessProperty); }
set { SetValue(ControllerStrokeThicknessProperty, value); }
}
public static readonly DependencyProperty ControllerStrokeThicknessProperty =
DependencyProperty.Register("ControllerStrokeThickness", typeof(double), typeof(ControllerCanvas), new FrameworkPropertyMetadata(1.0,
FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender));
public Brush GridStroke
{
get { return (Brush)GetValue(GridStrokeProperty); }
set { SetValue(GridStrokeProperty, value); }
}
public static readonly DependencyProperty GridStrokeProperty =
DependencyProperty.Register("GridStroke", typeof(Brush), typeof(ControllerCanvas), new FrameworkPropertyMetadata(Brushes.LightGray,
FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender));
public double GridStrokeThickness
{
get { return (double)GetValue(GridStrokeThicknessProperty); }
set { SetValue(GridStrokeThicknessProperty, value); }
}
public static readonly DependencyProperty GridStrokeThicknessProperty =
DependencyProperty.Register("GridStrokeThickness", typeof(double), typeof(ControllerCanvas), new FrameworkPropertyMetadata(1.0,
FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender));
public bool GridVisible
{
get { return (bool)GetValue(GridVisibleProperty); }
set { SetValue(GridVisibleProperty, value); }
}
public static readonly DependencyProperty GridVisibleProperty =
DependencyProperty.Register("GridVisible", typeof(bool), typeof(ControllerCanvas), new FrameworkPropertyMetadata(false,
FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender));
public Thickness GridMargin
{
get { return (Thickness)GetValue(GridMarginProperty); }
set { SetValue(GridMarginProperty, value); }
}
public static readonly DependencyProperty GridMarginProperty =
DependencyProperty.Register("GridMargin", typeof(Thickness), typeof(ControllerCanvas), new FrameworkPropertyMetadata(new Thickness(0.0, 0.0, 0.0, 0.0),
FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender));
public double GridSize
{
get { return (double)GetValue(GridSizeProperty); }
set { SetValue(GridSizeProperty, value); }
}
public static readonly DependencyProperty GridSizeProperty =
DependencyProperty.Register("GridSize", typeof(double), typeof(ControllerCanvas), new FrameworkPropertyMetadata(30.0,
FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender));
#endregion
#region Drawing Context
Pen penGrid = null;
Pen penController = null;
Brush previousGridStroke = null;
double previousGridStrokeThickness = double.NaN;
double penGridHalfThickness = double.NaN;
Brush previousControllerStroke = null;
double previousControllerStrokeThickness = double.NaN;
double penControllerHalfThickness = double.NaN;
Point p1 = new Point();
Point p2 = new Point();
Point p3 = new Point();
Point p4 = new Point();
double width = double.NaN;
double height = double.NaN;
void DrawGrid(DrawingContext dc)
{
width = this.ActualWidth;
height = this.ActualHeight;
// draw vertical grid lines
for (double y = GridMargin.Top; y <= height - GridMargin.Bottom; y += GridSize)
{
p1.X = GridMargin.Left;
p1.Y = y;
p2.X = width - GridMargin.Right;
p2.Y = y;
GuidelineSet g = new GuidelineSet();
g.GuidelinesX.Add(p1.X + penGridHalfThickness);
g.GuidelinesX.Add(p2.X + penGridHalfThickness);
g.GuidelinesY.Add(p1.Y + penGridHalfThickness);
g.GuidelinesY.Add(p2.Y + penGridHalfThickness);
dc.PushGuidelineSet(g);
dc.DrawLine(penGrid, p1, p2);
dc.Pop();
}
// draw horizontal grid lines
for (double x = GridMargin.Left; x <= width - GridMargin.Right; x += GridSize)
{
p1.X = x;
p1.Y = GridMargin.Top;
p2.X = x;
p2.Y = height - GridMargin.Bottom;
GuidelineSet g = new GuidelineSet();
g.GuidelinesX.Add(p1.X + penGridHalfThickness);
g.GuidelinesX.Add(p2.X + penGridHalfThickness);
g.GuidelinesY.Add(p1.Y + penGridHalfThickness);
g.GuidelinesY.Add(p2.Y + penGridHalfThickness);
dc.PushGuidelineSet(g);
dc.DrawLine(penGrid, p1, p2);
dc.Pop();
}
}
void DrawController(DrawingContext dc)
{
width = this.ActualWidth;
height = this.ActualHeight;
// draw vertical controller line
p1.X = 0.0;
p1.Y = Point.Y;
p2.X = width;
p2.Y = Point.Y;
GuidelineSet g1 = new GuidelineSet();
g1.GuidelinesX.Add(p1.X + penControllerHalfThickness);
g1.GuidelinesX.Add(p2.X + penControllerHalfThickness);
g1.GuidelinesY.Add(p1.Y + penControllerHalfThickness);
g1.GuidelinesY.Add(p2.Y + penControllerHalfThickness);
dc.PushGuidelineSet(g1);
dc.DrawLine(penController, p1, p2);
dc.Pop();
// draw horizontal controller line
p3.X = Point.X;
p3.Y = 0.0;
p4.X = Point.X;
p4.Y = height;
GuidelineSet g2 = new GuidelineSet();
g2.GuidelinesX.Add(p3.X + penControllerHalfThickness);
g2.GuidelinesX.Add(p4.X + penControllerHalfThickness);
g2.GuidelinesY.Add(p3.Y + penControllerHalfThickness);
g2.GuidelinesY.Add(p4.Y + penControllerHalfThickness);
dc.PushGuidelineSet(g2);
dc.DrawLine(penController, p3, p4);
dc.Pop();
}
protected override void OnRender(DrawingContext dc)
{
base.OnRender(dc);
// create ord update grid pen
if (penGrid == null)
{
penGrid = new Pen(GridStroke, GridStrokeThickness);
previousGridStroke = GridStroke;
previousGridStrokeThickness = GridStrokeThickness;
penGridHalfThickness = penGrid.Thickness / 2.0;
}
else
{
if (GridStroke != previousGridStroke || GridStrokeThickness != previousGridStrokeThickness)
{
previousGridStroke = GridStroke;
previousGridStrokeThickness = GridStrokeThickness;
penGrid.Brush = GridStroke;
penGrid.Thickness = GridStrokeThickness;
penGridHalfThickness = penGrid.Thickness / 2.0;
}
}
// create ord update controller pen
if (penController == null)
{
penController = new Pen(ControllerStroke, ControllerStrokeThickness);
previousControllerStroke = ControllerStroke;
previousControllerStrokeThickness = ControllerStrokeThickness;
penControllerHalfThickness = penController.Thickness / 2.0;
}
else
{
if (ControllerStroke != previousControllerStroke || ControllerStrokeThickness != previousControllerStrokeThickness)
{
previousControllerStroke = ControllerStroke;
previousControllerStrokeThickness = ControllerStrokeThickness;
penController.Brush = ControllerStroke;
penController.Thickness = ControllerStrokeThickness;
penControllerHalfThickness = penController.Thickness / 2.0;
}
}
// drag grid
if (GridVisible)
{
DrawGrid(dc);
}
// draw controller
DrawController(dc);
}
#endregion
#region Mouse Events
protected override void OnMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs e)
{
if (!this.IsMouseCaptured)
{
this.Point = e.GetPosition(this);
this.Cursor = Cursors.Hand;
this.CaptureMouse();
}
base.OnMouseLeftButtonDown(e);
}
protected override void OnMouseLeftButtonUp(System.Windows.Input.MouseButtonEventArgs e)
{
if (this.IsMouseCaptured)
{
this.Cursor = Cursors.Arrow;
this.ReleaseMouseCapture();
}
base.OnMouseLeftButtonUp(e);
}
protected override void OnMouseMove(System.Windows.Input.MouseEventArgs e)
{
if (this.IsMouseCaptured)
{
this.Point = e.GetPosition(this);
}
base.OnMouseMove(e);
}
#endregion
}
}
<强> MainWindow.xaml 强>
<Window x:Class="XYControllerDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:XYControllerDemo"
Title="XYControllerDemo" Height="410" Width="680">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*"/>
<ColumnDefinition Width="50*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<local:ControllerCanvas x:Name="controller1"
Margin="10" Grid.Column="0" Grid.Row="0"
Background="Transparent" Width="300" Height="300"
GridMargin="0,0,0,0" GridVisible="True" GridSize="30"
GridStroke="LightGray" GridStrokeThickness="1.0"
ControllerStroke="Red" ControllerStrokeThickness="1.0"
Point="50,50"/>
<TextBox Grid.Row="1" Grid.Column="0" Margin="10" Width="100"
Text="{Binding ElementName=controller1, Path=Point, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<local:ControllerCanvas x:Name="controller2"
Margin="10" Grid.Column="1" Grid.Row="0"
Background="Transparent" Width="300" Height="300"
GridMargin="0,0,0,0" GridVisible="True" GridSize="30"
GridStroke="LightGray" GridStrokeThickness="1.0"
ControllerStroke="Blue" ControllerStrokeThickness="1.0"
Point="90,250"/>
<TextBox Grid.Row="1" Grid.Column="1" Margin="10" Width="100"
Text="{Binding ElementName=controller2, Path=Point, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</Window>
<强> MainWindow.xaml.cs 强>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace XYControllerDemo
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}