2012-03-30 18 views
4

私はそれをどのように行うことができるのかという基本的な考え方を持っていますが、私は実際にコードを知らないのです。 Visual StudioでWPFアプリケーションを使用したいと思います。ユーザーが「描画」ボタンをクリックすると、キャンバス上に(ポリラインを使用して)形状(スピログラフ)が描画されますが、捻じれがあると、一度に1行ずつポイントごとに描画する必要があります。この「アニメーション」を参照してください。また、キャンバスに描かれている間に、図面をキャンセル/停止することができます。まず、リストやポイントの配列を生成する必要があります(配列にもっと精通しています)。キャンバス上でゆっくりとシェイプを描くことによって、「進行状況を報告する」バックグラウンドワーカーにポイントを渡す必要があります。ここにスピログラフを描画するためのコードはありますが、実際の形状は大丈夫です。どのようにC#でポイントごとに図形を描くのですか?

<Canvas Name="Canvas" MouseLeftButtonUp="Canvas_MouseLeftButtonUp" MouseRightButtonUp="Canvas_MouseRightButtonUp"> 
    <!-- you can customize your polyline thickness/color/etc here --> 
    <Polyline x:Name="Poly" Stroke="Black" StrokeThickness="1" /> 
</Canvas> 

次に、アプリケーションをマルチスレッドする必要があります:あなたのキャンバスを設定

public void DrawSpiroGraph() 
{ 
    for (inti = 0; i<= numPoints; i++) 
    { 
     pt = newPoint(); 
     pt.X = x0 + r * Math.Cos(a); 
     pt.Y = y0 + r * Math.Sin(a); 
     double rr = 0.5 * r; 
     double aa = -0.8 * a; 
     Point pnt = newPoint(); 
     pnt.X = pt.X + rr * Math.Cos(aa); 
     pnt.Y = pt.Y + rr * Math.Sin(aa); 
     a += 0.5; 
     pline.Points.Add(pnt); 
    } 
} 

答えて

5

まず、。 WPFでのマルチスレッドは、別のスレッドから描画コンテキストにアクセスすることができないため、厄介なビジネスです。幸いなことに、ProgressChangedイベントが同じスレッドで実行されるので、BackgroundWorkerクラスはここで頭痛を軽減できます。だから、キャンバス上のユーザーがクリックしたとき:

private BackgroundWorker _animationWorker; 

private void Canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { 
    var p = e.GetPosition(Canvas); 
    Poly.Points.Add(p); 

    _animationWorker = new BackgroundWorker { 
    WorkerReportsProgress = true, 
    WorkerSupportsCancellation = true}; 
    _animationWorker.ProgressChanged += AnimationWorkerOnProgressChanged; 
    _animationWorker.DoWork += AnimationWorkerOnDoWork; 
    _animationWorker.RunWorkerAsync(p); 
} 

は、今、私たちは、バックグラウンドワーカーを設定したことを、私たちはDoWorkデリゲートの内側に重い物を持ち上げるのほとんどの操作を行います。

private void AnimationWorkerOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs) { 
    var p = (Point) doWorkEventArgs.Argument; 

    const int numPoints = 1000; 
    var r = 100; 
    var a = 0.0; 

    var pc = new PointCollection(); 
    for(var i = 0; i <= numPoints; i++) { 
    var pt = new Point(); 
    pt.X = p.X + r * Math.Cos(a); 
    pt.Y = p.Y + r * Math.Sin(a); 
    double rr = 0.5 * r; 
    double aa = -0.8 * a; 
    Point pnt = new Point(); 
    pnt.X = pt.X + rr * Math.Cos(aa); 
    pnt.Y = pt.Y + rr * Math.Sin(aa); 
    a += 0.5; 
    _animationWorker.ReportProgress(0, pnt); 
    Thread.Sleep(10); 
    if(_animationWorker.CancellationPending) break; 
    } 
} 

注どのように我々 ReportProgressメソッドを使用してポイントを渡します。これは、実行中のスレッドにアクセスするために私たちを有効にして、私たちのポリラインに追加します。

private void AnimationWorkerOnProgressChanged(object sender, ProgressChangedEventArgs progressChangedEventArgs) { 
    var p = (Point) progressChangedEventArgs.UserState; 
    Poly.Points.Add(p); 
} 

は今のまま唯一のことは、アニメーションを停止サポートすることです。私はこれを右クリック(左クリックで描画、右クリックで停止/クリア)しました。もちろん、この機能にどのようなコントロールを添付してもかまいません。ここでは、マウスの右ボタンハンドラです:

