[WPF Bug清单]之(11)——错位的RenderTransform动画
2009-12-02 21:39
471 查看
在WPF中制作位移类动画大致有3种方式,Margin、RenderTransform和LayoutTransform。虽然3者的效果略有不同,但是不少情况下3种方式可以通用。这时RenderTransform就以其优秀的平均性能成了3者的首选。因为RenderTransform不涉及Layout的调整,不会触发界面的重新布局(关于RenderTransform与LayoutTransform之间的不同可以参考这篇文章,由于某个众所周知的原因,要看这篇文章需要FQ,所以直接点是打不开的)。但是当你了解到RenderTransform所存在的Bug时,可能就需要考虑一番了。
我们都知道很多控件都有FocusVisualStyle,一般就是一个虚线框。RenderTransform的问题就在于,控件的FocusVisualStyle中的元素,不会随着控件本身一起被Transform。
Bug的重现过程如下图所示。
Demo Code
1 <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
2 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
3 x:Class="AnimationConflict.MainWindow"
4 x:Name="Window" Title="MainWindow"
5 Width="200" Height="200">
6 <Window.Resources>
7 <Storyboard x:Key="OnMouseEnter">
8 <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="button"
9 Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)">
10 <SplineDoubleKeyFrame KeyTime="00:00:01" Value="100"/>
11 </DoubleAnimationUsingKeyFrames>
12 <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="button"
13 Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.Y)">
14 <SplineDoubleKeyFrame KeyTime="00:00:01" Value="100"/>
15 </DoubleAnimationUsingKeyFrames>
16 </Storyboard>
17 </Window.Resources>
18 <Window.Triggers>
19 <EventTrigger RoutedEvent="Mouse.MouseEnter" SourceName="button">
20 <BeginStoryboard Storyboard="{StaticResource OnMouseEnter}"/>
21 </EventTrigger>
22 </Window.Triggers>
23 <Grid x:Name="LayoutRoot">
24 <Button x:Name="button" RenderTransformOrigin="0.5,0.5"
25 HorizontalAlignment="Left" VerticalAlignment="Top"
26 Width="75" Content="Button">
27 <Button.RenderTransform>
28 <TranslateTransform/>
29 </Button.RenderTransform>
30 </Button>
31 </Grid>
32 </Window>
程序刚运行起来的时候Button是没有获得焦点的,我们按几下Tab键,让这个Button获得焦点。如图2所示。
[align=center]图2. Button得到了焦点[/align]
然后不要再动任何键了,把鼠标移到Button上面以触发动画。发现什么了?看图3。
图3. Focus被Button落下了
当鼠标再次MouseEnterButton时,虚框会被自动放回正确的位置。整个示例程序可以从这里下载。运行环境是.NET Framework 3.5 SP1。
初步判断,这个Bug与WPF对FocusVisualStyle的设计方式有关。这个虚框与Adorner Layer类似,并不属于这个Button,所以渲染时没有被当被Button的一部分。而RenderTransform是一种纯渲染时行为,所以虚框就被无视了。从逻辑上来讲似乎这个Bug很合理,不然会让Render的设计很复杂,本来Render一个控件只要关心控件自身就可以了,现在还要关心相关的其它控件。但是我相信以微软的水平,应该是有能力解决这个问题的。就像文字渲染模糊的问题,WPF刚出来就被人诟病,微软还振振有词地解释说这就是WPF的像素无关和理想渲染模型设计原则的体现。这不到了.NET 4.0最终还是提供了选项关闭WPF的理想渲染模型。
其实在WPF中类似这样的问题有很多,比如Validation Error Template、Popup Window都有类似的问题。没有为PopupWindow写一个专题是因为产生Bug的条件可以避免出现。但是对于这类,动画与Focus的组合,是很常见的情况。
而且这个问题从WPF的使用者而言是如此底层。到目前为止,我也只想到两个不是办法的办法。
1. 不显示FocusVisualStyle。
2. 不用RenderTransform。
其实这只是回避问题式的方案。不知大家有什么好的解决方案?
更新解决方案:
根据Curry的回复。目前已经的最佳解决方案是。在Storyboard的CurrentTimeInvalidated事件处理函数中加入下面的代码。
AdornerLayer.GetAdornerLayer(button).Update();
这样Button的行为就正确了。但是大量使用可能导致一些性能问题。当然如果只有几个控件应该是没有问题的。
我们都知道很多控件都有FocusVisualStyle,一般就是一个虚线框。RenderTransform的问题就在于,控件的FocusVisualStyle中的元素,不会随着控件本身一起被Transform。
Bug的重现过程如下图所示。
Demo Code
1 <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
2 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
3 x:Class="AnimationConflict.MainWindow"
4 x:Name="Window" Title="MainWindow"
5 Width="200" Height="200">
6 <Window.Resources>
7 <Storyboard x:Key="OnMouseEnter">
8 <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="button"
9 Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)">
10 <SplineDoubleKeyFrame KeyTime="00:00:01" Value="100"/>
11 </DoubleAnimationUsingKeyFrames>
12 <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="button"
13 Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.Y)">
14 <SplineDoubleKeyFrame KeyTime="00:00:01" Value="100"/>
15 </DoubleAnimationUsingKeyFrames>
16 </Storyboard>
17 </Window.Resources>
18 <Window.Triggers>
19 <EventTrigger RoutedEvent="Mouse.MouseEnter" SourceName="button">
20 <BeginStoryboard Storyboard="{StaticResource OnMouseEnter}"/>
21 </EventTrigger>
22 </Window.Triggers>
23 <Grid x:Name="LayoutRoot">
24 <Button x:Name="button" RenderTransformOrigin="0.5,0.5"
25 HorizontalAlignment="Left" VerticalAlignment="Top"
26 Width="75" Content="Button">
27 <Button.RenderTransform>
28 <TranslateTransform/>
29 </Button.RenderTransform>
30 </Button>
31 </Grid>
32 </Window>
程序刚运行起来的时候Button是没有获得焦点的,我们按几下Tab键,让这个Button获得焦点。如图2所示。
[align=center]图2. Button得到了焦点[/align]
然后不要再动任何键了,把鼠标移到Button上面以触发动画。发现什么了?看图3。
图3. Focus被Button落下了
当鼠标再次MouseEnterButton时,虚框会被自动放回正确的位置。整个示例程序可以从这里下载。运行环境是.NET Framework 3.5 SP1。
初步判断,这个Bug与WPF对FocusVisualStyle的设计方式有关。这个虚框与Adorner Layer类似,并不属于这个Button,所以渲染时没有被当被Button的一部分。而RenderTransform是一种纯渲染时行为,所以虚框就被无视了。从逻辑上来讲似乎这个Bug很合理,不然会让Render的设计很复杂,本来Render一个控件只要关心控件自身就可以了,现在还要关心相关的其它控件。但是我相信以微软的水平,应该是有能力解决这个问题的。就像文字渲染模糊的问题,WPF刚出来就被人诟病,微软还振振有词地解释说这就是WPF的像素无关和理想渲染模型设计原则的体现。这不到了.NET 4.0最终还是提供了选项关闭WPF的理想渲染模型。
其实在WPF中类似这样的问题有很多,比如Validation Error Template、Popup Window都有类似的问题。没有为PopupWindow写一个专题是因为产生Bug的条件可以避免出现。但是对于这类,动画与Focus的组合,是很常见的情况。
而且这个问题从WPF的使用者而言是如此底层。到目前为止,我也只想到两个不是办法的办法。
1. 不显示FocusVisualStyle。
2. 不用RenderTransform。
其实这只是回避问题式的方案。不知大家有什么好的解决方案?
更新解决方案:
根据Curry的回复。目前已经的最佳解决方案是。在Storyboard的CurrentTimeInvalidated事件处理函数中加入下面的代码。
AdornerLayer.GetAdornerLayer(button).Update();
这样Button的行为就正确了。但是大量使用可能导致一些性能问题。当然如果只有几个控件应该是没有问题的。
相关文章推荐
- WPF Bug清单之(13)——应该出现却没有出现的ListView水平滚动条
- [WPF Bug清单]之(12)——与自定义Attached Property相关的Binding Path运行时错误
- WPF中Storyboard动画延迟的一个BUG(缺陷?)
- [WPF Bug清单]之(2)——RadioButton的IsChecked绑定失效
- [WPF Bug清单]之(6)——Button的IsCancel属性失效
- [WPF Bug清单]之(3)——暗中创建文件的打开文件对话框
- [WPF Bug清单]之(4)——点击RadioButton的空白没有反应
- [WPF Bug清单]之(7)——顽固的Error Template
- [WPF Bug清单]之(10)——CheckBox在不同主题下具有不同的行为
- [WPF Bug清单]之(8)——RowDefinition中MaxHeight在一定条件下失效
- [WPF Bug清单](序)与之(1)——可以多选的单选ListBox
- WPF Bug清单之(13)——应该出现却没有出现的ListView水平滚动条
- [WPF Bug清单]之(9)——消失的光标
- [WPF Bug清单]之(5)——隐藏模态对话框后变成非模态
- WPF动画效果
- WPF中的动画(转自博客园周银辉的开发博客)
- WPF中绘画和动画
- WPF当属性值改变时利用PropertyChanged事件来加载动画
- duilib List控件,横向滚动时列表项不移动或者显示错位的bug的修复
- WPF之小动画二