程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> WPF Bug清單之(10)——CheckBox在不同主題下具有不同的行為

WPF Bug清單之(10)——CheckBox在不同主題下具有不同的行為

編輯:關於.NET

我們都知道Window有多種主題(Theme)。一般情況下,顯然我們會希望不同 主題下,我們的應用程序的行為不會有變化。或者說,我們不希望為了特定的主 題,為控件寫特定的邏輯。然而不幸的是,.NET Framework裡一些控件自帶的主 題就存在問題,使得我們不得不在使用時,為這個控件在特定的主題下特殊處理 。

下面舉一個例子。在 ListBox裡放CheckBox,組成一個CheckBoxList應該是 一個比較常見的應用。從理論上來說,在WPF裡最簡單的方式就是在 ListBox的 ItemTemplate裡或是ItemContainerStyle裡放一個CheckBox就可以了。

但是實際上,在做這個簡單的CheckBoxList的時候,會遇到一個又一個的問 題。首先重申一下文本的意圖,怕自己又沒有說明白誤導大家。

本文不是討論

CheckBoxList

裡的藍條問題,而是在討論CheckBox

在不同主題下的不同行為的問題。CheckBoxList

僅僅是個例子。

先來看看效果圖。

圖1. 兩種主題下的CheckBox

在上圖中,左側是Classic主題下的CheckBox。右側是XP默認的Luna主題下的 CheckBox。

問題1. CheckBox的IsChecked狀態與ListBoxItem的IsSelected狀態不同步。 如果你想保留選中時的藍條,那麼比較好辦,把這兩個屬性Binding到一起就可 以了。如果你不想要那個選中時的藍條,會稍稍復雜一些。解決方案很多,就不 贅述了。示例程序中,為減少干擾,不對這個問題進行解決。

問題2. CheckBox所在的Item被選中時,為藍色。CheckBox裡的文字為黑色, 這個與ListBoxItem的默認顏色行為不一致。為了讓 CheckBox在被選中時文字為 白色並不難,寫個Binding就OK了。這個根本不是問題,但是解決這個問題,造 成了下面的問題,才是主要問題。(當然,如果你隱藏了藍條,就沒有任何問題 。)

問題3. 這個是這篇文章的主要議題,看看下面幾個圖就知道了。我們對兩邊 的CheckBoxList做同樣的操作。先來右邊的。

圖2. 選中最後一個CheckBox

圖3. 點擊剛才選中CheckBox邊上的空白,使其選中

注意,這裡CheckBox裡的勾還是可見的。很費話是吧,怎麼可能不見?下面 讓你來見識一下,Classic主題下的勾就看不到了。跟沒有選中一樣。

圖4. 選中經典CheckBox,勾可見

圖5. 點擊空白,選中它,勾不見了

再給個提示,注意圖2和圖3,之間的變化,Item3被選中之後,變成了白色。 而勾的顏色沒有變。再來看圖4和圖5。

應該已經猜到了吧?沒有錯,Classic主題下,CheckBox裡的勾也成了白色的 。

熟悉WPF的人應該也已經猜到了,這個是由於不同主題下,CheckBox的默認 Template的實現不同所導致的。下面是CheckBox在不同主題下的代碼。(直接來 自於Blend,根本來源是.NET Framework裡的PresentationFramework.Classic和 PresentationFramework.Luna兩個 DLL。)

