窗口阴影
实现
因项目需要给用户一定提示,设计师建议在鼠标进入时显示窗口阴影,离开时取消窗口阴影。
很自然,都会想到直接在窗口的内容或者自定义窗口的最外层元素上加效果。示例如下:
<Grid> <Grid.Effect> <DropShadowEffect x:Name="ShadowEffect" BlurRadius="15" Direction="270" Opacity="0" ShadowDepth="15" Color="Yellow" /> </Grid.Effect> </Grid>
很不幸,上述方法不会生效,原因何在?窗口的非工作区(也就是Grid的四周)的渲染是由系统控制。虽然不清楚是绘出来了被遮住了还是根本没绘出来,但是告诉了我们此路不通。
还好我们用的是WPF,可以自己定义模板和样式。于是简单测试了一下就发现了方案。下面就是我使用的样式一个示例(省略了模拟的控制按钮和其他的一些功能)
<Style x:Key="ShadowWindow" TargetType="Window"> <Setter Property="OverridesDefaultStyle" Value="True" /> <Setter Property="AllowsTransparency" Value="True" /> <Setter Property="WindowStyle" Value="None" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Window"> <Border Padding="15"> <Border Name="BdrShadow"> <ContentPresenter ContentSource="Content" /> <Border.Effect> <DropShadowEffect x:Name="ShadowEffect" BlurRadius="15" Direction="270" Opacity="0" ShadowDepth="15" Color="Yellow" /> </Border.Effect> </Border> </Border> <ControlTemplate.Triggers> <EventTrigger RoutedEvent="MouseEnter"> <BeginStoryboard> <Storyboard> <DoubleAnimation Duration="0:0:1" Storyboard.TargetName="ShadowEffect" Storyboard.TargetProperty="Opacity" To="0.5" /> </Storyboard> </BeginStoryboard> </EventTrigger> <EventTrigger RoutedEvent="MouseLeave"> <BeginStoryboard> <Storyboard> <DoubleAnimation Duration="0:0:1" Storyboard.TargetName="ShadowEffect" Storyboard.TargetProperty="Opacity" /> </Storyboard> </BeginStoryboard> </EventTrigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style>
需要注意的是必须将OverridesDefaultStyle和AllowsTransparency设为true。根据需要设置相应的属性,比如外层Border的Padding要与阴影的属性保持一致。
不足
- 有时鼠标进入和离开时阴影显隐失灵
- 效率低下,特别是经常需要更新界面时(所以最后我们放弃这个方案了)
- 上述样式存在一个Bug,即在用到界面验证时,会发生验证无法显示的情况。因为验证常用的AdornedElementPlaceholder中包含一个Adorner,在系统默认的Window样式中包含AdornerLayer,所以显示正常。为此我们需要在样式中手动添加一个AdornerLayer。
夜间模式
因项目需要设计了一个夜间模式,而项目的地图是由位图拼接而成的,无法直接修改。所以我在上面放置了一个Border作为遮罩,原理示意如下:
<DockPanel> <Slider DockPanel.Dock="Top" LargeChange="0.2" Maximum="1" Minimum="0" Orientation="Horizontal" TickFrequency="0.1" Value="{Binding ElementName=BdrShade, Path=Opacity}" /> <Grid> <Image Source="Lighthouse.jpg" /> <Border Name="BdrShade" Background="Black" IsHitTestVisible="False" Opacity="0.5" /> </Grid> </DockPanel>
原理很简单,就是将要设置夜间模式的控件上层放置一个背景为黑色的Border,通过调整其透明度实现。值得注意的是,必须将Border的IsHitTestVisible设为False(即不参与命中测试), 否则控件不能点击。
查看控件模板或样式
在使用WPF的过程中,我们经常会对控件的外观进行修改,这个时候查看原有的样式或者模板进行参考就很有必要了。这样一能够减少许多工作,只修改需要的部分,二能够避免修改模板或样式后失去某些功能。下面就是我常用的两个方法
查看当前控件使用的模板
可通过手动创建控件,然后使用XamlWriter类将模板保存的方式来查看控件的当前模板。对此感兴趣的可查看《WPF编程宝典——C#2010版》(《Pro WPF in C# 2010》)17.2.2 剖析控件相关部分。
上述方法经过简单修改之后也可用来查看样式或者第三方控件的模板,比如我就利用该方法查看过DevExpress中图表控件的模板。
上述方法的缺点是使用XamlWriter类保存后的XAML代码有些冗余且不直观,因为其总是会使用属性元素语法而不是特性语法。
查看主题使用的模板
WPF中为自带的控件准备了4套主题,分别是Aero、Classic、Luna、Royale。分别对应PresentationFramework.Aero.dll、PresentationFramework.Classic.dll、PresentationFramework.Luna.dll、PresentationFramework.Royale.dll这4个DLL,在需要时,可通过C#反编译工具(如ILSpy、dotPeek)查看其中的BAML资源。如下图: