開發日誌 #3 - 多指令管理系統與 UI 優化
這次做了什麼?
Phase 2 做完自訂指令功能後,我很開心地用了幾天,但很快就發現一個問題:「欸,為什麼一個專案只能設定一個指令啊?」
你想想看,一個前端專案可能會有:
npm run dev- 開發伺服器npm run build- 建置專案npm test- 跑測試npm run lint- 檢查程式碼
每次只能記一個指令,其他的還是要手動打,這樣好像沒有完全解放生產力欸?就像手機通訊錄只能存一個聯絡人一樣,太限制了吧!
所以這次的 Phase 3,我決定把「單一指令」升級成「多個指令」。現在你可以為每個專案建立一整個指令工具箱,想執行哪個就點哪個,再也不用記那些常用指令了!
而且這次不只是功能升級,連 UI 也重新設計了好幾次,經過三輪優化才達到現在這個「看起來簡潔、用起來順手」的狀態。開發過程還蠻有趣的,中間踩了不少坑,也學到不少東西。
用了哪些技術?
ObservableCollection:自動更新的魔法
這次最關鍵的技術選擇,就是用 ObservableCollection 取代原本的單一字串。
一開始我天真地想說:「不就是把 string 改成 List 嗎?超簡單!」結果寫完後發現:新增指令後,畫面完全不更新!按了 Add 按鈕,輸入框消失了,但指令列表就是不出現新的項目。
Debug 半天才發現問題:List 雖然可以存多筆資料,但它不會主動告訴 UI「嘿,我的內容變了,你該更新了」。UI 就傻傻等在那邊,完全不知道資料已經改變。
後來改用 ObservableCollection 後,問題瞬間解決!這個集合類型很聰明,每次你加入或移除元素,它都會自動發送通知給所有綁定的 UI 元件。就像是有個小精靈在旁邊隨時喊「更新!更新!」,UI 收到通知就會立刻重新渲染。
這就是 MVVM 架構的魅力啊——選對資料結構,很多問題根本不需要手動處理,框架會幫你搞定一切。
ViewModel 的邏輯擴充
為了支援多指令功能,我在 ViewModel 裡加了一些新東西:
新屬性:
NewCommandText:儲存使用者正在輸入的新指令IsAddingCommand:一個布林值,控制輸入框要不要顯示
新 Command:
StartAddCommandCommand:開始新增模式(顯示輸入框)ConfirmAddCommandCommand:確認新增(把指令加進去)CancelAddCommandCommand:取消新增(隱藏輸入框)RemoveCustomCommandCommand:刪除某個指令
這些 Command 串起來,就形成了一個完整的「新增指令」工作流程。使用者點 Add → 輸入框出現 → 打字 → 確認 → 指令加入列表。整個過程流暢自然,完全符合直覺。
DataTrigger:聲明式 UI 的威力
以前學 WPF 的時候,老師都說「能用 XAML 做的事,就不要寫程式碼」。這次我終於深刻體會到這句話的意義。
輸入框的顯示/隱藏,我完全沒有在程式碼裡寫任何邏輯,全部用 DataTrigger 搞定。當 IsAddingCommand 是 true 時,輸入框和確認/取消按鈕就會出現;當它是 false 時,就會隱藏。
這種「聲明式」的寫法有個超大好處:邏輯清楚明瞭,不會散落在一堆 if-else 裡。未來如果要修改,直接看 XAML 就知道顯示邏輯是怎麼運作的,不用翻程式碼。
而且說真的,這樣寫起來也比較優雅。感覺就像在告訴電腦「當這個條件成立時,你應該顯示這個」,而不是「去檢查這個值,然後手動設定那個元件的 Visibility 屬性」。
UI/UX 優化的三次進化
這次開發最花時間的部分,其實不是寫程式邏輯,而是調整 UI。我前前後後改了三次,每次都覺得「這次應該完美了吧」,結果用一用又發現可以改進的地方。
第一次改進:Add 按鈕搬家
一開始我把 Add 按鈕放在指令列表的下方,邏輯上很合理:列表在上面,新增按鈕在下面。
但實際用起來發現,當指令變多的時候,按鈕會被擠到很下面,眼睛要找一下才看得到。而且視覺上也不太平衡,左邊是路徑列表,右邊是一大串指令加一個按鈕,感覺右邊特別長。
後來我突然想到:「欸,為什麼不把 Add 按鈕放到標題旁邊?」就像很多 App 的設計一樣,標題列右側放個 + 按鈕,既節省空間又直覺。
改完後發現效果真的好很多!現在「Custom Commands」標題和「+ Add」按鈕在同一排,視覺上很緊湊,而且不管指令有多少個,Add 按鈕的位置都固定在標題旁邊,超好找。
第二次改進:ScrollViewer 拯救世界
功能做完後,我開心地測試了一下,然後瘋狂新增指令:dev、build、test、lint、format、deploy…加到第十個的時候,突然發現畫面爆了。
下面的指令完全被視窗擋住,而且沒辦法往下捲!我試著調整視窗大小,但右側面板就是不會出現捲軸,下面的內容永遠看不到。
問題出在哪?原來是我沒有把右側面板包在 ScrollViewer 裡。WPF 的元件預設不會自動產生捲軸,內容超出範圍就直接被裁切掉了。
解決方法很簡單:把整個右側面板(包含 Path Details 和 Settings View)外面包一層 ScrollViewer,設定 VerticalScrollBarVisibility="Auto"。這樣當內容超出視窗時,捲軸就會自動出現,使用者可以往下滾動看到所有指令。
而且我特意沒有限制 MaxHeight,讓它跟隨視窗大小自動調整。這樣不管使用者把視窗開多大,內容都能完美適應,不會有奇怪的留白。
第三次改進:統一按鈕樣式的美學
功能都做完了,但我越看越覺得按鈕有點醜。Add 是一個樣子,Run 是一個樣子,刪除又是一個樣子,整體看起來不夠統一,像是拼湊出來的。
所以我決定重新設計所有按鈕的樣式,建立一套統一的視覺語言。
核心設計原則:
- 所有按鈕都用圓角(CornerRadius=“4”),看起來柔和不銳利
- 滑鼠懸停時顏色變深,點擊時更深,提供清楚的互動反饋
- 游標變成小手(Cursor=“Hand”),讓使用者知道這是可以點的
- 加上 ToolTip,滑鼠停留時顯示說明
顏色語意化:
- 藍色:新增功能(+ Add)
- 綠色:執行/確認動作(▶ Run、✓ 確認)
- 紅色:刪除/危險動作(✕ 刪除)
- 灰色:取消/次要動作(✕ 取消)
這樣設計的好處是,使用者看到顏色就知道這個按鈕的功能。看到綠色就知道「這會執行某個動作」,看到紅色就知道「這會刪掉東西,要小心」,完全不用思考。
改完後整個介面的質感提升超多!雖然功能沒變,但看起來更專業、更精緻,用起來也更舒服。這就是所謂的「細節決定成敗」吧。
遇到什麼挑戰?
挑戰一:UI 不更新的謎團
第一次實作時,我用 List 來存指令,結果發現新增指令後 UI 完全不動。我還以為是 Binding 寫錯,檢查了三遍 XAML,語法都對啊!
後來 Google 了一下才知道,List 不會主動通知 UI 更新。要嘛手動觸發 PropertyChanged,要嘛換用 ObservableCollection。
我選了後者,因為它更符合 MVVM 的精神:讓資料結構自己處理通知邏輯,ViewModel 不用操心這些細節。改完後問題瞬間解決,那種「終於通了!」的感覺真的很爽。
挑戰二:Binding 語法的陷阱
在寫指令列表的 DataTemplate 時,我遇到一個超詭異的錯誤:程式一啟動就崩潰,錯誤訊息是「雙向繫結需要 Path 或 XPath」。
但我明明沒有寫雙向繫結啊!Binding 就只是單純顯示指令文字而已,為什麼會要求雙向?
Debug 了半天才發現,原來 WPF 的某些情況下會預設使用雙向繫結,而在 DataTemplate 裡這樣做會有問題。解決方法是明確指定 Mode=OneWay,告訴 WPF「這是單向的,不要自作聰明」。
這個經驗告訴我:有時候明確比含蓄好。雖然 WPF 很聰明,但它不是讀心術,該講清楚的地方還是要講清楚。
挑戰三:XAML 結構迷宮
這次 UI 改動比較大,XAML 檔案變得很複雜。StackPanel 裡面有 ItemsControl,ItemsControl 裡面有 DataTemplate,DataTemplate 裡面又有 StackPanel…
改著改著,我自己都搞不清楚現在在哪一層了。有一次想加個 Margin,結果加錯地方,整個版面跑掉。
後來我學乖了,先用 git checkout 把檔案還原到穩定狀態,然後用最簡單的語法重新寫。少寫一些花俏的巢狀結構,多用直白的排版,維護起來輕鬆很多。
有時候簡單就是美啊!能用平鋪直述的方式寫,就不要搞得太複雜。複雜不代表厲害,簡潔才是真功夫。
挑戰四:ScrollViewer 要放在哪?
一開始我想說,那就在 ItemsControl 外面包個 ScrollViewer 吧。結果發現只有指令列表能捲動,Path Details 和其他區塊還是會被擠出畫面外。
後來才理解到,ScrollViewer 要包的是「整個可能超出範圍的內容」,而不是「某個特定元件」。所以正確的做法是把整個右側面板包起來,讓 Path Details、Custom Commands、Settings 全部都在可捲動範圍內。
這個經驗讓我更理解 WPF 的版面配置邏輯。不是哪裡會超出就包哪裡,而是要從整體架構去思考:使用者需要看到的完整內容是什麼?那整個範圍就應該是可捲動的。
使用體驗大升級
現在使用 Fast CLI Tool 的流程變成這樣:
- 選擇一個專案路徑
- 看到右側已經設定好的指令列表(可能是空的)
- 點 ”+ Add” 新增指令
- 輸入指令名稱(像 npm run dev)
- 確認 → 指令出現在列表中
- 重複 3-5,建立完整的指令工具箱
- 需要執行時,直接點對應的 ”▶ Run” 按鈕
每個指令都有自己的 Run 按鈕和刪除按鈕,整齊排列。視覺上清楚,操作上直覺,完全不需要學習成本。
而且因為有 ScrollViewer,就算你設定了二十個指令,也完全不會有畫面爆掉的問題。捲一捲就能看到所有內容,舒服。
技術總結
這次開發最大的收穫有幾個:
1. ObservableCollection 是 MVVM 的好夥伴
以前只知道它會自動通知更新,但沒有真正體會到它的威力。這次親身經歷「用 List 失敗 → 改用 ObservableCollection 成功」的過程,才真正理解為什麼 MVVM 架構這麼強調它。
選對資料結構,很多問題根本不是問題。這就是框架的價值——把常見的模式封裝起來,讓開發者專注在業務邏輯上。
2. UI 設計需要反覆打磨
從第一版到第三版,每一次調整都讓介面變得更好用一點點。Add 按鈕位置、捲動功能、按鈕樣式,這些看似小事,但累積起來就是「能用」和「好用」的差別。
而且我發現,自己用和想像中的使用體驗往往不一樣。很多問題是做出來、實際用了之後才會發現。所以不要怕改,勇敢調整,直到用起來真的順手為止。
3. 統一的視覺語言很重要
按鈕樣式統一後,整個介面的專業感瞬間提升。顏色語意化讓使用者不用思考就知道每個按鈕的功能,大幅降低認知負擔。
這讓我學到:設計不只是好看,更重要的是「有意義的好看」。每個顏色、每個圓角、每個互動效果,都應該有它的理由,而不是隨便選。
4. 簡潔優於複雜
XAML 寫得越複雜,維護起來越痛苦。能用簡單的結構達成目標,就不要搞得太花俏。這不是偷懶,而是為未來的自己(和其他維護者)著想。
三個月後回來看程式碼,你會感謝當初寫得簡潔明瞭的自己。
下一步
Phase 3 完成後,Fast CLI Tool 的核心功能基本上已經很完整了。但如果要繼續優化,還有一些方向可以探索:
短期改進:
- 指令排序功能(拖曳調整順序)
- 指令備註/標籤(為指令加上說明)
- 快捷鍵支援(Ctrl+1 執行第一個指令)
中期目標:
- 指令執行歷史記錄
- 常用指令範本庫(一鍵套用 npm、python、dotnet 的常用指令)
- 批次執行多個指令
長期願景:
- 指令輸出捕捉和顯示(在工具內直接看 log)
- 執行狀態監控(知道指令是否還在運行)
- 與 VS Code Terminal 整合
不過這些都是未來的事了。現在的版本已經能大幅提升我的開發效率,每次切換專案、執行指令都變得超級順暢。從單一指令到多個指令,這是質的飛躍,使用體驗完全不同!
開發完成於 2025年10月24日