none
VB2008 表單元件如何大量更新Label元件的圖案 RRS feed

  • 問題

  • 請教大大..小弟在表單中放入大約100各Label元件用來顯示各槽的液位SNESOR狀態 PUMP的ON/OFF

    當小弟收集完PLC I/O點的狀態後!顯示到對應的Label元件上,此時發覺..畫面更新速度相當的慢大約4-5Sec.但數據的收集只花大約0.6Sec

    小弟猜想應該是label元件顯示出了問題,以下是小弟的程式碼片段

    '宣告一個萬用變數來抓取對應的元件名稱
    Function GetControlsByName(ByVal Name As String) As Control
     Dim ctrl As Control = Me
     Do
      If ctrl.Name = Name Then Return ctrl
      ctrl = GetNextControl(ctrl, True)
     Loop Until IsNothing(ctrl)
     Return Nothing
     End Function
    '液位SENSOR顯示
     Private Sub LEVEL()
        For item As Integer = 73 To 95
          LEVELDISPLAY(item, mybuffer.IO_Buffer(item - 1))
        Next
        For item1 As Integer = 128 To 140
          LEVELDISPLAY(item1, mybuffer.IO_Buffer(item1 - 1))
        Next
      End Sub
      Delegate Sub LEVELCallback(ByVal item As Integer, ByVal state As Boolean)
      Private Sub LEVELDISPLAY(ByVal item As Integer, ByVal state As Boolean) ''液位狀態
        Select Case item
          Case item
            If DirectCast(GetControlsByName(("Label" & item).ToString), Label).InvokeRequired Then
              Dim d As New LEVELCallback(AddressOf LEVEL)
              Me.Invoke(d, New Object() {item, state})
            Else
              If state = True Then
                Dim R As Bitmap = New Bitmap(My.Resources.RED_LIGHT)
                DirectCast(GetControlsByName(("Label" & item).ToString), Label).Image = R
              Else
                Dim OFF As Bitmap = New Bitmap(My.Resources.OFF_LIGHT)
                DirectCast(GetControlsByName(("Label" & item).ToString), Label).Image = OFF
              End If
            End If
        End Select
      End Sub
    
    

    初步的判斷應該跑太多迴圈去找對應LABEL元件了!這部分有其它做法能改善嗎?

     


    新手上路
    2010年7月15日 上午 10:21

