[英]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。 這是一種可能的方法:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.