免费国产精品自在自线-91精品国产色综合久久久浪潮-99热久久免费频精品-国产精品国模在线观看-久久亚洲国产精品成人?V秋霞-久久国产一级A片免费播放-亚洲国产欧洲综合97久久-久久国产白嫩美女呻吟高潮

ARTICLE DETAIL

資訊詳情

深耕商務(wù)建站與企業(yè)官網(wǎng)運(yùn)營的一線實(shí)戰(zhàn)洞察。

WPF界面框架分層實(shí)踐:主題Token、MVVM與換膚避坑

WPF界面框架分層實(shí)踐:主題Token、MVVM與換膚避坑 簡介面向 WPF 桌面應(yīng)用開發(fā)者的一套現(xiàn)代化界面框架源碼與示例適合希望擺脫默認(rèn)控件外觀、快速搭建統(tǒng)一視覺風(fēng)格的中級(jí)開發(fā)者參考也可作為團(tuán)隊(duì) UI 規(guī)范與主題定制的基礎(chǔ)。壓縮包約 1.84MB共 336 個(gè)文件以 143 個(gè) C# 源碼與 115 個(gè) XAML 界面文件為主體另含 15 個(gè) csproj 工程文件、圖標(biāo)與圖片素材、Visual Studio 項(xiàng)目模板及少量說明文檔覆蓋控件實(shí)現(xiàn)、窗口外殼、主題資源到可復(fù)用模板的完整鏈路。目前已有 6464 人學(xué)習(xí)下載。內(nèi)容圍繞現(xiàn)代化窗口、菜單、對(duì)話框與過渡動(dòng)畫控件展開涉及主題外觀管理與系統(tǒng)版本適配等模塊可據(jù)此理解樣式模板、資源字典與 MVVM 在真實(shí)項(xiàng)目中的組織方式并直接改造為自己的應(yīng)用外殼。1. 一個(gè)漂亮的 WPF 界面框架漂亮之外到底解決什么問題上個(gè)月幫一個(gè)內(nèi)部工具做收尾功能全跑通了評(píng)審時(shí)被一句話打回來這套 WPF 界面看著像十年前的。真正動(dòng)手改才發(fā)現(xiàn)難點(diǎn)不在畫得好看而在主色散落在二十多個(gè) XAML 里、按鈕存在三套模板、每個(gè)窗口的標(biāo)題欄各寫各的。這份 WPF 界面框架源碼包就是把這堆散件整理成三層主題 Token、控件模板、窗口外殼再配一套 MVVM 骨架和若干示例頁。它主要解決兩件具體的事改一處顏色不用翻遍工程新頁面照著示例拼就能交付。適合正在用 WPF 做桌面端、要把界面從「能跑」推到「能驗(yàn)收」的人不適合想找拖拽式設(shè)計(jì)器、指望完全不寫 XAML 的人。2. 拆目錄結(jié)構(gòu)主題 Token、控件模板、窗口外殼這三層怎么分拿到一個(gè)界面框架先別急著運(yùn)行 Demo也別急著往自己的工程里復(fù)制。先看Themes目錄的層次因?yàn)樗鼪Q定了后面所有覆蓋、換膚、定制動(dòng)作能不能生效。這套框架的劃分是比較典型的做法Tokens放純值顏色、字號(hào)、圓角、間距、控件高度Controls放控件樣式與ControlTemplateSkins放皮膚覆蓋Shell放窗口外殼與標(biāo)題欄。分層的意義在于職責(zé)邊界清晰Token 里不該出現(xiàn)任何ControlTemplate控件模板里不該出現(xiàn)任何寫死的#2F6FED。我見過太多項(xiàng)目的資源字典是一個(gè)幾千行的Styles.xaml顏色、模板、動(dòng)畫、轉(zhuǎn)換器全塞在一起。這種文件在項(xiàng)目第三個(gè)月就會(huì)變成黑匣子誰都不敢刪任何一行因?yàn)椴恢绖h了會(huì)影響哪個(gè)頁面。分層不是潔癖是為了讓你敢改。2.1 先看資源字典的合并順序它決定后面所有覆蓋能不能生效WPF 在MergedDictionaries里查找同一個(gè) Key 時(shí)后加入的字典優(yōu)先級(jí)更高。這條規(guī)則很短但它是整套換膚機(jī)制的基石。很多人的皮膚切換做不出來原因就一個(gè)把皮膚字典加在了 Token 字典前面于是 Token 里的舊值一直勝出換膚看起來「沒反應(yīng)」。正確的合并順序是先 Token再 Controls最后 Skin。!-- App.xamlxmlns 省略實(shí)際工程里按模板生成的寫 -- Application.Resources ResourceDictionary ResourceDictionary.MergedDictionaries !-- 第 1 層純值沒有任何模板。換膚就是換這一層的值 -- ResourceDictionary SourceThemes/Tokens/Colors.Default.xaml/ ResourceDictionary SourceThemes/Tokens/Typography.xaml/ ResourceDictionary SourceThemes/Tokens/Metrics.xaml/ !-- 第 2 層控件模板只允許引用第 1 層的 Key -- ResourceDictionary SourceThemes/Controls/Button.xaml/ ResourceDictionary SourceThemes/Controls/TextBox.xaml/ ResourceDictionary SourceThemes/Controls/Card.xaml/ !-- 第 3 層皮膚覆蓋第 1 層同名 Key必須放最后 -- ResourceDictionary SourceThemes/Skins/Light.xaml/ /ResourceDictionary.MergedDictionaries /ResourceDictionary /Application.Resources這段順序背后有兩條必須記住的規(guī)則。第一條是查找方向MergedDictionaries的檢索是從后往前所以最后加載的皮膚能蓋住前面所有同名 Key。第二條是作用域Application.Resources是全局的窗口和控件會(huì)逐級(jí)向上查找一旦某一層出現(xiàn)了同名 Key上層的就永遠(yuǎn)取不到——調(diào)試時(shí)「樣式在別的頁面正常、就這個(gè)頁面不對(duì)」八成是這個(gè)頁面的Window.Resources里藏了一個(gè)同名項(xiàng)??绯绦蚣脮r(shí)路徑要改成 pack URI比如pack://application:,,,/YourApp;component/Themes/Tokens/Colors.Default.xaml程序集名寫錯(cuò)的話運(yùn)行時(shí)會(huì)拋IOException而不是編譯錯(cuò)誤這也是常見的第一道問題。2.2 顏色、字號(hào)、圓角三組 Token為什么主色要用 DynamicResourceToken 層只做一件事把「設(shè)計(jì)決定」變成「命名常量」。這套框架里大致是三類鍵名前綴即語義分類示例 Key典型值說明顏色Color.Accent.Base#2F6FED主色另配 Hover、Pressed 兩檔顏色Color.Surface.Card#FFFFFF卡片、面板底色字號(hào)Font.Size.Body13正文另有 Caption 12、Title 18圓角Radius.Control6按鈕、輸入框統(tǒng)一圓角間距Space.Unit8所有間距取它的整數(shù)倍尺寸Metric.Control.Height32單行控件標(biāo)準(zhǔn)高度主色不要只給一個(gè)值Hover 和 Pressed 要手調(diào)出來別用Opacity疊。原因是透明疊加的結(jié)果依賴背景色同一個(gè)按鈕放在白色卡片和深色側(cè)欄上淺色疊加出來的效果完全不同深色背景上會(huì)發(fā)灰。這是很常見的視覺問題單看沒問題一放到深色區(qū)域就顯得臟。!-- Tokens/Colors.Default.xaml -- ResourceDictionary xmlns... xmlns:x... !-- 主色三檔Hover/Pressed 手工調(diào)深保證在深色和淺色背景上都成立 -- Color x:KeyColor.Accent.Base#2F6FED/Color Color x:KeyColor.Accent.Hover#245BD0/Color Color x:KeyColor.Accent.Pressed#1C48A8/Color !-- Brush 單獨(dú)一層控件模板里用 Brush換膚時(shí)只換 Brush 即可 -- SolidColorBrush x:KeyBrush.Accent.Base Color{StaticResource Color.Accent.Base}/ SolidColorBrush x:KeyBrush.Accent.Hover Color{StaticResource Color.Accent.Hover}/ SolidColorBrush x:KeyBrush.Text.OnAccent Color#FFFFFF/ SolidColorBrush x:KeyBrush.Surface.Card Color#FFFFFF/ !-- 字號(hào)與度量數(shù)值型 Token 用 sys:Double必須帶命名空間 -- sys:Double x:KeyFont.Size.Body13/sys:Double sys:Double x:KeyFont.Size.Title18/sys:Double CornerRadius x:KeyRadius.Control6/CornerRadius sys:Double x:KeyMetric.Control.Height32/sys:Double /ResourceDictionary關(guān)鍵在于「顏色」和「畫筆」分成兩層。Color是不可變結(jié)構(gòu)體SolidColorBrush才是控件屬性真正吃的東西模板里引用的必須是 Brush。數(shù)值型 Token 要注意類型x:Double需要引入sys命名空間clr-namespace:System;assemblySystem.Runtime不同目標(biāo)框架程序集名略有差異CornerRadius直接寫元素即可不用額外命名空間?,F(xiàn)在說DynamicResource和StaticResource怎么選這是最常被問的一個(gè)問題。結(jié)論是會(huì)隨皮膚變化的 Key 用DynamicResource永不變的 Key 用StaticResource。StaticResource在 XAML 解析時(shí)就把值取出來定死換膚后不會(huì)重新求值所以把主色用StaticResource引到模板里切皮膚必然失敗DynamicResource每次取值走一次資源查找會(huì)帶來額外開銷在DataGrid單元格模板、ItemsControl項(xiàng)模板這種會(huì)實(shí)例化成千上萬次的地方能靜態(tài)就靜態(tài)。我的習(xí)慣是Token 里跟顏色相關(guān)的全用DynamicResource字號(hào)、圓角、間距這類換膚不動(dòng)的用StaticResource。這個(gè)劃分一句話能記住后面維護(hù)的人也不會(huì)亂改。2.3 拆一個(gè)按鈕從 Token 到 ControlTemplate 的完整鏈路理解了層次再拆一個(gè)具體控件看看這套框架里一個(gè)主按鈕是怎么拼起來的。這段值得逐行讀因?yàn)樗呛竺嫠锌丶哪0濉?-- Controls/Button.xaml只引用 Token不出現(xiàn)任何字面色值 -- Style x:KeyButton.Primary TargetTypeButton Setter PropertyHeight Value{StaticResource Metric.Control.Height}/ Setter PropertyMinWidth Value72/ Setter PropertyPadding Value16,0/ Setter PropertyFontSize Value{StaticResource Font.Size.Body}/ Setter PropertyForeground Value{DynamicResource Brush.Text.OnAccent}/ Setter PropertyBackground Value{DynamicResource Brush.Accent.Base}/ Setter PropertyBorderThickness Value0/ Setter PropertyCursor ValueHand/ Setter PropertyTemplate Setter.Value ControlTemplate TargetTypeButton Border x:NameBd Background{TemplateBinding Background} CornerRadius{StaticResource Radius.Control} SnapsToDevicePixelsTrue ContentPresenter HorizontalAlignmentCenter VerticalAlignmentCenter Margin{TemplateBinding Padding} RecognizesAccessKeyTrue/ /Border ControlTemplate.Triggers !-- 順序有講究后定義的觸發(fā)器優(yōu)先級(jí)更高 -- Trigger PropertyIsMouseOver ValueTrue Setter TargetNameBd PropertyBackground Value{DynamicResource Brush.Accent.Hover}/ /Trigger Trigger PropertyIsPressed ValueTrue Setter TargetNameBd PropertyBackground Value{DynamicResource Brush.Accent.Pressed}/ /Trigger Trigger PropertyIsEnabled ValueFalse Setter TargetNameBd PropertyOpacity Value0.45/ Setter PropertyCursor ValueArrow/ /Trigger /ControlTemplate.Triggers /ControlTemplate /Setter.Value /Setter /Style這里有幾個(gè)容易被忽略的細(xì)節(jié)每一個(gè)都可能變成后期幾分鐘甚至幾小時(shí)地去排查的問題。第一TemplateBinding是單向的、不支持轉(zhuǎn)換器且只能綁到模板化父級(jí)的屬性上。所以Padding能綁Background能綁但如果你想把Background轉(zhuǎn)成漸變就必須換成{Binding Background, RelativeSource{RelativeSource TemplatedParent}}再掛 Converter?;煊眠@兩者時(shí)不要抱有僥幸心理模板里一旦出現(xiàn)轉(zhuǎn)換需求就整體換成Binding。第二觸發(fā)器順序決定最終效果。IsMouseOver和IsPressed同時(shí)為真時(shí)后定義的IsPressed生效所以按下色必須寫在懸停色后面。反過來寫按下時(shí)看到的還是懸停色看起來就像「按下沒反饋」。第三IsEnabledFalse時(shí)如果只改OpacityCursorHand依然生效鼠標(biāo)移上去還是手型用戶會(huì)以為可以點(diǎn)。所以禁用態(tài)要順手把Cursor也改回來這類細(xì)節(jié)在驗(yàn)收時(shí)最容易被挑出來。最后控件模板里不要出現(xiàn)字面色值這是硬性約束。只要模板里出現(xiàn)一個(gè)#2F6FED換膚那一刻它就會(huì)變成色塊不一致的元兇而且很難靠肉眼定位到具體是哪個(gè)模板??蚣芾镞@類違規(guī)用一次文本搜索就能找出來接入前先搜一遍#順手清理掉。3. 接入現(xiàn)有工程從復(fù)制目錄到跑出第一個(gè)頁面3.1 目錄落位與 csproj 里的資源聲明把整個(gè)工程復(fù)制進(jìn)來是最快的驗(yàn)證方式但真正接入現(xiàn)有項(xiàng)目時(shí)建議只復(fù)制Themes和Infrastructure兩個(gè)目錄示例頁按需挑選著拿。目錄放在項(xiàng)目根下即可不要塞進(jìn)Properties或者obj旁邊obj目錄在清理時(shí)容易誤刪。csproj這塊有個(gè)高頻問題SDK 風(fēng)格工程的默認(rèn) glob 已經(jīng)把**/*.xaml編譯成Page你再手寫一遍Page IncludeThemes\**\*.xaml /就會(huì)報(bào)重復(fù)項(xiàng)NETSDK1022。所以大部分情況下什么都不用寫只在需要排除或者改變構(gòu)建方式時(shí)才動(dòng)它。ItemGroup !-- 默認(rèn) glob 已包含 **/*.xaml這里只做排除草稿目錄不參與編譯 -- Page RemoveThemes\_draft\**\*.xaml / !-- 字體、圖標(biāo)這類二進(jìn)制資源用 Resource隨程序集嵌入pack URI 才取得到 -- Resource IncludeAssets\Fonts\*.ttf / Resource IncludeAssets\Icons\*.png / !-- 示例數(shù)據(jù)用 Content 復(fù)制到輸出目錄別用 Resource 塞進(jìn)程序集 -- Content IncludeSampleData\*.json CopyToOutputDirectoryPreserveNewest / /ItemGroup構(gòu)建項(xiàng)的選擇直接決定資源能不能取到這張表建議存下來構(gòu)建項(xiàng)編譯結(jié)果適用場景取用方式Page編譯為 BAML所有 XAML包括資源字典Source相對(duì)路徑Resource嵌入程序集字體、圖標(biāo)、小圖片pack://application:,,,/程序集;component/路徑Content原樣復(fù)制到輸出目錄示例數(shù)據(jù)、可替換配置相對(duì)輸出目錄的路徑None不處理文檔、說明無字體用Resource嵌入之后在 XAML 里通過FontFamily/Assets/Fonts/#字體內(nèi)部名引用注意斜杠后面那個(gè)#后面跟的是字體家族名不是文件名。寫錯(cuò)了不會(huì)報(bào)錯(cuò)只會(huì)靜默回退到系統(tǒng)默認(rèn)字體——這類靜默失敗最耗時(shí)間。3.2 App.xaml 合并字典與運(yùn)行時(shí)換膚皮膚切換不要用「先清空再添加」的寫法。清空MergedDictionaries會(huì)在瞬間讓所有DynamicResource失效控件重繪時(shí)可能拋出找不到資源的異常而「只添加不移除」會(huì)讓字典越積越多切換五十次之后內(nèi)存和查找開銷都會(huì)肉眼可見地上漲。正確做法是把皮膚字典固定在MergedDictionaries的最后一位換膚就是替換這一項(xiàng)。public static class ThemeManager { // 皮膚字典固定放在 MergedDictionaries 的末位替換這一項(xiàng)即為換膚 private static readonly Uri LightSkin new(pack://application:,,,/YourApp;component/Themes/Skins/Light.xaml); private static readonly Uri DarkSkin new(pack://application:,,,/YourApp;component/Themes/Skins/Dark.xaml); public static void Apply(bool dark) { var merged Application.Current.Resources.MergedDictionaries; if (merged.Count 0) return; var skin new ResourceDictionary { Source dark ? DarkSkin : LightSkin }; // 關(guān)鍵替換而不是 Add避免字典無限增長 merged[merged.Count - 1] skin; } }替換完成后不需要手動(dòng)刷新任何控件DynamicResource會(huì)自動(dòng)重新求值。這里有個(gè)前提所有隨皮膚變化的引用必須是DynamicResource只要模板里漏了一個(gè)StaticResource換膚后那個(gè)控件就會(huì)保持舊色在一片新配色里顯得很突兀。還要注意換膚的時(shí)機(jī)。啟動(dòng)時(shí)在App構(gòu)造函數(shù)里調(diào)用是最穩(wěn)的如果放在窗口Loaded之后用戶會(huì)看到一次明顯的閃爍。真想做到無閃爍就得在窗口顯示前把皮膚定好也就是把配置讀取提前到App初始化階段。3.3 無邊框窗口外殼與自定義標(biāo)題欄這套框架的外殼用的是WindowChrome而不是WindowStyleNone加AllowsTransparencyTrue。后者雖然也能做出無邊框效果但會(huì)讓窗口失去硬件加速路徑拖動(dòng)和縮放時(shí)明顯發(fā)澀圓角邊緣還會(huì)有鋸齒。WindowChrome保留系統(tǒng)窗口的非客戶區(qū)行為只把繪制權(quán)交給你。!-- Shell/AppWindow.xaml無邊框外殼保留系統(tǒng)縮放與拖拽行為 -- Window x:ClassYourApp.Shell.AppWindow WindowStyleNone ResizeModeCanResize AllowsTransparencyFalse Background{DynamicResource Brush.Surface.Card} !-- CaptionHeight頂部可拖拽區(qū)高度等于標(biāo)題欄高度 -- !-- ResizeBorderThickness邊緣拖拽熱區(qū)觸屏場景建議放大到 8 -- WindowChrome.WindowChrome WindowChrome CaptionHeight40 ResizeBorderThickness6 GlassFrameThickness0 CornerRadius0 UseAeroCaptionButtonsFalse/ /WindowChrome.WindowChrome Grid Grid.RowDefinitions RowDefinition Height40/ RowDefinition Height*/ /Grid.RowDefinitions !-- 標(biāo)題欄區(qū)域內(nèi)的按鈕必須聲明 IsHitTestVisibleInChrome否則點(diǎn)不到 -- StackPanel Grid.Row0 OrientationHorizontal HorizontalAlignmentRight WindowChrome.IsHitTestVisibleInChromeTrue Button Content— Style{StaticResource Button.Caption}/ Button Content□ Style{StaticResource Button.Caption}/ Button Content× Style{StaticResource Button.Caption}/ /StackPanel ContentPresenter Grid.Row1 Content{Binding Body}/ /Grid /Window三個(gè)參數(shù)決定了使用手感。CaptionHeight要和標(biāo)題欄那一行的高度嚴(yán)格對(duì)齊寫 40 就是 40寫小了頂部會(huì)剩一條既不能拖也不能點(diǎn)的死區(qū)ResizeBorderThickness在鼠標(biāo)場景 6 夠了觸屏設(shè)備建議 8 到 10否則邊緣拖不動(dòng)GlassFrameThickness0去掉系統(tǒng)玻璃層避免深色皮膚下邊緣發(fā)亮。標(biāo)題欄里的按鈕必須加WindowChrome.IsHitTestVisibleInChromeTrue這一條不加按鈕會(huì)變成「看得見點(diǎn)不著」鼠標(biāo)事件被拖拽區(qū)吃掉了。這是無邊框窗口最常見的一個(gè)問題排查時(shí)容易往命令綁定上想繞一圈才發(fā)現(xiàn)是命中測試。另一個(gè)是最大化蓋住任務(wù)欄。無邊框窗口最大化時(shí)系統(tǒng)給的尺寸包含非工作區(qū)直接鋪滿會(huì)頂?shù)羧蝿?wù)欄。簡單的做法是在窗口構(gòu)造里設(shè)MaxHeight SystemParameters.MaximizedPrimaryScreenHeight多顯示器場景下這個(gè)值只在主屏正確需要監(jiān)聽WM_GETMINMAXINFO自己算工作區(qū)框架的Shell目錄里有對(duì)應(yīng)的輔助類接入時(shí)按屏幕數(shù)量確認(rèn)一下。3.4 驗(yàn)收入口DPI、縮放、多屏的檢查清單界面框架接進(jìn)去功能點(diǎn)兩下都正常不代表能交付。下面這張表是我每次接入必過的一遍成本不到十分鐘能提前擋掉大部分返工。檢查項(xiàng)操作不通過的表現(xiàn)單屏 100%系統(tǒng)縮放調(diào)到 100%逐頁翻一遍1px 邊框消失或變粗單屏 150%縮放到 150%看文字與圖標(biāo)文字發(fā)虛、圖標(biāo)模糊多屏不同縮放兩屏分別設(shè) 100% 和 150%拖窗口來回拖到另一屏后整體變糊高對(duì)比度切換系統(tǒng)高對(duì)比度主題文字與背景同色看不見觸摸觸屏設(shè)備上點(diǎn)邊緣縮放邊緣拖不動(dòng)最小尺寸拖到最小寬高標(biāo)題欄按鈕被擠出可視區(qū)DPI 那一欄解釋一下WPF 是邏輯像素體系縮放變化時(shí)如果圖片資源只準(zhǔn)備了一份系統(tǒng)會(huì)拉伸結(jié)果就是模糊。圖標(biāo)盡量用矢量路徑位圖則按 100%、150%、200% 各備一份靠Image的尺寸選擇邏輯去匹配。這條不做用戶在 150% 縮放的筆記本上第一眼就會(huì)覺得「這軟件有點(diǎn)舊」。4. MVVM 骨架命令、通知、導(dǎo)航與對(duì)話框怎么切界面層的活干完接著看骨架。這套框架的Infrastructure目錄里放的是一套很薄的基礎(chǔ)設(shè)施ViewModelBase、RelayCommand、AsyncCommand、導(dǎo)航服務(wù)、對(duì)話框服務(wù)。判斷一套 MVVM 骨架好不好標(biāo)準(zhǔn)只有一條ViewModel 里能不能完全不出現(xiàn)Window、MessageBox、Application.Current。出現(xiàn)了單測就廢了復(fù)用也別想了。4.1 ViewModelBase屬性通知與 SetProperty 的寫法public abstract class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler? PropertyChanged; // 返回 bool方便 setter 里鏈?zhǔn)酵ㄖ缮鷮傩?protected bool SetPropertyT(ref T field, T value, [CallerMemberName] string? name null) { // 用 EqualityComparer 而不是 object.Equals避免值類型裝箱 if (EqualityComparerT.Default.Equals(field, value)) return false; field value; OnPropertyChanged(name); return true; } protected void OnPropertyChanged([CallerMemberName] string? name null) PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); }CallerMemberName省掉了手寫屬性名字符串改屬性名時(shí)不會(huì)漏改通知。相等判斷用EqualityComparerT.Default而不是因?yàn)榉盒屠飳?duì)值類型是裝箱比較屬性高頻更新時(shí)這點(diǎn)開銷會(huì)積少成多。返回bool是個(gè)很實(shí)用的小設(shè)計(jì)if (SetProperty(ref _name, value)) OnPropertyChanged(nameof(DisplayName));這樣派生屬性只在真正變化時(shí)通知避免無謂的綁定刷新。老項(xiàng)目里常見的寫法是 setter 里連續(xù)調(diào)三次OnPropertyChanged每次輸入都觸發(fā)一輪完整重算。集合屬性是另一個(gè)問題點(diǎn)。ObservableCollectionT不要每次賦值都new那等于讓ItemsControl重建整個(gè)視圖樹滾動(dòng)位置會(huì)跳回頂部。正確做法是初始化一次之后用Clear()加Add()更新。4.2 RelayCommand 與 AsyncCommandCanExecute 刷新時(shí)機(jī)的坑public sealed class RelayCommand : ICommand { private readonly Actionobject? _execute; private readonly Funcobject?, bool? _canExecute; public RelayCommand(Actionobject? execute, Funcobject?, bool? canExecute null) { _execute execute ?? throw new ArgumentNullException(nameof(execute)); _canExecute canExecute; } public bool CanExecute(object? parameter) _canExecute?.Invoke(parameter) ?? true; public void Execute(object? parameter) _execute(parameter); public event EventHandler? CanExecuteChanged { add CommandManager.RequerySuggested value; remove CommandManager.RequerySuggested - value; } // 手動(dòng)觸發(fā)異步命令結(jié)束、集合變化后必須調(diào) public void RaiseCanExecuteChanged() CommandManager.InvalidateRequerySuggested(); }CommandManager.RequerySuggested是隱式刷新只在鍵盤、鼠標(biāo)等輸入事件之后觸發(fā)。這意味著如果你在后臺(tái)線程里改了某個(gè)標(biāo)志位按鈕的可用狀態(tài)可能要等到用戶點(diǎn)一下別的地方才更新。血淚經(jīng)驗(yàn)是異步命令執(zhí)行完畢、批量數(shù)據(jù)加載完成這類時(shí)刻必須顯式調(diào)一次RaiseCanExecuteChanged()否則用戶會(huì)看到「按鈕還是灰的但功能其實(shí)已經(jīng)好了」。異步命令額外要處理兩件事。一是防重入執(zhí)行中把CanExecute置為false避免用戶連點(diǎn)三次發(fā)出三個(gè)請(qǐng)求。二是異常兜底async void里拋出的異常會(huì)直接打到未處理異常上進(jìn)程就掛了??蚣芾锏腁syncCommand用try/catch包住執(zhí)行體把異常交給注入的錯(cuò)誤處理器再由它彈提示。public sealed class AsyncCommand : ICommand { private readonly Funcobject?, Task _execute; private readonly Funcobject?, bool? _canExecute; private bool _isRunning; // 防重入標(biāo)志 public AsyncCommand(Funcobject?, Task execute, Funcobject?, bool? canExecute null) { _execute execute; _canExecute canExecute; } public bool CanExecute(object? parameter) !_isRunning (_canExecute?.Invoke(parameter) ?? true); public async void Execute(object? parameter) { if (_isRunning) return; _isRunning true; RaiseCanExecuteChanged(); try { await _execute(parameter); } catch (Exception ex) { // 必須兜住async void 里的異常無處可去 ErrorHandler?.Invoke(ex); } finally { _isRunning false; RaiseCanExecuteChanged(); } } public event ActionException? ErrorHandler; public event EventHandler? CanExecuteChanged; public void RaiseCanExecuteChanged() CanExecuteChanged?.Invoke(this, EventArgs.Empty); }AsyncCommand我沒有復(fù)用CommandManager而是自己維護(hù)CanExecuteChanged。原因是異步場景下按鈕狀態(tài)變化跟輸入事件沒有必然關(guān)系走隱式刷新容易失準(zhǔn)自己控制雖然要多調(diào)幾次RaiseCanExecuteChanged但行為可預(yù)期。4.3 導(dǎo)航與對(duì)話框接口怎么切才不綁死 UI接口切分的目標(biāo)是讓 ViewModel 只描述「要什么」不描述「怎么顯示」。這張表是框架里的切法接口職責(zé)不該做的事INavigationService按 ViewModel 類型切換主區(qū)域內(nèi)容不引用具體Page、Window類型IDialogService確認(rèn)框、輸入框、等待提示不直接調(diào)MessageBoxINotificationService頂欄消息、Toast不持有具體控件的引用IThemeService讀取與切換皮膚不碰Application.Resources的索引public interface IDialogService { // 返回 Task調(diào)用方 await 之后拿結(jié)果避免回調(diào)地獄 Taskbool ConfirmAsync(string title, string message); Taskstring? PromptAsync(string title, string message, string? defaultValue null); }實(shí)現(xiàn)側(cè)常見的做法是用一個(gè)覆蓋全窗口的ContentControl加半透明遮罩把對(duì)話框內(nèi)容當(dāng)作普通控件渲染返回值靠TaskCompletionSourcebool在用戶點(diǎn)擊時(shí)SetResult。這樣對(duì)話框也吃同一套 Token深色皮膚下不會(huì)出現(xiàn)一個(gè)白底黑字的系統(tǒng)彈窗視覺上跳出來。這里要提醒一點(diǎn)TaskCompletionSource必須用TaskCreationOptions.RunContinuationsAsynchronously否則SetResult會(huì)在 UI 線程上同步跑完整個(gè)await后續(xù)邏輯遇到重活就卡界面。這個(gè)問題非常隱蔽表現(xiàn)為「點(diǎn)確認(rèn)之后界面頓一下」把選項(xiàng)加上就好。5. 避坑記錄樣式不生效、內(nèi)存上漲、DPI 模糊的排查順序這一章是這份框架里最值得先讀的部分。下面四條都是我在真實(shí)接入里遇到過的每一條按現(xiàn)象、原因、解決的順序?qū)懹龅酵悊栴}時(shí)可以照著順序查。5.1 現(xiàn)象改了 Token控件紋絲不動(dòng)現(xiàn)象把Color.Accent.Base從藍(lán)改成橙重新生成主按鈕還是藍(lán)的。清緩存、重啟 IDE 都試過仍然是藍(lán)。原因控件模板里引用的是{StaticResource Brush.Accent.Base}。StaticResource在 BAML 解析階段就把值取出來寫死了后續(xù)字典變化對(duì)它沒有任何影響。另一種可能是這個(gè)頁面的Window.Resources里定義了一個(gè)同名的Brush.Accent.Base局部作用域優(yōu)先全局的改動(dòng)被它遮住。解決在解決方案里搜StaticResource Brush.凡是顏色類的引用全部換成DynamicResource再逐頁檢查Window.Resources和UserControl.Resources里有沒有同名 Key。這兩步做完改動(dòng)立即生效。我的習(xí)慣是把這條寫進(jìn)代碼評(píng)審清單模板里出現(xiàn)StaticResource后面跟Brush.前綴的一律打回。5.2 現(xiàn)象切幾次皮膚內(nèi)存一路往上走現(xiàn)象在設(shè)置頁反復(fù)切換淺色和深色任務(wù)管理器里的內(nèi)存從 180MB 漲到 400MB 不回落切得越多漲得越多。原因換膚實(shí)現(xiàn)寫成了merged.Add(skin)每次切換都往MergedDictionaries里追加一個(gè)字典舊字典既沒被移除也沒被回收資源查找還得從后往前遍歷一長串。另一個(gè)隱性原因是畫筆沒凍結(jié)SolidColorBrush每次創(chuàng)建都是新的可凍結(jié)對(duì)象沒調(diào)Freeze()就參與變更通知無法被共享。解決換膚改成替換末位元素見 3.2 的寫法Token 里所有SolidColorBrush在創(chuàng)建后調(diào)一次Freeze()凍結(jié)后的畫筆可以被多個(gè)控件共享內(nèi)存占用會(huì)明顯下降。改完再測反復(fù)切換五十次內(nèi)存曲線基本走平。提示Freeze()只能用于可凍結(jié)對(duì)象而且凍結(jié)后不能再改屬性。如果某個(gè)畫筆需要在運(yùn)行時(shí)調(diào)色就別凍結(jié)它。5.3 現(xiàn)象4K 屏上字體發(fā)虛、1px 邊框消失現(xiàn)象開發(fā)機(jī) 100% 縮放一切正常換到 150% 縮放的筆記本上正文明顯發(fā)糊卡片之間的 1px 分隔線時(shí)有時(shí)無圓角處還有毛邊。原因三個(gè)疊加問題。位圖圖標(biāo)只準(zhǔn)備了一份系統(tǒng)拉伸導(dǎo)致模糊Border的BorderThickness用了1但沒開UseLayoutRounding和SnapsToDevicePixels在非整數(shù)縮放比下 1px 被攤到兩個(gè)物理像素上顏色被稀釋到看不見再就是窗口設(shè)了AllowsTransparencyTrue透明窗口在部分縮放下失去硬件渲染路徑。解決圖標(biāo)改用矢量路徑在外殼根元素上開UseLayoutRoundingTrue需要像素對(duì)齊的Border和Image加SnapsToDevicePixelsTrue無邊框窗口換回WindowChrome方案。三條一起改150% 下再對(duì)比截圖邊緣和文字都恢復(fù)銳利。5.4 現(xiàn)象設(shè)計(jì)器里正常運(yùn)行時(shí)拋 XamlParseException現(xiàn)象XAML 在設(shè)計(jì)器里預(yù)覽得好好的一運(yùn)行就拋XamlParseException提示找不到某個(gè)資源堆棧指向一個(gè)看起來沒問題的Setter。原因StaticResource的解析是編譯期決定的它只能向前查找——引用必須出現(xiàn)在定義之后或者定義在更早就合并的字典里。設(shè)計(jì)器有時(shí)會(huì)更寬松地加載于是出現(xiàn)「設(shè)計(jì)器能看、跑起來就崩」。另一種原因是跨程序集引用時(shí) pack URI 里的程序集名寫錯(cuò)編譯期不校驗(yàn)運(yùn)行時(shí)才爆。解決先在App.xaml里把 Token 字典排在最前面確保所有模板在后面再檢查所有跨程序集的Source程序集名、大小寫、component關(guān)鍵字都要對(duì)上。定位技巧是把App.xaml里的合并字典臨時(shí)注釋掉一半二分法很快能鎖定是哪個(gè)文件。5.5 常見問題速查表癥狀第一嫌疑快速驗(yàn)證換膚后個(gè)別控件不變色模板里用了StaticResource全工程搜StaticResource Brush.按鈕點(diǎn)不動(dòng)標(biāo)題欄命中測試被吃檢查IsHitTestVisibleInChrome最大化蓋住任務(wù)欄無邊框窗口的尺寸計(jì)算看MaxHeight是否按工作區(qū)算列表刷新后滾動(dòng)條跳頂集合被整體new檢查ObservableCollection賦值處命令按鈕一直灰隱式刷新未觸發(fā)手動(dòng)調(diào)一次RaiseCanExecuteChanged字體不是預(yù)期那個(gè)FontFamily的家族名寫錯(cuò)核對(duì)#后面是家族名而非文件名6. 進(jìn)階把換膚做成可以回歸驗(yàn)證的流程界面類的改動(dòng)最難驗(yàn)證因?yàn)樗缚雌饋頉]問題」和「確實(shí)沒問題」之間差得很遠(yuǎn)。這套框架里我最常用的一個(gè)技巧是把換膚做成可以對(duì)比的回歸流程。第一步把 Token 抽出來做一份中間格式??蚣艿腡okens目錄本身就是鍵值對(duì)用幾十行代碼掃一遍 XAML把Color.*、Font.Size.*、Radius.*導(dǎo)出成 JSON交給設(shè)計(jì)側(cè)對(duì)齊。之后設(shè)計(jì)師改配色改的是這份 JSON工程師用腳本回寫 XAML比在群里發(fā)色值截圖靠譜得多?;貙懩_本要注意保留原有的x:Key順序和縮進(jìn)否則每次生成都是幾百行 diff評(píng)審時(shí)沒人看得下去。第二步準(zhǔn)備一個(gè)「Token 全覽頁」。把所有 Token 渲染成色塊、字號(hào)樣例、圓角樣例配上鍵名放在調(diào)試菜單里。換膚以后直接翻這一頁哪個(gè)鍵沒被覆蓋一眼就能看出來。這個(gè)頁面我一般不到半小時(shí)就能拼完但省下來的排查時(shí)間遠(yuǎn)超投入。第三步截圖比對(duì)。把關(guān)鍵頁面在淺色和深色下各截一張存在版本庫里改了樣式之后重新截用逐像素差值定位變化區(qū)域。閾值不要設(shè)在零字體渲染有亞像素抖動(dòng)允許千分之三以內(nèi)的差異超過就是真改了。這套流程跑順之后改一個(gè) Token 影響哪些頁面提交前就能知道。還有一個(gè)小習(xí)慣值得說一下每次接入新的界面框架我都會(huì)先在空工程里跑一遍它的 Demo把所有頁面點(diǎn)一遍重點(diǎn)看深色皮膚下的對(duì)比度和禁用態(tài)。原因很簡單這些地方最容易藏問題而在自身工程里樣式問題會(huì)被業(yè)務(wù)邏輯掩蓋很難判斷到底是框架的問題還是你寫錯(cuò)了。從那以后我每次往項(xiàng)目里引 UI 框架都強(qiáng)制先走一遍這套空工程驗(yàn)證再動(dòng)業(yè)務(wù)代碼。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取
返回列表
PREV
查看更多資訊
NEXT
返回資訊列表
久久精彩视频| 婷婷在线网| 久久五月天激情婷婷| 成人免费120分钟啪啪| 狠狠五月激情婷婷直播片| 婷婷狠狠青青| 69超碰在线| 五月婷婷|欧美| 婷婷丁香五月视频| 亚洲成人网站在线观看| 99re热在线视频观看| 亚洲色婷婷五月天| 日韩在线视频中文字幕| 亚洲综合五月天| 5月丁香婷婷| 一区无码| 思思网站| 六月婷婷av| 1级欧美日韩| 国产精品电影| 这里只有精品2| 伊人久久五月天| 伊人婷婷青青cao| 婷婷激情五月天桃花网| 97婷婷五月丁香| 五月综合色| 97热这里精品在线视频| 99热99精品| 青青草原99热| 天天干 夜夜爽| 99久久99热这里只有精品| 香蕉狠狠爱视频| 日本欧美999久久久三级片| 无码少妇高潮喷水A片免费| 啪啪操超碰| 99热成人在线观看| 国产26uuu| 丁香五月综合福利视频导航| 夜夜操夜夜姧| www.ppypp| 色丁香在线视频| 91大神操美女| 狼人婷婷久久| 五月丁香啪啪网| 五月丁香婷婷免费视频| 99激情| 五月丁香五月天现场视频| 99在线观看亚洲| 超碰在线精品| 七七九九色色| 五月激情久久综合| 思思re99视频在线观看| 天堂综合久| 九九热在线观看视频| 涩涩涩婷婷| 丁香五月天激情五月天激情五月天激情网 | 日本97在线视频| 成人一区在线观看| 五月婷婷啪啪啪| 色婷婷综合影院| 99草视频在线观看| 91热爆在线| 精品久久99码| 天天操天天草天天草天天| 全国最新疫情| www.激情| 五月综合激情婷婷六月色窝| 亚洲欧美日韩另类| 五月丁香婷婷久久| 五月天婷婷色小说| 日本久久人| 人妻激情综合| 久99999热视频在线观看免费| 51精品国自产在线| 99热碰碰| 久久精彩视频| 国产性爱色| 91chinese 在线| 玖玖激情五月天| 超碰人人操人人干| 久久性爱视频这里只有精品| AA丁香综合激情| 专区无日本视频高清8| 激情综合丁香| 99九九在线| 色婷婷丁香五月高清在线| 天天激情夜夜干| 久久这里有精品视频| 激情五月天偷拍综合网| 99视频在线观看地址| 婷婷五月天丁香久久| 玖玖激情五月天| 国产中文亚洲欧美日韩性交| WWW五月| www,超碰| 亚洲色区17| 九九这里是免费的视频5| 欧美色婷婷| 伊人六月无码视频| 欧美99| 国产99精品免费视频| 日逼免费视频| 色婷婷电影网| 7超碰自拍| 久婷婷久草| 日本操B视频| 天天插天天插天天插天天插| 操操日韩| 久色资源网| 另类视频综合| 六月丁香色色色| 涩涩五月天综合| 五月天六月色| 东京热人妻一区二区三区在线| 久久99婷婷| 久久这里有精品视频在线免费观看| 夜夜 操无码| 日韩久久系列| sewuyue第四色| 天天肏视频| 精品九九久久| 久久婷婷五月天亚洲欧美| 99超级碰免费视频| 欧美久久网| 欧美色综合天天久久综合精品| 亚洲激情| 99久久极情精品一区| 丁香五月性| 色婷婷色九月| 久久伦乱| 一逼色综合| 99热只有精| 精品无码久久久久久久久| 色啪影院| 九九成人| 亚洲成人色五月天| 色婷婷激情| 丁香六月婷婷久久综合| 婷婷五月激情中文字幕| 99热永久在线观看| 裸睡玩奶头(高H)| 色情综合| 爱射综合| 无码婷婷五月天| 激情五月天综合网| 婷婷五月天 偷拍| 九热精品| 能看的AV| 五月天婷婷青青草| 激情婷婷。| 丁香五月色五月婷婷宗合| 丁香五月天堂| 婷婷五月色综合| 三年高清大片免费观看国语| 六月婷婷激情小说网| AA片在线观看视频在线播放| 五月天激情无码高清| 日本黄色三级片内射| 亚洲AV激情五月综合网| 成人AV片播放| 色99色| 黄色成人网站在线播放| 99综合| 婷婷激情伍月网| 九九这里有精品| 思思热国产视频| 亚洲操操操| 99久久免费性爱视频`| 五月天婷婷丁香| 五月丁了香蕉综合| 97色一二三| 亚洲色爱综合| 日韩中文欧美| 色就色94欧美setu| 97色久| 人妻人人操| 99无码视频| 午夜爱爱爱成人| 北条麻妃伊人| 丁香六月综合激情| 久久婷婷五月天激情| 丁香8月手机综合| 天天做天天爱天天做| 丁香五月婷婷欧美成人色图| 激情深爱五月天| 婷婷区日本| 久久婷婷青青| 99热精品在这里| 亚洲色五月天是什么| 超碰在线50| 日日爱激情| 激情綜合網址| 桃色五月婷婷| 丁香九月激情| 亚洲五月色| 91丨九色丨白浆秘| 91九色PORNY大屁股| 精品无吗va视频免费观看| 日本久久人人| ai97re99一本| 色五月婷婷网| 五月丁香啪啪啪| 五月婷婷色综图片| 天天色图| 欧美成人日韩| 婷婷9月天| 婷婷情色五月天| 色国产五月| 日本99色| 亚洲123区高清入口| 色婷婷av在线观看| 综合网天天| 激情六月丁香综合| 好看的国产精品| 久久无码成人| 夜夜穞天天穞狠狠穞AV美女按摩| www.91在线观看| 开心久久爱五月天| 欧洲99视频在线| 二区成人视频| 丁香五月婷婷社区| 成人午夜天| 九九九九九九综合| 丁香六月天婷婷色| 国产露脸150部国语对白| 无码动漫av| 日本九九热| 99久久免费性爱视频`| 狠狠狠狠青草| 六月婷婷操逼| 激情五月婷婷色综合| 综合网激情| 天天日 天天草| 激情综合激情五月| 五月天婷婷成人网| 这里都是精品99| 丁香六月激情| www婷婷| A色色| 国产色色网址网站| 五月丁香久久网| 五月丁香好婷婷A片网| YW无码| 色噜噜狠狠色综无码久久合欧美| 日日夜夜干| 综合色五月| 激情丁香九九五月综合网| 色婷婷裸体色性在线| 99在线视频免费| 亚洲综合五月天婷婷丁香| 五月天大香蕉AV| 成人龟情网丁香五月| 五月色丁香| 国产欧洲欧洲精品久久| 91chinese在线| 九九成人| 亚洲狠狠爱婷婷| 狠狠爱深色婷婷综合| 性做爰1一7伦| 综久久久| 人人操97| 99成人网站| ri电影在线| 婷婷五月丁香狠狠| 五月激情综合深爱| 婷婷久久综合| 成人中文字幕在线| 大香蕉婷婷色| 电影《战争与艾拉》免费观看| 國語久久婷| 大香蕉99| 五月丁香狠狠爱婷婷综合| 日韩成人五月天| 人妻久久久久久久久妻久久久久| 夜夜爽天操| 国产26uuu| 九九在线精品| 99性爱| 99久久综合| 免费无码毛片一区二区A片 | 成人精品视频99在线观看免费| 久操婷婷| 中文字幕+中文在线| 丁香婷婷婷五月综合色情| 五月丁香九九| 激情婷婷丁香五月天小说| 99热这里只有在线播放| 人妻啪啪啪| 日韩在线99| 超碰在线看| 婷婷五月激情综合| 丁香玖玖视频大全| 国产毛片精品一区二区色欲黄A片| 婷婷久久网| 婷婷色婷婷| 婷婷九月久久| 激情婷婷丁香五月天小说| yazhouzonghesese| 激情久久久久久| 六月丁香久久| 成人在线综合| 五月天另类小说久久小说网| 久操综合| 亚洲色区17| 91avse| 九九热九九热精品| 超碰二区| 久热爱大香蕉在线蜜臀悦色| Av性爱网| 91在线日| 欧美操逼天堂| 色综合综合网| 99在线免费视频| 五月丁香A片| 天天肏视频| 五月婷婷九| 国产激情AV| 九月婷婷在线视频| 日韩砖区| 中文字幕成人| 五月天婷婷香蕉狠狠超碰综合| 亚洲综合丁香五月| 色色色五月婷婷| 1024在线观看免费视频| 99re热视频| 这里只有精品视频99| 97碰碰叉| va婷婷在线免费观看| 色综合久久天天综合网| www.久久爱.c n| 丁香五月网| 国产成人网| 婷婷五月天毛片| 玖玖精品视频99| 色婷婷影视| www.综合久久| 亭亭五月色男人| 熟女人妻一区二区三区免费看| 激情婷婷久久| 99九九在线精品热动漫| 久婷婷五月天影院| 亚洲六月婷婷| 五月婷婷九九久久| 中文av网| 婷婷九九视频| 波多野结衣AV无码Porn| 五月天婷婷激情小说电影| 亚洲五月婷婷| 天天舔夜夜操www com| 在线网黄| 九九热99视频| 丁香六月激情| 77799热| 夜夜操狠狠操| 激情婷婷综合| 无码视频国内精品久久久| 无码网| 亚洲综合九九| 五月丁香六月色情网欧美| www超碰| www.主妇. com| 97热视频| 久久最新色色色| 99视频内射三四| 大香蕉九九| 99操99| 啪啪91| 26uuu欧美日本| 婷婷六月丁香激情| 精品国产AV色一区二区深夜久久| 婷婷九月激情网| 国产成人精品亚洲线观看| 激情五月天在线观看色婷婷| 国产精品a无线| 亚洲一区先锋影音| 婷婷五月六月| 思思久久99| 婷婷丁香午夜综合影视| 色婷婷丁香AV综合| 久热大香蕉| 永久思思热在线| www超碰| 九六五月天婷婷| 开心五月婷婷伊人| 香蕉狠狠爱视频| 大香蕉手机视频| 婷婷久久五月| www.com五月天| 在线不卡中文字幕| 久热这里只精品| 99热99在线| 伊人婷婷色激情丁香| 亚洲成人丁香花| 99亚洲精品| 丁香五月亚洲激情婷婷射| 五月婷婷丁香| 欧美丁香五月夫妻天| 91免费看片| 亚洲激情在线| 丁香五月天综合| 综合久| 欧美婷婷色| 欧美精品18| 天天干,天天舔| 天天射天天操天天干| 激情综合激情综合| 五月丁香婷婷六月天| 无码一级片| 激情五月天在线视频| 最新精品视频99| 国产成人在线不卡AV| 超碰在线人妻| 97超碰在线免费观看| 丁香婷婷久久| 人人操人人干AV| 中字幕视频在线永久在线观看免费| 亚洲视频伍月婷婷| 久久激情综合| 五月天另类视频| 五月四色色| 外国碰视频网站97| 五月黄色婷婷| 江苏少妇性BBB搡BBB爽爽爽| 97人人操人人干| 婷婷激情5月| 爱操人妻| 激情综合99| 成人国产网| 噜噜视频| 色婷婷丁香五月| 综合色色色| 2020久久婷婷五月| 99开心五月五月丁香激情| 2021日韩无码| 五月丁香婷婷欧美色图视频五月丁香777电影| 久久精品4| 激情五月天小说| 狠狠干2007| 超碰在线观看99| 色444综合网| 亚洲精品无AMM毛片| 婷婷色中文字幕| 婷婷五月天伊人| 婷婷五月天第四色| 亚洲视频在线观看| 丁香成人五月天| 成人综合视频在线| 色欧美影院| 99热九九热| 久久人妻高清中文| 99精品在线下载| 久热这里只有精品在线观看 | 丁香婷婷社区| 五月人妻婷婷视频| 饮料下药迷倒漂亮女同事强干| 国产精品久久久久久五月天加勒比| 人人干人人干骚美女| 九九精品综合| 久久丁香| 热九九在线| 狠狠色丁香| 色九月欧美| 天堂爱啪啪| 亚洲狠狠婷婷| 女操碰| 99a级片| 99日本视频| 色婷婷女优有码五月亭| 啪色综合| 九九精品热| 日本人妻A片成人免费看片| 天天爱天天做天天操| 婷婷五月天激情网站| 日日操,夜夜撸| 婷婷射图五月天| 亚洲婷婷乱乱丁香| 五月丁香婷婷五月| 国产操碰| 五月天婷婷综合网| av在线免费播放观看| 五月丁香久久激情综合| 婷婷 丁香 久久| 丁香婷婷综合激情五月色| 久青操| 久久久婷婷婷| 五月天婷婷久久综合| 99热精地址| 日本色爽| 五月天婷婷基地| 99ER热精品视频| Aaa久久| 婷婷久久18| 六月婷婷七月丁香| 99热思思在线观看| 色J香五月天| 久婷五月| 婷婷色五月婷婷姐妹| 丁香五月 综合| 日韩六十路91性交电影| 综合色播| 国产真人做爰视频免费| se婷97| 亚洲乱码在线观看| 99久久综合网| Av九九| 99精品偷自拍| 亚洲成人另类| 激情五月综合ì香亚洲| 丁香婷婷激情六月五月开心| 五月天激情国产综合婷婷婷| 精品无码片| 色综合久久天天综合网| 人妻操在线看| 欧美人妻一区二区| 色月丁| 婷婷五月在线免费| 开心激情综合| 国产肥白大熟妇BBBB视频| 亚洲国产另类av| 久热这里只有精品性色AV| 淫水导航| 99性爱| 日韩在线一级| 极品人妻videosss人妻| 色五月天在线观看| www.五月天色色色| 欧美色欲色欲天天天www| 五月伊人网| 91丨九色丨国产| 五月开心婷婷极品激情| 色五月综合资源推荐| 天天射影院| 色婷婷激情| 亚洲国产精品VA在线看黑人| 97视频91| 五月丁香成人小说| 丁香五月 性爱| 天天色天天搡| 操逼六区| 婷婷综合九月| 大陆肏屄视频| 亚洲成人AV高清字幕| 色5月婷婷| 开心五月激情网| 久久久久久五月天| 婷婷五月开心中文字幕色| 免费黄色视频网址| 精品牛仔裤超碰| 国产视频婷婷| 禁片二区| 99热这里只要精品免费| 九月丁香八月婷婷加勒比| 国产乱码久久| 26uuu国产| 久久99久久99精品免观看粉嫩| 婷婷五月天综合中文| 99热思思| 五月情婷婷五月| 色五月av| 第二色AⅤ| 色偷偷综合| 99色在线视频| 五月婷在线观看| 91热爆在线| 色五月婷婷在线| 欧美三级黄色片久久| 狠狠五月天| 在线不卡的视频| A久网| 亚洲黄网在线| www五月天com| 国产激情在线| 色五月丁香五月| 久久人人九九| 婷婷丁香五月天激情| 99色激| 91精品国产91久久久久青草| 丁香五月婷婷啪啪| ay2区| www.婷婷六月天| 婷婷五月丁香狠狠| 香蕉久操| 五月久视频| 九九综舍久久| 婷婷五月天激情文学| 伊人春天av| 激情小说五月天| 高清无码 一区 二区 三区| AA片在线观看视频在线播放 | 99久久九九视频| 都市激情小说婷婷| 97碰在线免费观看| 五月丁香五月综合欧美| 婷婷性爱无码视频| 欧洲区自拍| 日韩AV片| 色欲香综合网| 亚洲热久| 日日夜夜天天| 99热这里只有精品无码| 丁香五月婷婷激情尤物| 久久五月视频| 无码任你操| 五月丁香性爱| 丁香六月丁香婷婷激情| 2022人人操人人看| 五月天激情四射| 凹凸7777操操操| 182tv992tv人之初午夜免费观看| 丝袜激情网| 久久这有这里精品| 久久五月天激情婷婷| 五月停亭六月,六月停亭的英语 | WWW五月| 成人五月丁香社区| 九九日伊人| 天天干,夜夜爽| 五月丁香在线视频观看| 婷婷五月激情综合啪啪| 开心五月婷婷| 99免费视频| 天天色综合色| 99噜噜| 欧美A级成人婬片免费看理论| 婷婷色五月婷| 亚洲久久婷婷丁香五月天| 中文字幕成人影视| se色婷婷视频| 欧美激情VA永久在线播放| 婷婷.com| 久久这里只有精品07| 99热在线看| 国产成人片| www.91婷婷| 亚洲九九在线| 丁香婷婷精品视频| 射久久丁香五月| 三年高清大片免费观看国语| 丁香五月另类色婷婷麻豆| 国产日日操夜夜操的肉棒视频| 婷婷五月丁香久久| 777久久久| av在线播放网站| 99爱在线视频| 亚洲无码播放| 六月婷婷五月丁香| 人人爱人人摸人人澡| 99热网站在线观看| 性爱综合网| www.久热| 久色五月| 激情九九综合网| 婷婷九月激情| 四色综合网| 色婷婷久久综| 欧美伊人9| 1024久婷| 婷婷丁香色五月天| 久久久国产精品黄毛片| 深爱激情五月天| 色吧综合网| 9热在线观看| 欧美激情综合五月色丁香| 五月丁香色婷| 五月婷婷免费| 五月四色色| 天天肏天天插| 狠狠色婷婷丁香六月| www.天天色综合| 另类图片色五月| 美女精品一级不卡视频| 久久婷婷五月天激情四射| 婷婷五月天美女| 成人精品视频99在线观看免费 | site:ornaments52.com| 69er小视频| 激情婷婷亚洲五月| 日韩AAA| 色婷激情网| 国产精品色一哟哟| 五月激情婷婷六月| 色噜噜狠狠色综无码久久合欧美| 丁香五月在线播放| 欧美综合五月丁香六月婷| 丁香伊人网| 大香蕉啪啪啪| 天天爽天天| 9热网站| 1级欧美日韩| 丁香婷婷综合色五月激情国产基地| 五月天堂六月丁香亚州中文字幕久久| 青青草Avb在线| 九热久| 天天五月丁香五月| 五月丁香婷婷激情| 五月色色激情网| 久久视频婷婷| 丁香五月成人网| 久久久亚洲精品一区二区三区浴池 | 久久人妻www| 在线视频激情网站| 思思精品视频| 潘金莲AAAAAAAAAA| av在线免费网站| 超碰狠狠操| 久久精品性爱| 国产欧美日韩综合精品一区二区 | 久久婷婷丁香五月一二三| 深爱五月天 开心网| 久久人妻伦理| 五夜婷婷| 亚洲欧洲另类图片| 久久婷婷丁香五月宗合| 久久久思思热| 婷婷六月丁香欧美视频在线| 综合色五月| 日本综合99| 久久3p| 综合狠狠干| 婷婷免费视频| 嫩草AV久久伊人妇女超级A| 中文字幕高清av| 99久久9| 亚洲欧州色情在线观看| 久久久久9| 婷婷丁香91综合| 日韩综合久久| 蜜臀嫩草| 亚洲操B| 另类图片五月天激情| 欧美色图45678| 99久久玖玖| 日本三级中国三级99人妇网站| 九热视频免费观看| 婷婷五月天激情综合| 婷婷狠狠18禁久久| 1024欧美看片| 激情网五月天| 大香蕉 伊人夜| 五月伊人视频在线看| 香蕉国产2013| 青青999| 丁香婷婷久久激情| 97超碰人人操| 精品一区二区三区三区| 亚洲AAA| 激情综合啪啪| 五月天婷婷久草丁香| 思思热精品免费视频| 欧美在线干| 午夜丁香六月婷| www.夜夜騎夜夜狠| 五月丁香六月激情综合| 九九99九九99九九99视频网| 99国产这里只有精品| 成人片黄网站色大片免费毛片 | 思思国产99| 国产精品久久久久久久久久久久 | 99热在线这里| 一本婷婷丁香久久 | 五月丁香本色在线观看| 三区激情四射av| 亚洲中文字幕AV| 久久久久98| 色播播五月| 五月激情综合婷婷| 狠狠干伊人| 免费看欧美成人A片无码| 国产精品久久99| 亚洲成人免费电影| 99爽视频| 色99在线视频| 六月丁香av| 久久激情五月天| 蜜桃婷婷狠狠久久| 五月天综合激情网| 另类伊人婷婷| 色色免费网站| 五月丁香久久网| 91seav| 丁香五月综合婷婷| 日本色色色| 思思热这里只有精品视频666| 爱操人妻| 五月丁香六月婷婷亚洲天堂网站| 能直接看的av网站| 六月五月丁香五月欧美| 九九久久精品| 99在线视频精品| 色色网站毛片| 色婷婷五月天不卡| 久久九九九九| 日韩久久色| 91精品综合久久久久久五月丁香| 婷婷五月天开心网| 亚洲精品乱码久久久久99| 中文成人在线| 天天肏天天爽夜夜爽| 67194线路二在线观看| 99热在线成人网站| 97婷婷狠狠久久综合9色| 亚洲婷婷91丁香| 婷婷五月天成人导航| 婷婷色婷婷| 亚洲久热无码| 欧美成人va| 国产97色在线| 亚洲久热| 26uuu另类亚洲欧美日本一| 月丁香久久久| 婷婷丁香成人| 丁香五月激情站| 五月激情久久| 色色色99| 久久婷婷五月激情网站| 久久五月天丁香| 丁香五月网址| 久久久er热| 亚洲123区高清入口| 99九九精品视频| 嫩草综合网| 欧美色色日韩| 狠狠干综合| 九九热这里只有精品31| 九九婷婷综合| 亚洲精品V天堂中文字幕| 色五月人妻| 67194国产| 久操香蕉| 欧美色图天堂网| 婷婷五月天com| 91色在线| 久久婷婷青青草| 日韩色五月| 丁香五月深爱五月婷婷| 婷婷网影院| 任你躁XXXXX麻豆精品| 婷婷久久综合| 久热最新视频| 婷婷久久五月丁香| 久久加勤综合| 99亚洲精品视频| 婷婷五月婷| 26UUU成人网| 啪啪六月婷婷| 五月J香蕉婷婷| 99九九久久| 色婷婷色五月天| 六月色日韩| pom538精品视频| 超碰亚洲欧美| 青青草五月天| 99热成人| 丁香网五月天激情| 精品无码av丁香五月激情| 成人在线高清| 婷婷五月激情基地| 99热精品在线播放| 久久婷.com| av五月天婷婷丁香| 五月天成人在线视频丁香| 国产精品色婷婷99久久精品| 九九热狼人| 天天爽天天干| 99天堂网| 91人人操人人爱| 97碰 在线视频观看| 色色色色热| www.五月天婷婷| 激情色视频| 五月天久久婷婷婷| 蜜桃婷婷五月| 99视频内射三四| 色婷婷免费视频| 久久久九九九 99| 操B无码视频国语| 五月天激情小说网| 青青草五月天| 国产成人99久久亚洲综合精品| 成年人丁香五月| 婷婷五月丁香六月天亚洲综合| 人妻操逼| 丁香五月天人体| 综合五月天亚洲婷婷| 67194成I人在线观看线路1 | 五月天色色激情综合| 在线另类视频| 丁香五月婷婷偷拍| 97操碰人人| 99精品女人天堂| 吾爱AV导航| 五月丁香婷色| 丁香五月大香蕉AV| 亚洲色婷婷五月天| 久色五月天| 色婷婷久久综| 大香蕉婷婷色| 国产成人网站在线观看| 色色婷婷五月天| 在线视频你懂得| www.色五月| 热99精品视频在线观看| 色欲婷婷夜夜| 色婷婷五月天天天天天天天天天| 色婷婷丁香五月观看| 色国产五月| 欧美综合五月丁香五月天| 五月天婷婷高清无码| 久久激情五月天| 99国产在线| 九九色图| 九九Av| 99操视频| 日本V在线观看不卡视频网站| 九九综合九| 二区成人视频| 五月天伊人综合| 男女99免费视频| 日本本土色网第一区| 内射丰满人妻| 亚洲99精品欧美一区| 偷偷操99| 99色| 99人人爽| 九九综舍久久| 精品视频这里只有精品| 天天插天天干天天舔| 99热a片免| 超碰在线国产| 日韩黄在免| 美国天天操无码| 亚洲精品视频在线播放| 婷婷五月激情丁香激情| 怡红院一二三| 亚洲色啪| 五月婷婷久久大片| 日本97在线| eeuus五月婷| 无码网站视频| 亚洲热久久| 免费91久久精品| 婷婷久久综| 丁香五月激情六月综合| 日本九婷婷| 99无码视频| 性爱激情小说AV五月丁香花| 久久这里只精品66| 日本韩国视频在线观看社区免费的9| 五月天色色色网| 91viP在线看| 激情九九综合网| www婷婷| 在线中文AV| 五月丁香成人| 五月婷婷九| 久热伊人| 影院久久久| 丁香女人五月天| 五月婷婷色五月| 91精品综合久久久久久五月丁香| 久热婷婷在线视频| 亚洲综合网激情五月天| 成人电影在线免费试看| www.婷婷com| 久er免费视频| 激情www| 亚洲国产精品二二三三区| 任我鲁这里有精品视频| 中文字幕久久一区二区三区| 五月丁香五月综合欧美| 五月丁香九九| 五月婷婷丁香在线视频| 婷婷五月激情中文字幕| 亚洲色色五月天| 久热中文字幕在线线观看| 天天日,天天干,天天操| 成年视频免费观看| 嫩BBB槡BBBB搡BBBB视频| 99热9| 亚洲九九婷婷| www色五月天| 精国产品一区二区三区A片| 玖玖在线视| 五月丁香六月在线| 五月婷婷 婷婷五月 一区二区 久久久| www色色com| 五月婷婷九九热| 秋霞网在线免费基地五月婷婷丁香| 秋霞三级影视资源| 99A片| 色5月婷婷色| 色五月综合| 五月 婷 久| 五月天婷婷久久综合| 婷婷综合网站| 五月丁香| 色综合色色| 日韩狠狠色| 97色97干| 久久综合婷婷五月| 色五月激情网| 久久99jiu9| 色婷婷六月丁香综合欲精品| 超碰婷婷五月| 丁香五月天啪啪激情综和网| 国产一二区爆乳_1国产日韩一区二区三-成人AV | 狠狠色丁婷婷日日,伊人激情综合网| 四虎成人精品永久免费AV九九| 99热资源在线| 精品久热69| 九九综合网色全集 | 中文字幕丰满乱孑伦无码专区| 91爱啪啪| 色狠狠色| 九九视频在线| 婷婷五月天激情网址| 99A片| 婷婷亚洲五| 五月激情天| 婷婷五月亚洲激情| 九九亚洲视频| 五月丁香婷婷激情久久| AA片在线观看视频在线播放| 五月天婷婷在看| 欧美性生交XXXXX无码小说| 五月丁香少妇网| 高清资源站日A美A欧亚…| www.minyis.com【JT】国内CDN落地页保证转化QQ2101460746 | 国产欧美日韩综合精品一区二区| 丁香五月色综合色播五月| 日本熟妇乱妇熟色A片蜜桃| 深爱网深爱综合网| 综合色七七| 26uuu精品国产| 久久婷婷五月综合啪| 人妻熟妇国产精品| 国产伊人五月天| 日韩限制级大尺度黑料泄密大尺度视频一区二区在线观看 | 激情二色月| 五月婷婷 自拍| 91狠狠色色丁香婷婷综合久久| 五月婷婷黄色| 99在线精品观看99| 伊人婷婷五月天av| 五月天婷综合| 天天骑天天操| 婷婷五月激情欧美大胆视频| www.九九婷婷| 中文字幕综合网| 狠狠狠狠狠狠草| 综合久久六月| 9+1视频网址| 五月婷在线色视频| 婷婷五月中文字幕| 91日本在线观看| 区区欧美你爱| 五月婷婷啪啪啪| 色五月婷婷网| 性色五月天| 五月婷丁香花| www...com黄在线观看| 9久热免费视频99| 色五月天中文字幕| 久久五月天免费网站| 丁香六月啪啪| 天天色官网| 久99久视频| 五月丁香色停停啪啪啪| 六月婷婷久久| 激情二色月| 国产精品操| 97一区二区| 丁香五月婷婷激情网| 青青福利网| 操97| 婷婷婷五月香蕉| 天天干电影| 六月综合婷婷开心伊人| 五月天综合缴情网网站0| 五月色婷丁香| 亚洲操人| 操操自拍| 色情免费视频播放| 另类天堂| 婷婷成人在线| 疯狂做受XXXX高潮A片| 日本美女上人| 综合五月天完整| 日日干日日色| 999精品乱码77777| 色五月色开心开心五月| 日韩成人电影AV| 激情五月天开心| 五月天激情.com| 婷婷欧美色| 综合激情在线| 1024久婷| 国产成人AV不卡| 免费亚洲婷婷| 久久女人天堂| www色五月| 五月丁香激情婷婷综合| 六月婷婷网| 久热综合| 国产精品汇聚精彩第二页 - 高清完整版在线 - 青蛙AV | 久99精品视频| 人妻激情久久| 欧美在线| 天天爽天天日| 91avse| 五月天综合在线观看视频| 狠色狠色综合久久| 丁香婷婷免费| 五月丁色AV| 狠狠色色| 丁香六月婷婷开心| 99riAV国产精品视频| 欧美久人人| 九九精品re免费视频| www.色色色com| 99热在线观看| 人人摸人人干| 国产成人网| 久久精彩视频| 丁香五月成人社区| a在线观看| 欧美久久婷婷| 激情文学久久| 久操热线| 99日本精品视频热| 亚洲超碰在线| 五月丁香婷婷色| 五月婷婷色情| 久热超碰| 婷婷五月天伊人| 激情五月婷| 中文字幕成人| 五月天激情小说| 国产午夜精品AV一区二区麻豆| 久热9| 亚洲精品字幕在线观看| 欧美电影在线播放| 久久久国产精品黄毛片| 草了bav视频在线观看| 中文字幕成人网站| 不卡成人免费| 香蕉五月婷婷| 二级黄色毛片| 99久在线精品| 俺来也综合网精品一区| 日日干日日色| 超碰AAAAAAV| 丁香久久久| 五月婷在线| 亚洲天堂久久| 五月丁香婷婷啪啪| 中文字幕无码AV| 色综合久久88色综合天天| 亚洲午夜一区二区| AV在线大香蕉| 国产成人网| 性色做爰片在线观看WW| 综合色七七| 人妻激情视频| 久久天天天| 日逼免费视频| 久久五月丁香| 夜夜操夜夜操| 五月开心播播网| xxxx五月天色色| 丁香婷婷基地| 插插网爽妇五月丁香| 欧美丁香婷婷五月天| AA久久| 欧美人与性动交CCOO| 丝雨一区二区| 久久五月网| 五月丁香色五月| 深爱五月激情| www婷婷| 天堂网操| 五月天色图| 五月激情基地| 婷婷性爱网| 欧美性色A片免费免费观看的 | 都市激情蜜桃婷婷五月天 | 欧美色色色色色色| 色黑鬼导航| 婷婷五月天在线综合| 9久精品视频| 色婷婷网大全在线| 26.uuu丁香五月婷婷| 五月天婷婷丁香蜜桃91| 色九九九九| 激情内射人妻1区2区3区| 综合五月丁香六月婷婷| 婷婷久久视频| 婷婷丁香五月婷婷| 中字幕视频在线永久在线观看免费 | 五月丁香综合| 久久一级片| 天天插天天日天天爽| 激情婷婷久久| AV中文网| 人人爱操| 天天干夜夜想| 午夜精品人妻无码一区二区三区| 亚洲成人网站在线播放| 99精品无码网站| 伊人激情影院| 99九九中文字幕视频| 激情WWW| 天天色宗合| 婷婷久久图片| 九九热在线观看视频网站| 激情六月丁香| www.九月婷婷丁香.com| 超碰超碰在线| 五月色婷| 9 1 A v久久久| 欧洲激情五月天| 五月婷婷久久综合| 99热观看| 99热在线中文字幕| 激情伊人五月天| 嫩草国产| 国产精品A片| 噼里啪啦在线观看免费完整版视频| 99精品97| 亚洲欧美一区二区三区爱爱动图| WWW.桔色成人.COM| 九九99久久| WWW免费视频碰碰碰碰| 91N 一起草| 婷婷娱乐丁香综合网| 99热精品在线| 91色五月| 丁香色综合| 欧美色碰| 终合激情网| 182.t午在线观看| 丁香五月天精品| 搡BBBB搡BBB搡18| 深爱婷婷基地| 日本不卡中文字幕| 激情五月色综合网| 精品无吗va视频免费观看| 羞羞嫩草视频| 操一操| 婷婷丁香18| 免费试看小视频 99| 无码人妻精品一区二区蜜桃色欲| 九九这里只有精品在线视频| 9久久精品| 久久婷婷热| 九月婷婷久久久| 婷婷欧美综合| 五月婷婷色影院| 六月丁香大香蕉| 亚洲欧洲国产精品| 久久久ww| 五月天大香蕉| 婷婷五月天在线看| 色婷婷香蕉| 天天噜天天爱| 超碰人人摸人人操|