解答

  • 以我的習慣是不會這樣寫, 通常我會自己去繼承現有控制項製作一個屬於自己的控制項來處理.這有幾個好處

    (1)主程式看起來不會這麼混亂

    (2)畫面上一百個控制項, 未必在一次收完資料後都會更新, 所以不需要更新的就不要更新

    你程式另一個問題是在大量同類型控制項時使用你自訂的 GetControlsByName Method靠迴圈去找控制項真的很慢

    以下我大概是仿照你的作法寫的程式, 你可以試著去測看看, 怎樣的方式比較快.


    畫面是有一個FlowLayoutPanel (因為我懶得一個個算Label的位置, 用這它會自己排), Dock為Top. 然後在此Panel下方放置四個Label(Label1~Label4, 這是為顯示執行的差異時間用的), 四個Button (三種有點差異的方式去處理這100個在FlowLayoutPanel的自訂控制項), 然後為了要和你相同, 我也在Resource檔中放了兩張圖.
    Step 1:建立一個自訂的類別 (繼承Label)

    Public Class InfoLabel
        Inherits Label
        Private ImgOpen As Image = My.Resources.Resource1.DoorOpen
        Private ImgClose As Image = My.Resources.Resource1.DoorClose
        Private m_Infovalue As Byte
        Public Property Infovalue As Byte
            Get
                Return m_Infovalue
            End Get
            Set(ByVal value As Byte)
                If value <> m_Infovalue Then
                    If value = 1 Then
                        Me.Image = ImgClose
                    Else
                        Me.Image = ImgOpen
                    End If
                    m_Infovalue = value
                End If
            End Set
        End Property

        Private m_Index As Integer
        Public Property Index As Integer
            Get
                Return m_Index
            End Get
            Set(ByVal value As Integer)
                m_Index = value
            End Set
        End Property
        Sub New(ByVal Index As Integer)
            Me.Margin = (New Padding(3, 18, 3, 18))
            Me.Infovalue = 0
            Me.Index = Index

        End Sub
    End Class

    Step2: 在Form1中寫下以下的程式
    Public Class Form1
        Private ImgOpen As Image = My.Resources.Resource1.DoorOpen
        Private ImgClose As Image = My.Resources.Resource1.DoorClose
        Private InfoLabelList As New List(Of InfoLabel)
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            For i As Integer = 0 To 99
                Dim myInfoLabel As New InfoLabel(i)
                myInfoLabel.ForeColor = Color.Blue
                myInfoLabel.Name = "InfoLabel" & i.ToString()
                InfoLabelList.Add(myInfoLabel)

            Next
            Me.FlowLayoutPanel1.Controls.AddRange(InfoLabelList.ToArray())
        End Sub

        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            Dim BeginTime As Date = Now()
            For i As Integer = 0 To 99
                InfoLabelList.Item(i).Infovalue = 1
            Next
            Label1.Text = Now.Subtract(BeginTime).TotalMilliseconds
        End Sub

        Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
            Dim BeginTime As Date = Now()
            Me.SuspendLayout()
            For i As Integer = 0 To 99
                InfoLabelList.Item(i).Infovalue = 0
            Next
            Me.ResumeLayout()
            Label2.Text = Now.Subtract(BeginTime).TotalMilliseconds
        End Sub

        Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
            Dim BeginTime As Date = Now()
            For i As Integer = 0 To 99
                DirectCast(GetControlsByName(("InfoLabel" & i.ToString()).ToString()), InfoLabel).Image = My.Resources.Resource1.DoorClose
            Next
            Label3.Text = Now.Subtract(BeginTime).TotalMilliseconds
        End Sub
        Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
            Dim BeginTime As Date = Now()
            For i As Integer = 0 To 99
                DirectCast(GetControlsByName(("InfoLabel" & i.ToString()).ToString()), InfoLabel).Image = Me.ImgOpen
            Next
            Label4.Text = Now.Subtract(BeginTime).TotalMilliseconds
        End Sub
        Private Function GetControlsByName(ByVal Name As String) As Control
            Dim ctrl As Control = Me
            Do
                If ctrl.Name = Name Then Return ctrl
                ctrl = GetNextControl(ctrl, True)
            Loop Until IsNothing(ctrl)
            Return Nothing
        End Function
    End Class

    其中

    Button1和Button2是我慣用的方式(自訂類別+使用List(of T)), 這兩個只是為了讓圖會改變而已, 程式內容是一樣的
    Button3和Button4則是依你的寫法做的, 但兩個方式的差異在於Image屬性的來源不同, 通常量大時呼叫資源檔會比較慢

    你可以試著跑這程式, 看看差異在哪邊. 然後再告訴我們你的心得是什麼.

    補充一下結果: 你的寫法正好是這三種最慢的


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。
    • 已標示為解答 eblue 2010年7月16日 下午 03:39
    2010年7月16日 下午 12:22
    版主

