篩選列與過濾面板
工具列上那一列篩選、它的分隔線、過濾面板與展開箭頭;SQL Memory 與 SQL Search 共用同一份。 這一層只放縮小搜尋範圍的條件(含套用查詢視窗連線的那一顆),排在輸入列上面;修飾字串怎麼比的 直接控制與作用在這一份結果的操作都在輸入列,界線見視窗骨架。 骨架與狀態表面見視窗骨架,視覺語言見 UI 準則, 元件的唯一出處見平台共用元件,驗收項目在視窗骨架那一份裡。
分隔線與換行
分隔線分兩級(
CreateGroupDivider/CreateItemDivider,住在中性的SqlAssistChrome.Buttons.cs—— 預覽工具列上導覽與命令那兩群用的是同一條),每一顆之間都有線: 群間高 18、左右各 6,群內矮一截淡一階(高 10、左右各 4、0.55)。分群規則一條:回答同一個 問題的是一群——Memory 是「狀態|期間|連線」,Search 是「搜哪裡|搜什麼|比對哪裡」。兩級 畫成同一種等於取消分群,使用者會把「種類」讀成第三個範圍。線不表達狀態,用Hairline。那一列篩選(
SqlFilterBar)唯一一份:先收字(只有SqlFilterFlyout降得了)再依群 換行,交給換行面板會讓三顆裡的一顆單獨掉到下一列;列首與整群Collapsed的(Favorites 的 狀態與期間)都收起自己前面那一條,否則列首從一條孤線開始。選了值的維度換強調底框(
SqlFilterFlyout.HasSelection,宿主判斷):必選的 SQL Search 伺服器 選定之後也亮,否則一排按鈕裡只有每一輪都在用的那一台看起來像還沒設定。與搜尋框開關的 「開著」同一組色,宣告在停駐與按下前面,停駐只換底色、強調框留著。窄窗收掉摘要之後,看得出 哪幾顆在縮小結果靠的就是它;工具列下面另列一排已選條件的那一版多佔一列,說的是同一件事。
過濾面板
- Filter Flyout(
SqlFilterFlyout)是唯一一份過濾面板:Search 的伺服器/資料庫/種類與 Memory 的伺服器/資料庫都是它。支援Single/Multiple/SearchableMultiple;單選用 radio、 選完自動關閉;分類保留 provider group 與 sort order。「沒有勾任何一個」是一個實際的預設, 所以它是面板第一列(SetEmptyOption)而不是一片空白——摘要寫著「全部」而一個勾都沒有時, 使用者會以為條件弄丟了。字與按鈕摘要共用一份且由宿主給,控制項不替它們挑一句; 伺服器與資料庫兩邊的未選都是「全部」(Search 的伺服器單選、必選,沒有這一列)。那一列畫成 radio(CreateFilterDefaultRow), 並用一條橫線(CreateFilterPanelDivider)與底下的複選區隔開:它與每一個選項互斥,選得上去、 取消不掉,而核取方塊的合約是可勾可取消,畫成核取方塊讀起來像壞掉;少了那條橫線,radio 又會 讓人以為整份清單只能選一個。它也不進虛擬化清單,留在捲動區外面:它是這個維度的預設值與 目前狀態,捲得走的那一版在名稱上百個時把狀態藏起來,而它又是回得去的那一條路。留在外面同時 讓它天生不受搜尋框過濾,不必再特判一次。 整批命令是兩顆並排、一直亮著的全選與全不選(SetBulkCommands),要不要由宿主決定, 而且接線時講一次就好——字不跟狀態跑。判準是有沒有搜尋框,不是是不是複選:沒有搜尋框時 「列出來的那一份」恆等於整份,全選就與「一個都沒勾」同義,那一列只剩一顆按不出差別的鈕, Search 的「種類」因此沒有它。不收起其中一顆:收起的那一版讓剩下那一顆滑進它的位置,同一個像素 就換了意思,按完全選、手沒移開再按一次會全清掉;也不停用,停用等於永遠有一顆是灰的。代價是 全勾時按全選、全空時按全不選各是一次 no-op,而 no-op 毀不掉東西,比會動、會換意思的按鈕便宜。 全不選仍走第一列那個預設的同一條清除路徑,不另寫一份狀態同步;所以「清除」依舊不畫成 第三個入口,而它值得在命令列上再出現一次——第一列是一個值,回答「沒指名時用哪一個」, 命令列上是一個動作,回答「把我剛勾的這幾個拿掉」,而勾了一部分是最常見的狀態。 全選只作用在目前列出來的那一份:面板把搜尋框的過濾字交給宿主(SelectAllRequested), 兩邊照SqlFilterFlyout.Matches同一條規則再篩一次,打了字就是「把篩出來的這幾個都勾起來」。 宿主那一端才知道整份是什麼——Search 按下去會先把整台的資料庫問回來,Memory 只勾得到已經載入 的那幾頁。自己在宿主重寫一份比對的症狀是面板上看到五個、勾起來卻是七個。這條界線由宿主用 自己的話掛在那一顆上(SetSelectAllHint,走 Tooltip 與 HelpText,不佔清單上方那一行—— 那一行要留給「正在讀取」與載入失敗)。全不選相反,清的是整個維度而不是篩出來的那一份。 別的選項變動時只改那一列(SyncEmptyOption):重建整份會把捲動位置與鍵盤焦點一起丟掉,而使用者可能正在 連勾好幾個。命令列是DockPanel而且LastChildFill關著:開著的話最後加進去的那一顆會吃掉 排序左邊的整段空白,命令鈕從一顆貼著文字的鈕變成一條橫跨面板的色塊。續頁(SetMore)與排序(SetSortOptions)是面板等級的一般能力,不綁任何一個 功能的值:兩顆鈕都在捲動區外面一直看得見,位移與頁大小留在宿主,換排序只問出去、宿主寫回來 才換按鈕。選項清單是虛擬化ItemsControl,不用ScrollViewer + StackPanel;快取 Style/ ControlTemplate,不快取有 parent 的UIElement。 - 排序選單開著的期間面板先不自己關(
StaysOpen),但收掉選單之後的還原要排到這一輪輸入 之後(RestoreAutoClose)。StaysOpen轉回false時 Popup 才去把滑鼠捕捉拿回來,而那一步 只在沒有人正握著捕捉時成立;選單關閉事件發出的那一刻捕捉還在選單手上,就地還原等於整步跳過。 症狀是使用者用過一次排序之後,那一份面板再也不會因為按到外面而收合——他接著點旁邊那一顆下拉, 兩張面板一起留在畫面上,唯一收得掉的方法是挑掉其中一個選項。面板在選單還開著時被收掉的話,Closed也要把StaysOpen寫回去,否則下一次打開就是同一張關不掉的面板。 這一條要在 SSMS 裡實測:沒有真的視窗與滑鼠捕捉,WPF 元件測試重現不出這個交互。 - 展開/收合箭頭(
CreateChevron/SetChevronExpanded;只有一顆箭頭的幽靈鈕是CreateChevronButton,給輸入列的下拉建議;攤開一段收著的字用連結,不用它)也只有一份:收合朝右、展開朝下, 轉過去而不是跳過去,而且綁的是Popup的開/關——綁 Click 的那一版在面板被按到外面關掉 之後箭頭仍朝下,指著一個不在畫面上的面板。