<ResourceDictionary
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:Microsoft_Windows_Themes="clr- namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Clas sic">
     <!-- Resource dictionary entries should be defined here. - ->
     <Style x:Key="CheckRadioFocusVisual">
         <Setter Property="Control.Template">
             <Setter.Value>
                 <ControlTemplate>
                     <Rectangle Stroke="Black" StrokeDashArray="1 2"  StrokeThickness="1" Margin="14,0,0,0"  SnapsToDevicePixels="true"/>
                 </ControlTemplate>
             </Setter.Value>
         </Setter>
     </Style>
     <Style x:Key="EmptyCheckBoxFocusVisual">
         <Setter Property="Control.Template">
             <Setter.Value>
                 <ControlTemplate>
                     <Rectangle Stroke="Black" StrokeDashArray="1 2"  StrokeThickness="1" Margin="1" SnapsToDevicePixels="true"/>
                 </ControlTemplate>
             </Setter.Value>
         </Setter>
     </Style>
     <Style x:Key="ClassicCheckBoxStyle" TargetType="{x:Type  CheckBox}">
         <Setter Property="FocusVisualStyle" Value="{StaticResource  CheckRadioFocusVisual}"/>
         <Setter Property="Foreground" Value="{DynamicResource {x:Static  SystemColors.WindowTextBrushKey}}"/>
         <Setter Property="Background" Value="{DynamicResource {x:Static  SystemColors.WindowBrushKey}}"/>
         <Setter Property="BorderBrush" Value="{x:Static  Microsoft_Windows_Themes:ClassicBorderDecorator.ClassicBorderBrush}"/& gt;
         <Setter Property="BorderThickness" Value="2"/>
         <Setter Property="Padding" Value="2,0,0,0"/>
         <Setter Property="FocusVisualStyle" Value="{StaticResource  EmptyCheckBoxFocusVisual}"/>
         <Setter Property="Template">
             <Setter.Value>
                 <ControlTemplate TargetType="{x:Type CheckBox}">
                     <BulletDecorator SnapsToDevicePixels="true"  Background="Transparent">
                         <BulletDecorator.Bullet>
                             <Microsoft_Windows_Themes:ClassicBorderDecorator  x:Name="CheckMark" Background="{TemplateBinding Background}"
                                 BorderBrush="{TemplateBinding BorderBrush}" BorderStyle="Sunken"  BorderThickness="{TemplateBinding BorderThickness}">
                                 <!-- The following Path Binding Fill to Foreground of  Templated parent, which is different from Luna's template -- >
                                 <Path x:Name="CheckMarkPath" Fill="{TemplateBinding Foreground}"  FlowDirection="LeftToRight" Margin="1,1,1,1"
                                         Width="7" Height="7" Data="M 0 2.0 L 0 4.8 L 2.5  7.4 L 7.1 2.8 L 7.1 0 L 2.5 4.6 Z"/>
                             </Microsoft_Windows_Themes:ClassicBorderDecorator>
                         </BulletDecorator.Bullet>
                         <ContentPresenter HorizontalAlignment="{TemplateBinding  HorizontalContentAlignment}" Margin="{TemplateBinding Padding}"
                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"  SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"  RecognizesAccessKey="True"/>
                     </BulletDecorator>
                     <ControlTemplate.Triggers>
                         <Trigger Property="IsChecked" Value="false">
                             <Setter Property="Visibility" TargetName="CheckMarkPath"  Value="Hidden"/>
                         </Trigger>
                         <Trigger Property="IsChecked" Value="{x:Null}">
                             <Setter Property="Background" TargetName="CheckMark"  Value="{DynamicResource {x:Static  SystemColors.ControlLightLightBrushKey}}"/>
                             <Setter Property="Fill" TargetName="CheckMarkPath"  Value="{DynamicResource {x:Static  SystemColors.ControlDarkBrushKey}}"/>
                         </Trigger>
                         <Trigger Property="IsPressed" Value="true">
                             <Setter Property="Background" TargetName="CheckMark"  Value="{DynamicResource {x:Static  SystemColors.ControlBrushKey}}"/>
                         </Trigger>
                         <Trigger Property="IsEnabled" Value="false">
                             <Setter Property="Background" TargetName="CheckMark"  Value="{DynamicResource {x:Static  SystemColors.ControlBrushKey}}"/>
                             <Setter Property="Fill" TargetName="CheckMarkPath"  Value="{DynamicResource {x:Static  SystemColors.GrayTextBrushKey}}"/>
                             <Setter Property="Foreground" Value="{DynamicResource {x:Static  SystemColors.GrayTextBrushKey}}"/>
                         </Trigger>
                     </ControlTemplate.Triggers>
                 </ControlTemplate>
             </Setter.Value>
         </Setter>
     </Style>
</ResourceDictionary>

代碼1. Classic主題下CheckBox的默認Template