所有回覆

  • 我覺得啦, 你應該是把整個陣列傳給委派函式 LEVELDISPLAY, 把迴圈寫在 LEVELDISPLAY中

    然後在迴圈外層 suspenlayout,resumelayout, 一次性的更新畫面.

    ex:

    Me.SuspendLayout

     For .......

     Next

    Me.ResumeLayout

     


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。
    2010年7月15日 下午 12:18
    版主
  • FAQ ,在本版

    1. 搜尋控制項陣列

    http://www.google.com.tw/search?hl=zh-TW&q=%E6%8E%A7%E5%88%B6%E9%A0%85%E9%99%A3%E5%88%97+site%3Asocial.msdn.microsoft.com%2FForums%2Fzh-TW%2F232%2F&aq=f

     

    2. 搜尋 SuspendLayout

    http://www.google.com.tw/search?hl=zh-TW&q=SuspendLayout+site%3Asocial.msdn.microsoft.com%2FForums%2Fzh-TW%2F232%2F&aq=f


    論壇是網友平等互助 保證解答請至 微軟技術支援服務
    提問時,錯誤情境描述與錯誤訊息很重要,情境描述包含你做了什麼,預期的結果與實際發生的結果。一個最爛的問法範例:「我的電腦電腦怎麼不能開機?」誰知道你家是不是沒電還是你根本找不到電源鈕。
    2010年7月15日 下午 12:43
  • Hi,

    提幾個我看到的問題點

    函式效能不佳 => GetControlsByName

    請改用.NET內建的方法

    不必要的動作太多 => GetControlsByName(("Label" & item)

    不需要每次搜尋,且常用的就是那幾個的話可用陣列存放物件參考

    不必要的轉型太多 => DirectCast(GetControlsByName(("Label" & item).ToString), Label)

    重複的轉型太多次了,考慮轉完後存放變數重複使用

     

    不必要的物件太多 =>New Bitmap(My.Resources.RED_LIGHT)

    頻繁使用考慮提升到全域

    不必要的分支判斷 =>Select Case item

    只有一個case不需分支

     


    謙卑學習,持之以恆,才能不斷的Level Up http://www.dotblogs.com.tw/larrynung/
    2010年7月15日 下午 01:02
  • 感謝大大們提供的建議

    關於控制陣列的部分小弟做法如下:

    宣告一個全域的函式:Dim ColLabel As New Collection

    for i as Integer =73 To 108

    ColLabel.Add(Me.Panel3.Controls.Find("Label" & i.ToString,True))

    Next

    中斷來看是有建立成功

    但我要怎麼呼叫出來使用呢?ColLabel(1) 跟ColLabel.Item(1) 都不對!


    新手上路
    2010年7月15日 下午 04:19
  •  你怎不直接Label的陣列 ? 或是用List(Of T)

     


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。
    2010年7月15日 下午 04:48
    版主
  • 抱歉.因為過去我沒建立過所以位的控制項陣列.所以找的方向會錯

    這是小弟的做法

    宣告全域的陣列函式

    Dim MyLabel(200) As Label

    在LOAD事件中將Label元件寫入陣列中

    for i as integer =1 to 200

    MyLabel(i)=GetControlsByName(("Label" & i).Tostring)

    這邊原本想利用MyLabel(i)=Me.Controls.Find("Label" & i ,True) '無法轉換成Label 關於這點小弟來去查一下MSDN

    Next


    新手上路

    2010年7月16日 上午 12:18
  • MyLabel(i)=GetControlsByName(("Label" & i).Tostring)

    這邊原本想利用MyLabel(i)=Me.Controls.Find("Label" & i ,True) '無法轉換成Label 關於這點小弟來去查一下MSDN

    Hi,

    MyLabel(i) = DirectCast(Me.Controls("Label" & i.ToString),Label)


    謙卑學習,持之以恆,才能不斷的Level Up http://www.dotblogs.com.tw/larrynung/
    2010年7月16日 上午 12:48
  • 恩恩~~突然想到我原本的作法有個轉型的DirectCast指令..感謝大大!

    能否請大大教小弟 List(Of T) 使用的方式..小弟看不太懂!


    新手上路
    2010年7月16日 上午 01:31
  • Dim myList as New List(of Label) 這樣就代表建立一個List物件, 其元素型別為 Label

    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。
    2010年7月16日 上午 02:35
    版主
  • 以我的習慣是不會這樣寫, 通常我會自己去繼承現有控制項製作一個屬於自己的控制項來處理.這有幾個好處

    (1)主程式看起來不會這麼混亂

    (2)畫面上一百個控制項, 未必在一次收完資料後都會更新, 所以不需要更新的就不要更新

    你程式另一個問題是在大量同類型控制項時使用你自訂的 GetControlsByName Method靠迴圈去找控制項真的很慢

    以下我大概是仿照你的作法寫的程式, 你可以試著去測看看, 怎樣的方式比較快.


    畫面是有一個FlowLayoutPanel (因為我懶得一個個算Label的位置, 用這它會自己排), Dock為Top. 然後在此Panel下方放置四個Label(Label1~Label4, 這是為顯示執行的差異時間用的), 四個Button (三種有點差異的方式去處理這100個在FlowLayoutPanel的自訂控制項), 然後為了要和你相同, 我也在Resource檔中放了兩張圖.
    Step 1:建立一個自訂的類別 (繼承Label)

    Public Class InfoLabel
        Inherits Label
        Private ImgOpen As Image = My.Resources.Resource1.DoorOpen
        Private ImgClose As Image = My.Resources.Resource1.DoorClose
        Private m_Infovalue As Byte
        Public Property Infovalue As Byte
            Get
                Return m_Infovalue
            End Get
            Set(ByVal value As Byte)
                If value <> m_Infovalue Then
                    If value = 1 Then
                        Me.Image = ImgClose
                    Else
                        Me.Image = ImgOpen
                    End If
                    m_Infovalue = value
                End If
            End Set
        End Property

        Private m_Index As Integer
        Public Property Index As Integer
            Get
                Return m_Index
            End Get
            Set(ByVal value As Integer)
                m_Index = value
            End Set
        End Property
        Sub New(ByVal Index As Integer)
            Me.Margin = (New Padding(3, 18, 3, 18))
            Me.Infovalue = 0
            Me.Index = Index

        End Sub
    End Class

    Step2: 在Form1中寫下以下的程式
    Public Class Form1
        Private ImgOpen As Image = My.Resources.Resource1.DoorOpen
        Private ImgClose As Image = My.Resources.Resource1.DoorClose
        Private InfoLabelList As New List(Of InfoLabel)
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            For i As Integer = 0 To 99
                Dim myInfoLabel As New InfoLabel(i)
                myInfoLabel.ForeColor = Color.Blue
                myInfoLabel.Name = "InfoLabel" & i.ToString()
                InfoLabelList.Add(myInfoLabel)

            Next
            Me.FlowLayoutPanel1.Controls.AddRange(InfoLabelList.ToArray())
        End Sub

        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            Dim BeginTime As Date = Now()
            For i As Integer = 0 To 99
                InfoLabelList.Item(i).Infovalue = 1
            Next
            Label1.Text = Now.Subtract(BeginTime).TotalMilliseconds
        End Sub

        Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
            Dim BeginTime As Date = Now()
            Me.SuspendLayout()
            For i As Integer = 0 To 99
                InfoLabelList.Item(i).Infovalue = 0
            Next
            Me.ResumeLayout()
            Label2.Text = Now.Subtract(BeginTime).TotalMilliseconds
        End Sub

        Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
            Dim BeginTime As Date = Now()
            For i As Integer = 0 To 99
                DirectCast(GetControlsByName(("InfoLabel" & i.ToString()).ToString()), InfoLabel).Image = My.Resources.Resource1.DoorClose
            Next
            Label3.Text = Now.Subtract(BeginTime).TotalMilliseconds
        End Sub
        Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
            Dim BeginTime As Date = Now()
            For i As Integer = 0 To 99
                DirectCast(GetControlsByName(("InfoLabel" & i.ToString()).ToString()), InfoLabel).Image = Me.ImgOpen
            Next
            Label4.Text = Now.Subtract(BeginTime).TotalMilliseconds
        End Sub
        Private Function GetControlsByName(ByVal Name As String) As Control
            Dim ctrl As Control = Me
            Do
                If ctrl.Name = Name Then Return ctrl
                ctrl = GetNextControl(ctrl, True)
            Loop Until IsNothing(ctrl)
            Return Nothing
        End Function
    End Class

    其中

    Button1和Button2是我慣用的方式(自訂類別+使用List(of T)), 這兩個只是為了讓圖會改變而已, 程式內容是一樣的
    Button3和Button4則是依你的寫法做的, 但兩個方式的差異在於Image屬性的來源不同, 通常量大時呼叫資源檔會比較慢

    你可以試著跑這程式, 看看差異在哪邊. 然後再告訴我們你的心得是什麼.

    補充一下結果: 你的寫法正好是這三種最慢的


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。
    • 已標示為解答 eblue 2010年7月16日 下午 03:39
    2010年7月16日 下午 12:22
    版主
  • 以我的習慣是不會這樣寫, 通常我會自己去繼承現有控制項製作一個屬於自己的控制項來處理.這有幾個好處

    (1)主程式看起來不會這麼混亂

    (2)畫面上一百個控制項, 未必在一次收完資料後都會更新, 所以不需要更新的就不要更新

    你程式另一個問題是在大量同類型控制項時使用你自訂的 GetControlsByName Method靠迴圈去找控制項真的很慢

    以下我大概是仿照你的作法寫的程式, 你可以試著去測看看, 怎樣的方式比較快.


    畫面是有一個FlowLayoutPanel (因為我懶得一個個算Label的位置, 用這它會自己排), Dock為Top. 然後在此Panel下方放置四個Label(Label1~Label4, 這是為顯示執行的差異時間用的), 四個Button (三種有點差異的方式去處理這100個在FlowLayoutPanel的自訂控制項), 然後為了要和你相同, 我也在Resource檔中放了兩張圖.
    Step 1:建立一個自訂的類別 (繼承Label)

    Public Class InfoLabel
        Inherits Label
        Private ImgOpen As Image = My.Resources.Resource1.DoorOpen
        Private ImgClose As Image = My.Resources.Resource1.DoorClose
        Private m_Infovalue As Byte
        Public Property Infovalue As Byte
            Get
                Return m_Infovalue
            End Get
            Set(ByVal value As Byte)
                If value <> m_Infovalue Then
                    If value = 1 Then
                        Me.Image = ImgClose
                    Else
                        Me.Image = ImgOpen
                    End If
                    m_Infovalue = value
                End If
            End Set
        End Property

        Private m_Index As Integer
        Public Property Index As Integer
            Get
                Return m_Index
            End Get
            Set(ByVal value As Integer)
                m_Index = value
            End Set
        End Property
        Sub New(ByVal Index As Integer)
            Me.Margin = (New Padding(3, 18, 3, 18))
            Me.Infovalue = 0
            Me.Index = Index

        End Sub
    End Class

    Step2: 在Form1中寫下以下的程式
    Public Class Form1
        Private ImgOpen As Image = My.Resources.Resource1.DoorOpen
        Private ImgClose As Image = My.Resources.Resource1.DoorClose
        Private InfoLabelList As New List(Of InfoLabel)
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            For i As Integer = 0 To 99
                Dim myInfoLabel As New InfoLabel(i)
                myInfoLabel.ForeColor = Color.Blue
                myInfoLabel.Name = "InfoLabel" & i.ToString()
                InfoLabelList.Add(myInfoLabel)

            Next
            Me.FlowLayoutPanel1.Controls.AddRange(InfoLabelList.ToArray())
        End Sub

        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            Dim BeginTime As Date = Now()
            For i As Integer = 0 To 99
                InfoLabelList.Item(i).Infovalue = 1
            Next
            Label1.Text = Now.Subtract(BeginTime).TotalMilliseconds
        End Sub

        Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
            Dim BeginTime As Date = Now()
            Me.SuspendLayout()
            For i As Integer = 0 To 99
                InfoLabelList.Item(i).Infovalue = 0
            Next
            Me.ResumeLayout()
            Label2.Text = Now.Subtract(BeginTime).TotalMilliseconds
        End Sub

        Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
            Dim BeginTime As Date = Now()
            For i As Integer = 0 To 99
                DirectCast(GetControlsByName(("InfoLabel" & i.ToString()).ToString()), InfoLabel).Image = My.Resources.Resource1.DoorClose
            Next
            Label3.Text = Now.Subtract(BeginTime).TotalMilliseconds
        End Sub
        Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
            Dim BeginTime As Date = Now()
            For i As Integer = 0 To 99
                DirectCast(GetControlsByName(("InfoLabel" & i.ToString()).ToString()), InfoLabel).Image = Me.ImgOpen
            Next
            Label4.Text = Now.Subtract(BeginTime).TotalMilliseconds
        End Sub
        Private Function GetControlsByName(ByVal Name As String) As Control
            Dim ctrl As Control = Me
            Do
                If ctrl.Name = Name Then Return ctrl
                ctrl = GetNextControl(ctrl, True)
            Loop Until IsNothing(ctrl)
            Return Nothing
        End Function
    End Class

    其中

    Button1和Button2是我慣用的方式(自訂類別+使用List(of T)), 這兩個只是為了讓圖會改變而已, 程式內容是一樣的
    Button3和Button4則是依你的寫法做的, 但兩個方式的差異在於Image屬性的來源不同, 通常量大時呼叫資源檔會比較慢

    你可以試著跑這程式, 看看差異在哪邊. 然後再告訴我們你的心得是什麼.

    補充一下結果: 你的寫法正好是這三種最慢的


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。


    當我第一次測試時就發現速度很慢的時候!我就覺得應該試跑了太多迴圈的語法,如大大提到的GetControlsByName.所以我在收集完數據後會先判斷I/O的狀態是否有變化,若有才進入變更顯示圖片.

    至於程式的差異!在自己的專案中就感受到相當的明顯..最先的程式..太多效能損耗在尋找Label元件的名稱中..與原程式相較大約只花1/5的處理時間~

     


    新手上路
    2010年7月16日 下午 03:39
  • Hi,

    您也可以改成用事件的方式處理

    撰寫個事件當資料收集完畢後觸發

    要變更的控制項繫上該事件做對應的處理應該就可以了


    謙卑學習,持之以恆,才能不斷的Level Up http://www.dotblogs.com.tw/larrynung/
    2010年7月16日 下午 04:17