private void Canvas_MouseRightButtonUp(object sender, MouseButtonEventArgs e) { 
    if(_animationWorker != null) _animationWorker.CancelAsync(); 
    Poly.Points.Clear(); // you may wish to do this elsewhere so the partial animation stays on the screen 
} 
1

今日は少し退屈だったので、私はあなたのための簡単なユーザーコントロールを手早く。タイマーを使ってアニメーションを作成するだけです。 Delay/Radius/Pointは依存関係のプロパティを数えますので、(スライダなどの)ものでバインドすることができます。

ユーザーコントロールのXAML

<UserControl x:Class="WpfApplication1.Spirograph" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:app="clr-namespace:WpfApplication1"> 
<Canvas> 
    <Path Stroke="{Binding Stroke, RelativeSource={RelativeSource AncestorType=app:Spirograph}}" StrokeThickness="{Binding StrokeThickness, RelativeSource={RelativeSource AncestorType=app:Spirograph}}"> 
     <Path.Data> 
      <PathGeometry> 
       <PathGeometry.Figures> 
        <PathFigure x:Name="_figure" StartPoint="{Binding StartPoint, RelativeSource={RelativeSource AncestorType=app:Spirograph}}" /> 
       </PathGeometry.Figures> 
      </PathGeometry> 
     </Path.Data> 
    </Path> 
</Canvas> 

ユーザーコントロールのコードウィンドウの

using System; 
using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Media; 
using System.Windows.Threading; 

namespace WpfApplication1 
{ 
    public partial class Spirograph : UserControl 
    { 
     private DispatcherTimer _timer; 
     private int _pointIndex = 0; 
     private List<Point> _points; 
     private bool _loaded = false; 

     public Spirograph() 
     { 
      _points = new List<Point>(); 
      CalculatePoints(); 

      InitializeComponent(); 

      _timer = new DispatcherTimer(); 
      _timer.Interval = TimeSpan.FromMilliseconds(Delay); 
      _timer.Tick += TimerTick; 

      this.Loaded += SpirographLoaded; 
      this.SizeChanged += SpirographSizeChanged; 
     } 

     void SpirographSizeChanged(object sender, SizeChangedEventArgs e) 
     { 
      bool running = Running; 
      Reset(); 
      StartPoint = new Point((this.ActualWidth/2) - Radius, (this.ActualHeight/2) - Radius); 

      if (running) 
       Start(); 
     } 

     void SpirographLoaded(object sender, RoutedEventArgs e) 
     { 
      _loaded = true; 
      if (AutoStart) 
       Start(); 
     } 

     void TimerTick(object sender, EventArgs e) 
     { 
      if (_pointIndex >= PointCount) 
       Stop(); 
      else 
       _figure.Segments.Add(new LineSegment(_points[_pointIndex], true)); 

      _pointIndex++; 
     } 

     public bool Running { get; protected set; } 


     public bool AutoStart 
     { 
      get { return (bool)GetValue(AutoStartProperty); } 
      set { SetValue(AutoStartProperty, value); } 
     } 
     public static readonly DependencyProperty AutoStartProperty = DependencyProperty.Register("AutoStart", typeof(bool), typeof(Spirograph), new UIPropertyMetadata(true)); 

     public int PointCount 
     { 
      get { return (int)GetValue(PointCountProperty); } 
      set { SetValue(PointCountProperty, value); } 
     } 
     public static readonly DependencyProperty PointCountProperty = DependencyProperty.Register("PointCount", typeof(int), typeof(Spirograph), new UIPropertyMetadata(100, new PropertyChangedCallback(PointCountPropertyChanged))); 

     private static void PointCountPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) 
     { 
      Spirograph spirograph = sender as Spirograph; 
      if (spirograph != null) 
       spirograph.PointCountPropertyChanged(e); 
     } 
     private void PointCountPropertyChanged(DependencyPropertyChangedEventArgs e) 
     { 
      bool running = Running; 
      Reset(); 
      CalculatePoints(); 
      if (running) 
       Start(); 
     } 

     #region Delay 

     public int Delay 
     { 
      get { return (int)GetValue(DelayProperty); } 
      set { SetValue(DelayProperty, value); } 
     } 
     public static readonly DependencyProperty DelayProperty = DependencyProperty.Register("Delay", typeof(int), typeof(Spirograph), new UIPropertyMetadata(30, new PropertyChangedCallback(DelayPropertyChanged))); 

     private static void DelayPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) 
     { 
      Spirograph spirograph = sender as Spirograph; 
      if (spirograph != null) 
       spirograph.DelayPropertyChanged(e); 
     } 
     private void DelayPropertyChanged(DependencyPropertyChangedEventArgs e) 
     { 
      bool running = Running; 
      Stop(); 
      _timer.Interval = TimeSpan.FromMilliseconds((int)e.NewValue); 

      if (running) 
       Start(); 
     } 

     #endregion 

     public double Radius 
     { 
      get { return (double)GetValue(RadiusProperty); } 
      set { SetValue(RadiusProperty, value); } 
     } 
     public static readonly DependencyProperty RadiusProperty = DependencyProperty.Register("Radius", typeof(double), typeof(Spirograph), new UIPropertyMetadata(10.0)); 


     public Point StartPoint 
     { 
      get { return (Point)GetValue(StartPointProperty); } 
      set { SetValue(StartPointProperty, value); } 
     } 
     public static readonly DependencyProperty StartPointProperty = DependencyProperty.Register("StartPoint", typeof(Point), typeof(Spirograph), new UIPropertyMetadata(new Point(0, 0), new PropertyChangedCallback(StartPointPropertyChanged))); 

     private static void StartPointPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) 
     { 
      Spirograph spirograph = sender as Spirograph; 
      if (spirograph != null) 
       spirograph.StartPointPropertyChanged(e); 
     } 
     private void StartPointPropertyChanged(DependencyPropertyChangedEventArgs e) 
     { 
      bool running = Running; 
      Stop(); 
      StartPoint = (Point)e.NewValue; 
      CalculatePoints(); 
      if (running) 
       Start(); 
     } 

     public Brush Stroke 
     { 
      get { return (Brush)GetValue(StrokeProperty); } 
      set { SetValue(StrokeProperty, value); } 
     } 
     public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register("Stroke", typeof(Brush), typeof(Spirograph), new UIPropertyMetadata(new SolidColorBrush(Colors.Blue))); 

     public Thickness StrokeThickness 
     { 
      get { return (Thickness)GetValue(StrokeThicknessProperty); } 
      set { SetValue(StrokeThicknessProperty, value); } 
     } 
     public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register("StrokeThickness", typeof(Thickness), typeof(Spirograph), new UIPropertyMetadata(new Thickness(1))); 


     public void Start() 
     { 
      if (!_loaded) 
       AutoStart = true; 
      else 
      { 
       Running = true; 
       _timer.Start(); 
      } 
     } 

     public void Stop() 
     { 
      Running = false; 
      _timer.Stop(); 
     } 

     public void Reset() 
     { 
      Stop(); 
      _figure.Segments.Clear(); 
      _pointIndex = 0; 
     } 

     private void CalculatePoints() 
     { 
      _points.Clear(); 
      Point lastPoint = StartPoint; 
      double a = 0.0; 

      double rr = 0.5 * Radius; 


      for (int i = 0; i <= PointCount; i++) 
      { 
       Point pt = new Point(); 
       pt.X = lastPoint.X + Radius * Math.Cos(a); 
       pt.Y = lastPoint.Y + Radius * Math.Sin(a); 
       _points.Add(pt); 
       double aa = -0.8 * a; 
       Point pnt = new Point(); 
       pnt.X = pt.X + rr * Math.Cos(aa); 
       pnt.Y = pt.Y + rr * Math.Sin(aa); 
       a += 0.5; 
       _points.Add(pnt); 
       lastPoint = pnt; 
      } 
     } 
    } 
} 

XAMLは、コントロールをホスト

<Window x:Class="WpfApplication1.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:app="clr-namespace:WpfApplication1" 
    Title="MainWindow" Height="350" Width="525" 
    x:Name="MainWindowX"> 
<Grid> 
    <Grid.ColumnDefinitions> 
     <ColumnDefinition Width="100" /> 
     <ColumnDefinition Width="*" /> 
    </Grid.ColumnDefinitions> 
    <StackPanel> 
     <TextBlock Text="Points:" Margin="5" /> 
     <Slider x:Name="PointSlider" Orientation="Horizontal" Minimum="10" Maximum="10000" Value="1000" /> 
     <Button Content="Start" Height="24" Margin="5" Click="StartClick" /> 
     <Button Content="Stop" Height="24" Margin="5" Click="StopClick" /> 
     <Button Content="Reset" Height="24" Margin="5" Click="ResetClick" /> 
     <TextBlock Text="Delay:" Margin="5" /> 
     <Slider x:Name="Slider" Orientation="Horizontal" Minimum="1" Maximum="500" Value="100" Height="50" /> 
    </StackPanel> 
    <app:Spirograph x:Name="Spirograph" Grid.Column="1" PointCount="{Binding Value, ElementName=PointSlider}" Radius="50" AutoStart="False" Delay="{Binding Path=Value, ElementName=Slider}" /> 
</Grid> 

関連する問題