2011-10-15 35 views

Trả lời

4

Tôi chưa thấy bất kỳ kiểm soát như thế này, tôi đoán bạn sẽ phải mã nó cho mình. Có một vài điều ở đây để triển khai và tôi sẽ chỉ nói về phần biểu đồ. Trước tiên, bạn nên xác định danh sách kiểm tra về cách điều khiển này được cho là hành xử (tức là di chuyển các đường có con trỏ chỉ khi nút chuột xuống), sau khi xong ... tốt, đó là phần thú vị!

EDIT: OK, bây giờ đây là phiên bản thô và khi tôi nói thô, tôi muốn nói. Tôi đặt nó vào một cửa sổ thay vì điều khiển người dùng, bạn có thể chỉ cần sao chép dán nó vào kiểm soát của bạn. Điều này có nhiều sai sót và chỉ nên được sử dụng một cách hiệu quả sau khi khắc phục mọi vấn đề xảy ra. Ngoài ra, bạn phải cẩn thận khi trộn thiết kế pixel với thiết kế linh hoạt/tương đối như Stretch-Alignment. Tôi đã hạn chế độ chính xác của pixel này bằng cách làm cho cửa sổ không thể thay đổi kích thước.

<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> 

code-behind trông giống như sau:

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

bạn có thể có một cái nhìn tại các thư viện biểu đồ DynamicDataDisplay. Đó là một thư viện được tạo ra như một dự án nghiên cứu của Microsoft (afaik) và có thể cung cấp chức năng mà bạn đang tìm kiếm.

Thứ nhất, tham khảo các dll DynamicDataDisplay trong dự án của bạn và sau đó tạo ra các không gian tên sau trong XAML của bạn:

xmlns:d3="http://research.microsoft.com/DynamicDataDisplay/1.0" 

Sau đó, bạn có thể thêm một đối tượng ChartPlotter để XAML và tước tất cả mọi thứ bạn không cần phải từ nó (trục, chú thích, ...). Bạn có thể sử dụng CursorCoordinateGraph để theo dõi con chuột. Nếu bạn muốn thay đổi bố cục, bạn có thể sử dụng đối tượng 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> 

Nếu bạn muốn theo dõi vị trí của chuột, bạn có thể sử dụng code-behind:

Point current = cursorGraph.Position; 

hoặc ràng buộc Position tài sản để viewmodel của bạn:

<d3:CursorCoordinateGraph x:Name="cursorGraph" Position="{Binding CurrentMousePosition}"/> 

Nếu bạn thực sự muốn sửa vị trí khi bạn nhấp vào, tôi đoán bạn sẽ phải tạo một CursorCoordinateGraph mới trong trình xử lý sự kiện OnClick hoặc MouseClick cho số ChartPlotter và c tính toán Điểm và cung cấp cho Biểu đồ mới:

//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); 
} 

Bạn có thể có một số công việc để làm cho nó trông giống như bạn muốn. Tôi đề nghị bạn duyệt qua các mẫu được cung cấp trên trang codeplex và xem trang thảo luận. Thư viện này là rất lớn và có rất nhiều khả năng, nhưng cung cấp ít tài liệu thực tế mặc dù ...

Hy vọng điều này giúp bạn đi đúng hướng!

1

Tôi đã tạo bản trình diễn về điều khiển ControllerCanvas có thể tái sử dụng đơn giản của mình. Hy vọng nó sẽ giúp bạn. Bạn có thể đặt tất cả các thuộc tính trong XAML của mình.

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(); 
     } 
    } 
} 
Các vấn đề liên quan