<ResourceDictionary
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:Microsoft_Windows_Themes="clr- namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Luna ">
     <!-- Resource dictionary entries should be defined here. - ->
     <LinearGradientBrush x:Key="CheckRadioFillNormal">
         <GradientStop Color="#FFD2D4D2" Offset="0"/>
         <GradientStop Color="#FFFFFFFF" Offset="1"/>
     </LinearGradientBrush>
     <LinearGradientBrush x:Key="CheckRadioStrokeNormal">
         <GradientStop Color="#FF004C94" Offset="0"/>
         <GradientStop Color="#FF003C74" Offset="1"/>
     </LinearGradientBrush>
     <Style x:Key="LunaCheckBoxStyle" TargetType="{x:Type  CheckBox}">
         <Setter Property="Foreground" Value="{DynamicResource {x:Static  SystemColors.ControlTextBrushKey}}"/>
         <Setter Property="Background" Value="{StaticResource  CheckRadioFillNormal}"/>
         <Setter Property="BorderBrush" Value="{StaticResource  CheckRadioStrokeNormal}"/>
         <Setter Property="BorderThickness" Value="1"/>
         <Setter Property="FocusVisualStyle" Value="{StaticResource  EmptyCheckBoxFocusVisual}"/>
         <Setter Property="Template">
             <Setter.Value>
                 <ControlTemplate TargetType="{x:Type CheckBox}">
                     <BulletDecorator SnapsToDevicePixels="true"  Background="Transparent">
                         <BulletDecorator.Bullet>
                             <Microsoft_Windows_Themes:BulletChrome  Background="{TemplateBinding Background}"  BorderBrush="{TemplateBinding BorderBrush}"
                                  BorderThickness="{TemplateBinding BorderThickness}"  IsChecked="{TemplateBinding IsChecked}"
                                  RenderMouseOver="{TemplateBinding IsMouseOver}"  RenderPressed="{TemplateBinding IsPressed}"/>
                         </BulletDecorator.Bullet>
                         <ContentPresenter HorizontalAlignment="{TemplateBinding  HorizontalContentAlignment}" Margin="{TemplateBinding Padding}"
                                 VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                 SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"  RecognizesAccessKey="True"/>
                     </BulletDecorator>
                     <ControlTemplate.Triggers>
                         <Trigger Property="HasContent" Value="true">
                             <Setter Property="FocusVisualStyle" Value="{StaticResource  CheckRadioFocusVisual}"/>
                             <Setter Property="Padding" Value="2,0,0,0"/>
                         </Trigger>
                         <Trigger Property="IsEnabled" Value="false">
                             <Setter Property="Foreground" Value="{DynamicResource {x:Static  SystemColors.GrayTextBrushKey}}"/>
                         </Trigger>
                     </ControlTemplate.Triggers>
                 </ControlTemplate>
             </Setter.Value>
         </Setter>
     </Style>
</ResourceDictionary>

代碼2. Luna主題Normal配色方案下,CheckBox的默認Template

從上面兩段代碼可以看出,Classic主題下的CheckBox的勾的顏色綁定到了 CheckBox本身的Foreground屬性上,但是Luna主題下的沒有這樣做(Luna下的 CheckBox的勾是用BulletChrome畫的)。也許微軟這樣做有自己的考慮,但是從 目前的結果來看,沒有帶來實現的好處,卻帶來了不小的麻煩,因為這個小問題 並不是想象中那麼容易完美解決的。

給出幾個方案。

1. 不把CheckBox的Foreground與ListBoxItem的Foreground綁定。然後解決 CheckBox黑色文字與藍條的沖突問題。方案之一就是改變藍條成灰條,橙條,顏 色隨你。

2. 重寫Classic主題下CheckBox的Template,可以,首先這個Template的代 碼不算少,而且你還要寫代碼去在程序啟動時判斷是否要加載這個特殊的 Template。這個還要讀注冊表。

3. 算了,我不要藍條還不行嗎?有時客戶或是公司的UX和QA會不同意,他們 不會因為你不好做就原諒你的。除非你很能忽悠。

好了,問題講完了。不過這裡的CheckBox只是一個例子,WPF裡類似的問題還 是不少的。比如很多控件的FocusVisualStyle根本無效。這個將在之後的文章中 介紹。

已經給WPF找了10個Bug個了,當然是按自己的標准找的Bug(被QA、UX和客戶 磨練出來了),也許有人不接受,認為這些不算是 Bug,但是討論這個實在沒有 什麼意義。這個系列文章的主要目標,是為了給計劃使用和正在使用WPF進行項 目開發的人,一些提示,在自己遇到的陷阱邊上立個牌子,讓大家少走一些彎路 ,畢竟這些問題在微軟的文檔大都是沒有涉及到的,能達到這個目標就足夠了。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved