簡體   English   中英

如何使用 Thread.Sleep 為二叉樹設置動畫?

[英]How to use Thread.Sleep to animate a Binary Tree?

我一直在研究一個程序,它可以直觀地輸出二叉樹的內容(依次由我自己編寫的類表示)。 我要包含在該程序中的最后一個特征是樹的后序、中序和前序構造的 animation。

事實證明,這比我想象的更具挑戰性。 這是原始的繪制方法:

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) 放在前兩個 if 子句中的每一個中——我真正需要做的就是在每次繪制節點之前暫停程序的執行 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();
                    }
                };
            }
        }

使用計時器並設置適當的更新間隔。 Tick事件中執行下一步的繪制並顯示它。

首先,給自己寫一個DrawNodesForLevel(int level) function。然后從頂層開始,啟動計時器,每次計時時,調用具有適當級別的DrawNodesForLevel() ,並遞增級別。 當你走到盡頭時,停止計時器。

編輯:更新了你想在每個節點之間暫停的理解,而不是每個級別。

將 function 中的變量移動到它們自己的DrawNodeState class,並在調用 DrawNode() 時傳遞該 class 的實例。 然后,不是讓 DrawNode() 調用自身,而是讓 DrawNode() 啟動一個計時器(也是DrawNodeState類的一部分)。 當該計時器計時時,計時 function 調用 DrawNode() 並將其傳遞給 state 結構。

state 結構還必須跟蹤它最后繪制的是左節點還是右節點,以便接下來繪制合適的節點。

將您的代碼分成兩部分 - 一個遍歷樹,另一個渲染(您已經擁有)。

將您的“遍歷樹”代碼重寫為IEnumerable<node>以便您可以一個接一個地選擇節點。 任何順序都有非遞歸版本的樹遍歷,所以你可以使用“yield return”來制作迭代器。 您應該能夠創建簡單的測試來驗證代碼(不需要 UI)。

比在定時器回調中簡單地從迭代器中取出下一個項目直到完成。

一種可能的解決方案是生成一個單獨的線程,然后使用該線程定期調用 DrawNode function。 這樣UI線程就不會被阻塞。

由於您生成的線程不是 UI 線程,因此您需要在 UI 線程上顯式調用 DrawNode function。 這是一種可能的方法:

如何從 C# 中的另一個線程更新 GUI?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM