[WPF Bug清單]之(11)——錯位的RenderTransform動畫

來源:互聯網
上載者:User

在WPF中製作位移類動畫大致有3種方式,Margin、RenderTransform和LayoutTransform。雖然3者的效果略有不同,但是不少情況下3種方式可以通用。這時RenderTransform就以其優秀的平均效能成了3者的首選。因為RenderTransform不涉及Layout的調整,不會觸發介面的重新布局(關於RenderTransform與LayoutTransform之間的不同可以參考這篇文章,由於某個眾所周知的原因,要看這篇文章需要翻牆,所以直接點是打不開的)。但是當你瞭解到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. 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的行為就正確了。但是大量使用可能導致一些效能問題。當然如果只有幾個控制項應該是沒有問題的。

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.