2012-04-06 14 views
1

私はバイナリツリーの内容を視覚的に出力するプログラムに取り組んできました。このプログラムに含める最後の機能は、ツリーのポストオーダー、インオーダー、およびプリオーダーの構成のアニメーションです。Thread.Sleepを使用してバイナリツリーをアニメーション化する方法は?

これは、私が思ったよりもはるかに難しいことが分かっています。ここでは、元のドローでメソッドの:

private void DrawNode(int x, int y, BinaryTreeNode<T> node, int nodeLevel, int maxDepth, int connectX = -1, int connectY = -1,) 
    { 
     //calculate distance between the node's children 
     int distance = CalculateDistance(nodeLevel, maxDepth); 

     //draw the node at the specified coordinate 
     node.Draw(x, y, this.device); 

     if (node.Left != null) 
     { 
      DrawNode(x - distance/2, y + 50, node.Left, nodeLevel + 1, maxDepth, x, y, node); 
     } 
     if (node.Right != null) 
     { 
      DrawNode(x + distance/2, y + 50, node.Right, nodeLevel + 1, maxDepth, x, y, node); 
     } 

     //connect the node to its parent 
     if ((connectX != -1) && (connectY != -1)) 
     { 
      node.Connect(connectX, connectY, device); 
     } 

     this.display.Image = surface; 
    } 

私のオリジナルのアイデアは、句あれば、単純に最初の二つのそれぞれの内部でのThread.sleep(1000)を置くことだった - 私は本当に行うために必要なすべてが1のためのプログラムの実行を一時停止しましたノードの各描画の前に2番目に

スリープメソッドが描画コードの実行をブロックしていることに気がついたので、私はそのメソッドをあきらめました。次に、タイマーを使用しようとしましたが、ツリーを扱うときには難しくありませんでした。

私の目標は、単にGUIの応答性を中断することなく、かつ過度のコードを複雑にすることなく、プログラムの実行を一時停止する方法を見つけるために..です

任意の助けいただければ幸いです:)。

編集:潜在的に関連性のある情報:プログラムはWinformsで動作し、すべてのグラフィックスはGDI +で処理されます。あなたが他の情報が必要な場合は、ちょうど:)

編集を尋ねる:SLaksについて、

//draw the node's children 
     if (drawChildren) 
     { 
      if (node.Left != null) 
      { 
       if (this.timer2.Enabled) 
       { 
        this.timer2.Stop(); 
       } 
       if (!this.timer1.Enabled) 
       { 
        this.timer1.Start(); 
       } 
       this.count1++; 
       this.timer1.Tick += (object source, EventArgs e) => 
       { 
        this.count1--; 
        DrawNode(x - distance/2, y + 50, node.Left, nodeLevel + 1, maxDepth, x, y, node); 
        if (this.count1 == 0) 
        { 
         this.timer1.Stop(); 
        } 
       }; 
      } 
      else 
      { 
       this.timer1.Stop(); 
       this.timer2.Start(); 
      } 
      if (node.Right != null) 
      { 
       this.count2++; 
       this.timer2.Tick += (object source, EventArgs e) => 
       { 
        this.count2--; 
        DrawNode(x + distance/2, y + 50, node.Right, nodeLevel + 1, maxDepth, x, y, node); 
        if (this.count2 == 0) 
        { 
         this.timer2.Stop(); 
        } 
       }; 
      } 
     } 
+1

タイマーを使用する必要があります。何が問題になったのですか? – SLaks

+1

C#5で 'await Task.Delay(...)'を使用することもできます – SLaks

+0

アニメーションがポストオーダーである必要があるためタイマーが機能しませんでした。タイマーで動作させる方法が見つかりませんでした私は2つ持っていた)。あなたがそれを働かせる方法を見つけることができるなら、コードはあります: 編集:文字の制限はここにコードを置くのを防ぎます、私は元の投稿に入れます。 – Daniel

答えて

4

は、タイマーを使用して、更新のための適切な間隔を設定します。ここではそれを行うのいずれかの潜在的な方法です。 Tickイベントでは、それを描画して表示する次の手順を実行します。

+0

私は(私は編集でコードを投稿しました)、私は本当にバイナリツリーで動作するようにはできませんでした。左右のノード図面の両方を考慮して、それらがすべて正しく遅延されていることを確認する必要があります。 – Daniel

+1

それは動作し、あなたの状態を正しく維持する必要があります。 2つのタイマーは必要ありません。あなたが最後に描画したものと、タイマーが動くたびに描画するものを追跡するだけで済みます。 –

0

一つの可能​​な解決策は、その後、別のスレッドを生成定期的にDrawNode機能を呼び出すために、そのスレッドを使用することです間隔。この方法では、UIスレッドはブロックされません。

あなたが作成したスレッドはUIスレッドではないため、UIスレッドで明示的にDrawNode関数を呼び出す必要があります。

How to update the GUI from another thread in C#?

+0

タイマーはこのために構築されており、UIスレッド上で 'Tick'イベントが実行されるので、スレッド間の問題について心配する必要はありません(イメージの生成に要する時間が長すぎず、 UIスレッドはOKです)。 –

2

まず、DrawNodesForLevel(int level)の機能を書いてください。その後、最上位レベルから開始し、タイマーを開始し、刻々と変化する度に、DrawNodesForLevel()に適切なレベルで電話をかけ、レベルを増加させます。終了すると、タイマーを停止します。

EDIT:レベルごとではなく、各ノード間で一時停止することを理解して更新されました。

関数内の変数を独自のDrawNodeStateクラスに移動し、DrawNode()を呼び出すたびにそのクラスのインスタンスを渡します。次に、DrawNode()を呼び出す代わりに、DrawNode()にタイマーを開始させます(DrawNodeStateクラスの一部でもあります)。そのタイマーがティックすると、tick関数はDrawNode()を呼び出し、それに状態構造を渡します。

ステート構造は、最後に左または右のノードを描画したかどうかを追跡する必要があるため、次に適切なノードを描画できます。

+0

あなたを助けた回答をupvote/acceptするのを忘れないでください。 (私だけでなく) –

+0

私はちょうど問題を発見しました。各ノードについて、それらの間に1秒の間隔で子供を描きたいと思います。これは、最初に左の子を描画し、1秒待ってから2番目の子を描画します。2番目のタイマーを追加しない限り、あなたの提案では実際には許されませんが、それが同期されていることを確認する必要があります他のタイマー... – Daniel

1

コードを2つの部分に分割します.1つはツリーを横切り、もう1つは既に持っているレンダリングです。

"traverse tree"コードをIEnumerable<node>に書き直して、ノードを1つずつ選択することができます。任意の順序でツリートラバーサルの非再帰的なバージョンがあるので、 "yield return"を使ってイテレータを作ることができます。あなたはコードを検証するための簡単なテストを作成することができます(これに必要なUIはありません)。

タイマーのコールバックよりも、イテレータの次の項目を単純にすべて完了するまで実行します。

関連する問題