• [WPF Bug清单]之(11)——错位的RenderTransform动画


    WPF中制作位移类动画大致有3种方式,MarginRenderTransformLayoutTransform。虽然3者的效果略有不同,但是不少情况下3种方式可以通用。这时RenderTransform就以其优秀的平均性能成了3者的首选。因为RenderTransform不涉及Layout的调整,不会触发界面的重新布局(关于RenderTransformLayoutTransform之间的不同可以参考这篇文章,由于某个众所周知的原因,要看这篇文章需要FQ,所以直接点是打不开的)。但是当你了解到RenderTransform所存在的Bug时,可能就需要考虑一番了。

    我们都知道很多控件都有FocusVisualStyle,一般就是一个虚线框。RenderTransform的问题就在于,控件的FocusVisualStyle中的元素,不会随着控件本身一起被Transform

    Bug的重现过程如下图所示。

    1. 程序运行图

     

    一个简单得不能再简单的程序。在空窗体上放一个Button,建立一个动画,当MouseEnter这个Button的时候,用RenderTransform把这个Button向右向下移动100个像素。代码如下。

    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所示。

    2. Button得到了焦点

     

    然后不要再动任何键了,把鼠标移到Button上面以触发动画。发现什么了?看图3

     

    3. FocusButton落下了

     

    当鼠标再次MouseEnterButton时,虚框会被自动放回正确的位置。整个示例程序可以从这里下载。运行环境是.NET Framework 3.5 SP1

     

    初步判断,这个BugWPFFocusVisualStyle的设计方式有关。这个虚框与Adorner Layer类似,并不属于这个Button,所以渲染时没有被当被Button的一部分。而RenderTransform是一种纯渲染时行为,所以虚框就被无视了。从逻辑上来讲似乎这个Bug很合理,不然会让Render的设计很复杂,本来Render一个控件只要关心控件自身就可以了,现在还要关心相关的其它控件。但是我相信以微软的水平,应该是有能力解决这个问题的。就像文字渲染模糊的问题,WPF刚出来就被人诟病,微软还振振有词地解释说这就是WPF的像素无关和理想渲染模型设计原则的体现。这不到了.NET 4.0最终还是提供了选项关闭WPF的理想渲染模型。

     

    其实在WPF中类似这样的问题有很多,比如Validation Error TemplatePopup Window都有类似的问题。没有为PopupWindow写一个专题是因为产生Bug的条件可以避免出现。但是对于这类,动画与Focus的组合,是很常见的情况。

     

    而且这个问题从WPF的使用者而言是如此底层。到目前为止,我也只想到两个不是办法的办法。

    1.       不显示FocusVisualStyle

    2.       不用RenderTransform

    其实这只是回避问题式的方案。不知大家有什么好的解决方案?

     

    更新解决方案:

    根据Curry的回复。目前已经的最佳解决方案是。在Storyboard的CurrentTimeInvalidated事件处理函数中加入下面的代码。

     

    AdornerLayer.GetAdornerLayer(button).Update();

     

    这样Button的行为就正确了。但是大量使用可能导致一些性能问题。当然如果只有几个控件应该是没有问题的。

     

  • 相关阅读:
    Pentaho
    sympy 解四元一次方程
    install R language on ubuntu
    pyside
    浙江省医院网上挂号
    mtu值相关
    Python 中除法运算需要注意的几点
    idea
    kilim
    good blog
  • 原文地址:https://www.cnblogs.com/nankezhishi/p/RenderTransformAndFocus.html
Copyright © 2020-2023  润新知