1

私は二分木の内容を視覚的に出力するプログラムに取り組んできました(私が自分で書いたクラスによって順番に表されます)。このプログラムに含めたい最後の機能は、ツリーのポストオーダー、インオーダー、およびプレオーダーの構築のアニメーションです。

これは私が思っていたよりもはるかに難しいことが証明されました。元の描画方法は次のとおりです。

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

私の当初のアイデアは、最初の2つのif句のそれぞれにThread.Sleep(1000)を入れることでした。実際に必要なのは、ノードを描画する前にプログラムの実行を1秒間一時停止することだけでした。

Sleepメソッドが描画コードの実行をブロックしていることに気づいたので、そのメソッドをあきらめました。次に、Timersを使用しようとしましたが、ツリーを処理するときに非常に困難であることがわかりました。

私の目標は、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();
                    }
                };
            }
        }
4

4 に答える 4

4

タイマーを使用して、更新の適切な間隔を設定します。イベントでは、Tick描画の次のステップを実行して表示します。

于 2012-04-06T18:05:56.063 に答える
2

まず、自分でDrawNodesForLevel(int level)関数を作成します。次に、トップレベルから開始し、タイマーを開始します。タイマーが作動するたびにDrawNodesForLevel()、適切なレベルで呼び出し、レベルを上げます。終わりになったら、タイマーを止めます。

編集:各レベルではなく、各ノード間で一時停止したいという理解で更新されました。

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

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

于 2012-04-06T18:14:19.763 に答える
1

コードを2つの部分に分割します。1つはツリーをトラバースし、もう1つはレンダリング(既に持っています)です。

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

タイマーコールバックよりも、すべてが完了するまでイテレータから次のアイテムを取得するだけです。

于 2012-04-06T19:37:03.460 に答える
0

考えられる解決策の1つは、別のスレッドを生成し、そのスレッドを使用して定期的な間隔でDrawNode関数を呼び出すことです。このようにして、UIスレッドはブロックされません。

生成したスレッドはUIスレッドではないため、UIスレッドでDrawNode関数を明示的に呼び出す必要があります。これを行うための1つの潜在的な方法は次のとおりです。

C#の別のスレッドからGUIを更新するにはどうすればよいですか?

于 2012-04-06T18:00:06.187 に答える