none
TabControl: Margin der TabItems animieren RRS feed

  • Frage

  • Hallo allerseits,

    im englischen Pendant dieses Postings hat bisher niemand geantwortet, daher versuche ich's mal hier.

    Ich habe einen TabControl-/TabItem-Style, bei dem die TabItems animiert sind. D.h., das selektierte Tab ist höher gegenüber unselektierten. Verweilt die Maus über einem nicht selektierten Tab, liegt dessen Höhe zwischen der des selektierten und der unselektierten. Der Übergang der einzelnen Status ist darüber hinaus animiert, und hier gibt's dann ein absolut seltsames Problem mit der Animation, die beim Hover läuft - diese läuft nur solange, bis ein Tab einmal selektiert wurde.

    Hier ein vereinfachtes Beispiel, das das Problem demonstriert:

    <Window x:Class="WpfTests.StyledTabControl_AnimationTest"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="Animating TabItems (bug?)" 
            Height="200" 
            Width="400">
       <Window.Resources>
          <Storyboard x:Key="TabItemStoryBoard_Unselected">
             <ThicknessAnimation Storyboard.TargetName="Border" 
                                 Storyboard.TargetProperty="Margin"
                                 To="0,8,0,0"
                                 FillBehavior="HoldEnd"
                                 Duration="0:0:0.1"/>
          </Storyboard>
          <Storyboard x:Key="TabItemStoryBoard_Selected">
             <ThicknessAnimation Storyboard.TargetName="Border" 
                                 Storyboard.TargetProperty="Margin"
                                 To="0,0,0,0"
                                 FillBehavior="HoldEnd"
                                 Duration="0:0:0.1"/>
          </Storyboard>
    
          <!-- Das ist die problematische Animation: -->
          <Storyboard x:Key="TabItemStoryBoard_Hover">
             <!--<ThicknessAnimation Storyboard.TargetName="Border" 
                                 Storyboard.TargetProperty="Margin"
                                 By="0,-4,0,0"
                                 FillBehavior="HoldEnd"
                                 Duration="0:0:0.1"/>-->
             <ThicknessAnimationUsingKeyFrames Storyboard.TargetName="Border" 
                                               Storyboard.TargetProperty="Margin">
                <SplineThicknessKeyFrame KeyTime="0:0:0.1"
                                         Value="0,2,0,0"/>
                <SplineThicknessKeyFrame KeyTime="0:0:0.2"
                                         Value="0,4,0,0"/>
             </ThicknessAnimationUsingKeyFrames>
          </Storyboard>
    
    
          <!-- The Style for TabItems (strips). -->
          <Style TargetType="{x:Type TabItem}">
             <Setter Property="Template">
                <Setter.Value>
                   <ControlTemplate TargetType="{x:Type TabItem}">
                      <Grid Height="35" VerticalAlignment="Bottom">
                         <Border Name="Border"
                                    BorderBrush="DarkGray"
                                    BorderThickness="2,1,1,0" 
                                    CornerRadius="3,3,0,0">
                            <ContentPresenter x:Name="ContentSite"
                                              VerticalAlignment="Center"
                                              HorizontalAlignment="Center"
                                              ContentSource="Header"
                                              Margin="7,2,12,2"
                                              RecognizesAccessKey="True"/>
                         </Border>
                      </Grid>
                      <ControlTemplate.Triggers>
    
                         <!-- The appearance of a TabItem when it's inactive/unselected -->
                         <Trigger Property="IsSelected" Value="False">
                            <Trigger.EnterActions>
                               <BeginStoryboard Storyboard="{StaticResource TabItemStoryBoard_Unselected}"/>
                            </Trigger.EnterActions>
                            <Trigger.ExitActions>
                               <BeginStoryboard Storyboard="{StaticResource TabItemStoryBoard_Unselected}"/>
                            </Trigger.ExitActions>
                            <Setter TargetName="Border" Property="Background" Value="LightGray" />
                         </Trigger>
    
                         <!-- The appearance of a TabItem when it's disabled (in addition to Selected=False) -->
                         <Trigger Property="IsEnabled" Value="False">
                            <Setter TargetName="Border" Property="Background" Value="DarkGray" />
                         </Trigger>
    
                         <!-- The appearance of a TabItem when the mouse hovers over it -->
                         <MultiTrigger>
                            <MultiTrigger.Conditions>
                               <Condition Property="Border.IsMouseOver" Value="True"/>
                               <Condition Property="IsSelected" Value="False"/>
                            </MultiTrigger.Conditions>
                            <MultiTrigger.EnterActions>
                               <BeginStoryboard Storyboard="{StaticResource TabItemStoryBoard_Hover}"/>
                            </MultiTrigger.EnterActions>
                            <MultiTrigger.ExitActions>
                               <BeginStoryboard Storyboard="{StaticResource TabItemStoryBoard_Unselected}"/>
                            </MultiTrigger.ExitActions>
                            <Setter TargetName="Border" Property="Background" Value="White" />
                         </MultiTrigger>
    
                         <!-- The appearance of a TabItem when it's active/selected -->
                         <Trigger Property="IsSelected" Value="True">
                            <Trigger.EnterActions>
                               <BeginStoryboard Storyboard="{StaticResource TabItemStoryBoard_Selected}"/>
                            </Trigger.EnterActions>
                            <Trigger.ExitActions>
                               <BeginStoryboard Storyboard="{StaticResource TabItemStoryBoard_Unselected}"/>
                            </Trigger.ExitActions>
                            <Setter TargetName="Border" Property="Background" Value="LightSalmon" />
                            <Setter TargetName="Border" Property="BorderBrush" Value="LightSalmon" />
                         </Trigger>
                      </ControlTemplate.Triggers>
                   </ControlTemplate>
                </Setter.Value>
             </Setter>
          </Style>
       </Window.Resources>
    
       <TabControl x:Name="tc" Margin="5" 
                   SelectedIndex="0"
                   Background="LightSalmon"
                   BorderBrush="LightSalmon">
          <TabItem Header="Tab 1"/>
          <TabItem Header="Tab 2"/>
          <TabItem Header="Tab 3" IsEnabled="False"/>
          <TabItem Header="Tab 4"/>
          <TabItem Header="Tab 5"/>
       </TabControl>
    </Window>

    Wenn man obiges Window anzeigt, ist der Hover-Effekt anfänglich genau, wie er sein soll (einfach mal mit der Maus über die Tabs fahren) - der Wechsel der Höhe der einzelnen Tabs funktioniert bestens (für die Tabs 2, 4 und 5).
    Wenn man allerdings ein Tab einmal selektiert hat, läuft die Hover-Animation für jenes Tab nicht mehr. Sprich, wenn man Tab 4 auswählt und dann wieder zurück auf Tab 1 wechselt, findet die Animation nur noch beim Hover über die Tabs 2 und 5 statt.

    Ich kann mir das absolut nicht erklären. Die Trigger in den Styles habe ich auch mal getraced - die feuern wie gewünscht.

    Hat hier jemand eine Idee ..?
    Cheers,
    Olaf
    Mittwoch, 20. Januar 2010 12:10

Antworten

  • Huhu!

    Ich mal mal wieder die Ingrid:

    [Olaf:]
    Aber, was soll ich sagen (ich sollte ja inzwischen vorsichtig sein mit "Positiväusserungen") - ich glaube, ich habe es inzwischen doch noch hinbekommen, jedenfalls kann ich momentan kein Fehlverhalten mehr feststellen (nach radikalen Umbaumaßnahmen). Werde jetzt erst mal etwas mampfen gehen und alles ein wenig sacken lassen, evt. findet sich ja danach doch noch ein Fehler.

    Scheint mir jetzt tatsächlich vernünftig zu funktionieren. Ich habe das vereinfachte Beispiel-Window doch noch einmal entspr. angepasst und kann auch dort kein Problem mehr feststellen:

    <Window x:Class="WpfTests.StyledTabControl_AnimationTest"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="Animating TabItems (resolved)" 
            Height="200" 
            Width="400">
       <Window.Resources>
          <Storyboard x:Key="TabItemStoryBoard_Default" x:Name="res_sbUnselected_Default">
             <ThicknessAnimation Storyboard.TargetName="Border" 
                                 Storyboard.TargetProperty="Margin"
                                 To="0,4,0,0"
                                 FillBehavior="HoldEnd"
                                 Duration="0:0:0.1"/>
          </Storyboard>
    
          <Storyboard x:Key="TabItemStoryBoard_Unselected" x:Name="res_sbUnselected">
             <ThicknessAnimation Storyboard.TargetName="Border" 
                                 Storyboard.TargetProperty="Margin"
                                 To="0,8,0,0"
                                 FillBehavior="HoldEnd"
                                 Duration="0:0:0.1"/>
          </Storyboard>
          <Storyboard x:Key="TabItemStoryBoard_Selected" x:Name="res_sbSelected">
             <ThicknessAnimation Storyboard.TargetName="Border" 
                                 Storyboard.TargetProperty="Margin"
                                 To="0,0,0,0"
                                 FillBehavior="HoldEnd"
                                 Duration="0:0:0.1"/>
          </Storyboard>
          <Storyboard x:Key="TabItemStoryBoard_Hover" x:Name="res_">
             <ThicknessAnimationUsingKeyFrames Storyboard.TargetName="Border" 
                                               Storyboard.TargetProperty="Margin">
                <SplineThicknessKeyFrame KeyTime="0:0:0.1"
                                         Value="0,2,0,0"/>
                <SplineThicknessKeyFrame KeyTime="0:0:0.2"
                                         Value="0,4,0,0"/>
             </ThicknessAnimationUsingKeyFrames>
          </Storyboard>
    
          <!-- The Style for TabItems (strips). -->
          <Style TargetType="{x:Type TabItem}">
             <Setter Property="Template">
                <Setter.Value>
                   <ControlTemplate TargetType="{x:Type TabItem}">
                      <Grid Height="35" VerticalAlignment="Bottom">
                         <Border Name="Border"
                                    BorderBrush="DarkGray"
                                    BorderThickness="2,1,1,0" 
                                    CornerRadius="3,3,0,0">
                            <ContentPresenter x:Name="ContentSite"
                                              VerticalAlignment="Center"
                                              HorizontalAlignment="Center"
                                              ContentSource="Header"
                                              Margin="7,2,12,2"
                                              RecognizesAccessKey="True"/>
                         </Border>
                      </Grid>
                      <ControlTemplate.Triggers>
    
                         <!-- The appearance of a TabItem when it's inactive/unselected -->
                         <MultiTrigger>
                            <MultiTrigger.Conditions>
                               <Condition Property="Border.IsMouseOver" Value="False"/>
                               <Condition Property="IsSelected" Value="False"/>
                            </MultiTrigger.Conditions>
                            <MultiTrigger.EnterActions>
                               <BeginStoryboard x:Name="sbUnselected" Storyboard="{StaticResource TabItemStoryBoard_Unselected}"/>
                            </MultiTrigger.EnterActions>
                            <MultiTrigger.ExitActions>
                               <StopStoryboard BeginStoryboardName="sbUnselected"/>
                            </MultiTrigger.ExitActions>
                            <Setter TargetName="Border" Property="Background" Value="LightGray" />
                         </MultiTrigger>
    
                         <!-- The appearance of a TabItem when it's disabled (in addition to Selected=False) -->
                         <Trigger Property="IsEnabled" Value="False">
                            <Setter TargetName="Border" Property="Background" Value="DarkGray" />
                         </Trigger>
    
                         <!-- The appearance of a TabItem when the mouse hovers over it -->
                         <MultiTrigger>
                            <MultiTrigger.Conditions>
                               <Condition Property="Border.IsMouseOver" Value="True"/>
                               <Condition Property="IsSelected" Value="False"/>
                            </MultiTrigger.Conditions>
                            <MultiTrigger.EnterActions>
                               <StopStoryboard BeginStoryboardName="sbUnselected_Hover_Exit"/>
                               <BeginStoryboard x:Name="sbHover" Storyboard="{StaticResource TabItemStoryBoard_Hover}"/>
                            </MultiTrigger.EnterActions>
                            <MultiTrigger.ExitActions>
                               <BeginStoryboard x:Name="sbUnselected_Hover_Exit" Storyboard="{StaticResource TabItemStoryBoard_Unselected}"/>
                            </MultiTrigger.ExitActions>
                            <Setter TargetName="Border" Property="Background" Value="White" />
                         </MultiTrigger>
    
                         <!-- The appearance of a TabItem when it's active/selected -->
                         <Trigger Property="IsSelected" Value="True">
                            <Trigger.EnterActions>
                               <StopStoryboard BeginStoryboardName="sbUnselected_Selected_Exit"/>
                               <BeginStoryboard x:Name="sbSelected" Storyboard="{StaticResource TabItemStoryBoard_Selected}"/>
                            </Trigger.EnterActions>
                            <Trigger.ExitActions>
                               <BeginStoryboard x:Name="sbUnselected_Selected_Exit" Storyboard="{StaticResource TabItemStoryBoard_Unselected}"/>
                            </Trigger.ExitActions>
                            <Setter TargetName="Border" Property="Background" Value="LightSalmon" />
                            <Setter TargetName="Border" Property="BorderBrush" Value="LightSalmon" />
                         </Trigger>
                      </ControlTemplate.Triggers>
                   </ControlTemplate>
                </Setter.Value>
             </Setter>
          </Style>
       </Window.Resources>
    
       <TabControl x:Name="tc" Margin="5" 
                   SelectedIndex="0"
                   Background="LightSalmon"
                   BorderBrush="LightSalmon">
          <TabItem Header="Tab 1"/>
          <TabItem Header="Tab 2"/>
          <TabItem Header="Tab 3" IsEnabled="False"/>
          <TabItem Header="Tab 4"/>
          <TabItem Header="Tab 5"/>
       </TabControl>
    </Window>
    

    Änderungen gegenüber den Vorgänger-Versionen:
    Ich habe den Unselected-Trigger nun in einen MultiTrigger überführt; außerdem werden die in den Exit-Triggern gestarteten Aufrufe des Unselected-Storyboards differenziert benamst und in den jew. EnterActions explizit wieder gestoppt. Macht in meinen Augen zwar absolut keinen Sinn, aber nur so habe ich es zum Werkeln überreden können ...

    Cheers,
    Olaf
    Montag, 25. Januar 2010 13:35
  • Huhu!

    Ich habe die Geschichte inzwischen auch in meinem Blog verarbeitet. Wen's interessiert (englisch): WPF: Styling the TabControl and its TabItems - Part 2: Animating TabItems
    Cheers,
    Olaf
    Mittwoch, 27. Januar 2010 09:11

Alle Antworten

  • Hallo Olaf,

    ich hab mich mal etwas mit deinem Problem herumgespielt, wenn ich die laufenden StoryBoards stoppe, dann funktionierts bei mir.

     

                            <ControlTemplate.Triggers>

     

                                <!-- The appearance of a TabItem when it's inactive/unselected -->

                                <Trigger Property="IsSelected" Value="False">

                                    <Trigger.EnterActions>

                                        <BeginStoryboard x:Name="bs1" Storyboard="{StaticResource TabItemStoryBoard_Unselected}"/>

                                    </Trigger.EnterActions>

     

                                    <Trigger.ExitActions>

                                        <StopStoryboard BeginStoryboardName="bs1" />

                                        <!--<BeginStoryboard Storyboard="{StaticResource TabItemStoryBoard_Unselected}"/>-->

                                    </Trigger.ExitActions>

     

                                    <Setter TargetName="Border" Property="Background" Value="LightGray" />

                                </Trigger>

     

                                <!-- The appearance of a TabItem when it's disabled (in addition to Selected=False) -->

                                <Trigger Property="IsEnabled" Value="False">

                                    <Setter TargetName="Border" Property="Background" Value="DarkGray" />                               

                                </Trigger>

     

                                <!-- The appearance of a TabItem when the mouse hovers over it -->

                                <MultiTrigger>

                                    <MultiTrigger.Conditions>

                                        <Condition Property="Border.IsMouseOver" Value="True"/>

                                        <Condition Property="IsSelected" Value="False"/>

                                    </MultiTrigger.Conditions>

                                    <MultiTrigger.EnterActions>

                                        <BeginStoryboard x:Name="bs2" Storyboard="{StaticResource TabItemStoryBoard_Hover}"/>

                                    </MultiTrigger.EnterActions>

                                    <MultiTrigger.ExitActions>

                                        <StopStoryboard BeginStoryboardName="bs2"/>

                                        <!--<BeginStoryboard x:Name="bs3" Storyboard="{StaticResource TabItemStoryBoard_Unselected}"/>-->                                

                                    </MultiTrigger.ExitActions>

                                    <Setter TargetName="Border" Property="Background" Value="White" />

                                </MultiTrigger>

     

                                <!-- The appearance of a TabItem when it's active/selected -->

                                <Trigger Property="IsSelected" Value="True">

                                    <Trigger.EnterActions>

                                        <BeginStoryboard x:Name="bs4" Storyboard="{StaticResource TabItemStoryBoard_Selected}"/>

                                    </Trigger.EnterActions>

                                    <Trigger.ExitActions>

                                        <StopStoryboard BeginStoryboardName="bs4"/>

                                        <!--<BeginStoryboard x:Name="bs5" Storyboard="{StaticResource TabItemStoryBoard_Unselected}"/>-->

                                    </Trigger.ExitActions>

                                    <Setter TargetName="Border" Property="Background" Value="LightSalmon" />

                                    <Setter TargetName="Border" Property="BorderBrush" Value="LightSalmon" />

                                </Trigger>

                            </ControlTemplate.Triggers>

                        </ControlTemplate>

     

     
    Hope that helps.

    Mittwoch, 20. Januar 2010 16:35
  • Hi Günther,

    danke, das hat mich zumindest auf den richtigen Weg gebracht. Problematisch mit Deiner Lösung war noch, dass damit die Animation ausblieb, die beim Hover-Exit laufen sollte (TabItemStoryBoard_Unselected ). Durch das Starten der entspr. Animation aus dem Hover-ExitTrigger heraus konnte ich das Probem aber beseitigen.

    In meinen Augen ist das ein Bug, denn per Default sollten die SBs nur ein einziges Mal laufen und ein StopStoryboard ergo nicht notwendig sein. Mit RepeatBehavior hatte ich auch herumgespielt, aber - entgegen der Dokum - resultiert das Setzen auf "1" in einer dauerhaften Animation. Oder ich versteh's einfach nur nicht ... :-)

    Hier das XAML, das zumindest das geschilderte Problem beseitigt, resp. einen Workaround bietet:

    <Window x:Class="WpfTests.StyledTabControl_AnimationTest"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="Animating TabItems (bug?)" 
            Height="200" 
            Width="400">
       <Window.Resources>
          <Storyboard x:Key="TabItemStoryBoard_Unselected">
             <ThicknessAnimation Storyboard.TargetName="Border" 
                                 Storyboard.TargetProperty="Margin"
                                 To="0,8,0,0"
                                 FillBehavior="HoldEnd"
                                 Duration="0:0:0.1"/>
          </Storyboard>
          <Storyboard x:Key="TabItemStoryBoard_Selected">
             <ThicknessAnimation Storyboard.TargetName="Border" 
                                 Storyboard.TargetProperty="Margin"
                                 To="0,0,0,0"
                                 FillBehavior="HoldEnd"
                                 Duration="0:0:0.1"/>
          </Storyboard>
    
          <!-- This is the problematic animation: -->
          <Storyboard x:Key="TabItemStoryBoard_Hover">
             <!--<ThicknessAnimation Storyboard.TargetName="Border" 
                                 Storyboard.TargetProperty="Margin"
                                 By="0,-4,0,0"
                                 FillBehavior="HoldEnd"
                                 Duration="0:0:0.1"/>-->
             <ThicknessAnimationUsingKeyFrames Storyboard.TargetName="Border" 
                                               Storyboard.TargetProperty="Margin">
                <SplineThicknessKeyFrame KeyTime="0:0:0.1"
                                         Value="0,2,0,0"/>
                <SplineThicknessKeyFrame KeyTime="0:0:0.2"
                                         Value="0,4,0,0"/>
             </ThicknessAnimationUsingKeyFrames>
          </Storyboard>
    
    
          <!-- The Style for TabItems (strips). -->
          <Style TargetType="{x:Type TabItem}">
             <Setter Property="Template">
                <Setter.Value>
                   <ControlTemplate TargetType="{x:Type TabItem}">
                      <Grid Height="35" VerticalAlignment="Bottom">
                         <Border Name="Border"
                                    BorderBrush="DarkGray"
                                    BorderThickness="2,1,1,0" 
                                    CornerRadius="3,3,0,0">
                            <ContentPresenter x:Name="ContentSite"
                                              VerticalAlignment="Center"
                                              HorizontalAlignment="Center"
                                              ContentSource="Header"
                                              Margin="7,2,12,2"
                                              RecognizesAccessKey="True"/>
                         </Border>
                      </Grid>
                      <ControlTemplate.Triggers>
    
                         <!-- The appearance of a TabItem when it's inactive/unselected -->
                         <Trigger Property="IsSelected" Value="False">
                            <Trigger.EnterActions>
                               <BeginStoryboard  x:Name="sbUnselected" Storyboard="{StaticResource TabItemStoryBoard_Unselected}"/>
                            </Trigger.EnterActions>
                            <Trigger.ExitActions>
                               <StopStoryboard BeginStoryboardName="sbUnselected"/>
                            </Trigger.ExitActions>
                            <Setter TargetName="Border" Property="Background" Value="LightGray" />
                         </Trigger>
    
                         <!-- The appearance of a TabItem when it's disabled (in addition to Selected=False) -->
                         <Trigger Property="IsEnabled" Value="False">
                            <Setter TargetName="Border" Property="Background" Value="DarkGray" />
                         </Trigger>
    
                         <!-- The appearance of a TabItem when the mouse hovers over it -->
                         <MultiTrigger>
                            <MultiTrigger.Conditions>
                               <Condition Property="Border.IsMouseOver" Value="True"/>
                               <Condition Property="IsSelected" Value="False"/>
                            </MultiTrigger.Conditions>
                            <MultiTrigger.EnterActions>
                               <BeginStoryboard x:Name="sbHover" Storyboard="{StaticResource TabItemStoryBoard_Hover}"/>
                            </MultiTrigger.EnterActions>
                            <MultiTrigger.ExitActions>
                               <StopStoryboard BeginStoryboardName="sbHover"/>
                               <BeginStoryboard Storyboard="{StaticResource TabItemStoryBoard_Unselected}"/>
                            </MultiTrigger.ExitActions>
                            <Setter TargetName="Border" Property="Background" Value="White" />
                         </MultiTrigger>
    
                         <!-- The appearance of a TabItem when it's active/selected -->
                         <Trigger Property="IsSelected" Value="True">
                            <Trigger.EnterActions>
                               <BeginStoryboard x:Name="sbSelected" Storyboard="{StaticResource TabItemStoryBoard_Selected}"/>
                            </Trigger.EnterActions>
                            <Trigger.ExitActions>
                               <StopStoryboard BeginStoryboardName="sbSelected"/>
                            </Trigger.ExitActions>
                            <Setter TargetName="Border" Property="Background" Value="LightSalmon" />
                            <Setter TargetName="Border" Property="BorderBrush" Value="LightSalmon" />
                         </Trigger>
                      </ControlTemplate.Triggers>
                   </ControlTemplate>
                </Setter.Value>
             </Setter>
          </Style>
       </Window.Resources>
    
       <TabControl x:Name="tc" Margin="5" 
                   SelectedIndex="0"
                   Background="LightSalmon"
                   BorderBrush="LightSalmon">
          <TabItem Header="Tab 1"/>
          <TabItem Header="Tab 2"/>
          <TabItem Header="Tab 3" IsEnabled="False"/>
          <TabItem Header="Tab 4"/>
          <TabItem Header="Tab 5"/>
       </TabControl>
    </Window>
    

    Cheers,
    Olaf
    • Als Antwort markiert Olaf Rabbachin Donnerstag, 21. Januar 2010 10:28
    • Tag als Antwort aufgehoben Olaf Rabbachin Sonntag, 24. Januar 2010 13:32
    Donnerstag, 21. Januar 2010 10:27
  • Hallo allerseits,

    Problematisch mit Deiner Lösung war noch, dass damit die Animation ausblieb, die beim Hover-Exit laufen sollte (TabItemStoryBoard_Unselected ). Durch das Starten der entspr. Animation aus dem Hover-ExitTrigger heraus konnte ich das Probem aber beseitigen.

    das war wohl etwas voreilig. Als ich gerade wieder mit dieser Thematik herumspielte, musste ich feststellen, dass nun das Storyboard nicht läuft, das laufen sollte, wenn ein TabItem von Selected auf Unselected wechselt. Bin also wieder zurück bei der ursprünglichen Frage "warum laufen die Storyboards nicht".

    Cheers,
    Olaf
    Sonntag, 24. Januar 2010 13:37
  • Hallo allerseits,

    ich habe inzwischen durch viel Experimentieren die "Kombinationen" von Start/StopStoryboard herausfinden können, mittels derer der gewünschte Effekt erzielt wird. Da ich für das Posting eine stark vereinfachte Variante des Problems erzeugt hatte und ich die Problemlösung am "lebenden Subjekt" betrieben habe, werde ich das vereinfachte Beispiel aber nicht mehr für diesen Thread nachbauen. Ich werde dazu in den nächsten Tagen einen Blogeintrag schreiben und melde das dann hier.

    Falls in der Zwischenzeit noch jemand entweder mir mein mangelndes Verständnis der Storyboards austreiben könnte oder jemand das Verhalten als Bug bestätigen könnte - I'm all ears! :-)

    Cheers,
    Olaf
    Sonntag, 24. Januar 2010 15:00
  • Hallo Olaf,

    schau mir grad nochmal die Animation an. Hast du bemerkt das wenn man nach dem Starten auf ein anderes Tab klickt das Unselect Storybord für Tab1 läuft?

    J

    Günter
    Sonntag, 24. Januar 2010 18:18
  • Ich glaub das Problem liegt daran das du IsSelected zweimal mit Triggern versiehst. Es gewinnt immer die letzte und dort hast du aber das Unselect Storyboard nicht gestartet.

     

          <!-- The Style for TabItems (strips). -->

          <Style TargetType="{x:Type TabItem}">

                <Setter Property="Template">

                      <Setter.Value>

                            <ControlTemplate TargetType="{x:Type TabItem}">

                                 <Grid Height="35" VerticalAlignment="Bottom">

                                       <Border Name="Border"

                                BorderBrush="DarkGray"

                                BorderThickness="2,1,1,0"

                                CornerRadius="3,3,0,0">

                                             <ContentPresenter x:Name="ContentSite"

                                          VerticalAlignment="Center"

                                          HorizontalAlignment="Center"

                                          ContentSource="Header"

                                          Margin="7,2,12,2"

                                          RecognizesAccessKey="True"/>

                                       </Border>

                                 </Grid>

                                 <ControlTemplate.Triggers>

     

                                       <!-- The appearance of a TabItem when it's inactive/unselected -->

                                       <Trigger Property="IsSelected" Value="False">

                                             <Trigger.EnterActions>

                                                   <BeginStoryboard  x:Name="sbUnselected1" Storyboard="{StaticResource TabItemStoryBoard_Unselected}"/>

                                             </Trigger.EnterActions>

                                             <Trigger.ExitActions>

                                                   <StopStoryboard BeginStoryboardName="sbUnselected1"/>

                                             </Trigger.ExitActions>

                                             <Setter TargetName="Border" Property="Background" Value="LightGray" />

                                       </Trigger>

     

                                       <!-- The appearance of a TabItem when it's disabled (in addition to Selected=False) -->

                                       <Trigger Property="IsEnabled" Value="False">

                                             <Setter TargetName="Border" Property="Background" Value="DarkGray" />

                                       </Trigger>

     

                                       <!-- The appearance of a TabItem when the mouse hovers over it -->

                                       <MultiTrigger>

                                             <MultiTrigger.Conditions>

                                                   <Condition Property="Border.IsMouseOver" Value="True"/>

                                                   <Condition Property="IsSelected" Value="False"/>

                                             </MultiTrigger.Conditions>

                                             <MultiTrigger.EnterActions>

                                                   <BeginStoryboard x:Name="sbHover" Storyboard="{StaticResource TabItemStoryBoard_Hover}"/>

                                             </MultiTrigger.EnterActions>

                                             <MultiTrigger.ExitActions>

                                                   <StopStoryboard BeginStoryboardName="sbHover"/>

                                                   <BeginStoryboard Storyboard="{StaticResource TabItemStoryBoard_Unselected}"/>

                                             </MultiTrigger.ExitActions>

                                             <Setter TargetName="Border" Property="Background" Value="White" />

                                       </MultiTrigger>

                                      

                                       <!-- The appearance of a TabItem when it's active/selected -->

                                       <Trigger Property="IsSelected" Value="True">

                                             <Trigger.EnterActions>

                                                   <BeginStoryboard x:Name="sbSelected" Storyboard="{StaticResource TabItemStoryBoard_Selected}"/>

                                                   <StopStoryboard BeginStoryboardName="sbUnselected"/>

                                             </Trigger.EnterActions>

                                             <Trigger.ExitActions>

                                                   <StopStoryboard BeginStoryboardName="sbSelected"/>

                                                   <BeginStoryboard  x:Name="sbUnselected" Storyboard="{StaticResource TabItemStoryBoard_Unselected}"/>

                                             </Trigger.ExitActions>

                                             <Setter TargetName="Border" Property="Background" Value="LightSalmon" />

                                             <Setter TargetName="Border" Property="BorderBrush" Value="LightSalmon" />

                                       </Trigger>                                    

                                 </ControlTemplate.Triggers>

                            </ControlTemplate>

                      </Setter.Value>

                </Setter>

          </Style>


    Wenn man es dort auch startet dann wird die Animation ausgeführt.

    Jetzt muss ich zu meiner Frau die ist schon eifersüchtig.
    Sonntag, 24. Januar 2010 19:24
  • Hi Günter,

    entgegen meiner Aussage im vorigen Posting funktionierte es dann doch noch nicht. Ich war eigentlich sicher, dass alles so lief, wie's sein sollte, aber nach ein wenig Herumspielen habe ich dann das Gegenteil feststellen müssen und - vollkommen frustriert - die Kiste ausgemacht ...

    Ich glaub das Problem liegt daran das du IsSelected zweimal mit Triggern versiehst. Es gewinnt immer die letzte und dort hast du aber das Unselect Storyboard nicht gestartet.
    [... XAML ...]
    Wenn man es dort auch startet dann wird die Animation ausgeführt.

    Ich habe den TabItem-Style mal übernommen und ausprobiert - da läuft jetzt wiederum das Hover-SB nicht mehr, wenn ein Tab erst einmal selektiert wurde. :-)
    Das Problem in Sachen StopStoryboard ist doch, dass der ExitTrigger einer Aktion nicht wissen kann, welches Storyboard als nächstes gestartet wird. Insofern dürfte IMHO der ExitTrigger ausschließlich das "eigene" Storyboard stoppen, nicht aber ein anderes starten.
    Laut Doku müsste es eigentlich nicht einmal notwendig sein, ein Storyboard zu stoppen, denn in meinem Beispiel laufen sie ja allesamt nur ein einziges Mal?!

    Habe es auch gerade noch einmal ausprobiert - wenn ich es wie o.a. mache, läuft alles augenscheinlich korrekt, aber bei genauerem Hinsehen sieht man, dass das Unselected-SB nicht beim Hover-Exit läuft. Sprich, egal an welcher Schraube ich drehe, irgendeine Animation fällt flach.

    Meine Vermutung ist, dass es da ein Problem bzw. einen Bug gibt, kann's mir einfach nicht anders erklären ...
    BTW - habe gerade gesehen, dass im hier geposteten Beispiel im Debug-Fenster Informationen zum Start/Ende eines Storyboards ausgegeben werden. Das ist mir vorher noch nicht aufgefallen. Allerdings sehe ich das im "echten" TabControl wiederum nicht. Ich hatte bislang nur per Trigger-Tracing überprüfen können, welcher Trigger wann läuft, aber für die Storyboards selbst konnte ich das nicht. Gibt's da eine Einstellung ..?
    Jedenfalls sehe ich da auch, dass eine EnterTrigger-Animation nach deren Ablauf wieder angehalten wird, die StopStoryboard-Anweisung sollte damit eigentlich überflüssig sein ...

    Jetzt muss ich zu meiner Frau die ist schon eifersüchtig.

    LOL. Kenne ich doch irgendwoher ... :-)

    Cheers,
    Olaf
    Montag, 25. Januar 2010 09:00
  • Sorry, ich hab nur auf die Unselect Animation geschaut und wie gesagt meine Frau wollte wieder beachtet werden ;), da ist mir die Hoover Animation nicht abgegangen.

    Wenn es ein Bug ist, ist er auch noch in WPF .Net 4.0 drinnen, habs grad probiert.

    Ich würde versuchen das Ganze mit nur einen Trigger für IsSelected (entweder den False oder den True) zu realisieren.
    Hast du auch mal an VisualStateManager gedacht?

    Tracing von Animationen:

    <configuration>
      <system.diagnostics>
        <sources>
          <source name="System.Windows.Media.Animation"
    switchName="SourceSwitch" >
            <listeners>
              <add name="textListener" />
            </listeners>
          </source></sources>
        <switches>
          <add name="SourceSwitch" value="All" />
        </switches>
        <sharedListeners>
          <add name="textListener"
               type="System.Diagnostics.TextWriterTraceListener"
               initializeData="Debug.txt" />
        </sharedListeners>
        <trace autoflush="true" indentsize="4"></trace>
      </system.diagnostics>
    </configuration>

    Ich hab es selbst noch nicht probiert, aber mit dem Performance Profiling Tools kann man sich etwas mitschreiben lassen - Siehe Link (am Ende des Beitrags findest du es).

    http://msdn.microsoft.com/en-us/library/system.diagnostics.presentationtracesources.aspx

    Montag, 25. Januar 2010 10:22
  • Hi Günter,

    Ich würde versuchen das Ganze mit nur einen Trigger für IsSelected (entweder den False oder den True) zu realisieren.
    Hast du auch mal an VisualStateManager gedacht?


    jau, habe ich. Aber da ich damit unter VS2008 an einigen Stellen noch (Designer-) Probleme feststellen musste, wollte ich gern ohne. Habe mich allerdings auch noch nicht allzu ausgiebig mit dem VSM beschäftigt. Sprich, das Thema habe ich einfach bis zur VS2010 final verschoben.

    Tracing von Animationen:
    [...]
    Ich hab es selbst noch nicht probiert, aber mit dem Performance Profiling Tools kann man sich etwas mitschreiben lassen - Siehe Link (am Ende des Beitrags findest du es).

    http://msdn.microsoft.com/en-us/library/system.diagnostics.presentationtracesources.aspx


    Danke für den Link. Als ich mich wegen des Problems intensiver mit Möglichkeiten der Nachverfolgung zu beschäftigen begonnen hatte, bin ich über das "Data See, Data Do" Blog gestolpert - da steht quasi genau das drin, was Du auch schon genannt hattest, resp. was sich hinter dem MSDN-Link findet (und mehr). Witzigerweise habe ich in meiner app.config - in die ich alle tracing-Einstellungen eingefügt hatte (die meisten davon allerdings auskommentiert), identische (aktive) Einstellungen. Hast Du das schon einmal ausprobiert? Ich finde nämlich beim besten Willen die Datei "Debug.txt" nicht, die ja eigentlich die tracing-Infos beinhalten soll; habe schon den ganzen Rechner abgesucht und auch (händisch) Pfade hinzugefügt, aber die Datei findet sich einfach nicht. Gleiches gilt im übrigen auch für die Console und XmlWriter TraceListeners - ich bekomme einfach keinen Output.

    Aber, was soll ich sagen (ich sollte ja inzwischen vorsichtig sein mit "Positiväusserungen") - ich glaube, ich habe es inzwischen doch noch hinbekommen, jedenfalls kann ich momentan kein Fehlverhalten mehr feststellen (nach radikalen Umbaumaßnahmen). Werde jetzt erst mal etwas mampfen gehen und alles ein wenig sacken lassen, evt. findet sich ja danach doch noch ein Fehler. Ich habe ohnehin sonst absolut nichts zu tun, deswegen beschäftige ich mich so ausgiebig mit unbezahltem Kram ... ;-)




    Cheers,
    Olaf
    Montag, 25. Januar 2010 12:29
  • Huhu!

    Ich mal mal wieder die Ingrid:

    [Olaf:]
    Aber, was soll ich sagen (ich sollte ja inzwischen vorsichtig sein mit "Positiväusserungen") - ich glaube, ich habe es inzwischen doch noch hinbekommen, jedenfalls kann ich momentan kein Fehlverhalten mehr feststellen (nach radikalen Umbaumaßnahmen). Werde jetzt erst mal etwas mampfen gehen und alles ein wenig sacken lassen, evt. findet sich ja danach doch noch ein Fehler.

    Scheint mir jetzt tatsächlich vernünftig zu funktionieren. Ich habe das vereinfachte Beispiel-Window doch noch einmal entspr. angepasst und kann auch dort kein Problem mehr feststellen:

    <Window x:Class="WpfTests.StyledTabControl_AnimationTest"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="Animating TabItems (resolved)" 
            Height="200" 
            Width="400">
       <Window.Resources>
          <Storyboard x:Key="TabItemStoryBoard_Default" x:Name="res_sbUnselected_Default">
             <ThicknessAnimation Storyboard.TargetName="Border" 
                                 Storyboard.TargetProperty="Margin"
                                 To="0,4,0,0"
                                 FillBehavior="HoldEnd"
                                 Duration="0:0:0.1"/>
          </Storyboard>
    
          <Storyboard x:Key="TabItemStoryBoard_Unselected" x:Name="res_sbUnselected">
             <ThicknessAnimation Storyboard.TargetName="Border" 
                                 Storyboard.TargetProperty="Margin"
                                 To="0,8,0,0"
                                 FillBehavior="HoldEnd"
                                 Duration="0:0:0.1"/>
          </Storyboard>
          <Storyboard x:Key="TabItemStoryBoard_Selected" x:Name="res_sbSelected">
             <ThicknessAnimation Storyboard.TargetName="Border" 
                                 Storyboard.TargetProperty="Margin"
                                 To="0,0,0,0"
                                 FillBehavior="HoldEnd"
                                 Duration="0:0:0.1"/>
          </Storyboard>
          <Storyboard x:Key="TabItemStoryBoard_Hover" x:Name="res_">
             <ThicknessAnimationUsingKeyFrames Storyboard.TargetName="Border" 
                                               Storyboard.TargetProperty="Margin">
                <SplineThicknessKeyFrame KeyTime="0:0:0.1"
                                         Value="0,2,0,0"/>
                <SplineThicknessKeyFrame KeyTime="0:0:0.2"
                                         Value="0,4,0,0"/>
             </ThicknessAnimationUsingKeyFrames>
          </Storyboard>
    
          <!-- The Style for TabItems (strips). -->
          <Style TargetType="{x:Type TabItem}">
             <Setter Property="Template">
                <Setter.Value>
                   <ControlTemplate TargetType="{x:Type TabItem}">
                      <Grid Height="35" VerticalAlignment="Bottom">
                         <Border Name="Border"
                                    BorderBrush="DarkGray"
                                    BorderThickness="2,1,1,0" 
                                    CornerRadius="3,3,0,0">
                            <ContentPresenter x:Name="ContentSite"
                                              VerticalAlignment="Center"
                                              HorizontalAlignment="Center"
                                              ContentSource="Header"
                                              Margin="7,2,12,2"
                                              RecognizesAccessKey="True"/>
                         </Border>
                      </Grid>
                      <ControlTemplate.Triggers>
    
                         <!-- The appearance of a TabItem when it's inactive/unselected -->
                         <MultiTrigger>
                            <MultiTrigger.Conditions>
                               <Condition Property="Border.IsMouseOver" Value="False"/>
                               <Condition Property="IsSelected" Value="False"/>
                            </MultiTrigger.Conditions>
                            <MultiTrigger.EnterActions>
                               <BeginStoryboard x:Name="sbUnselected" Storyboard="{StaticResource TabItemStoryBoard_Unselected}"/>
                            </MultiTrigger.EnterActions>
                            <MultiTrigger.ExitActions>
                               <StopStoryboard BeginStoryboardName="sbUnselected"/>
                            </MultiTrigger.ExitActions>
                            <Setter TargetName="Border" Property="Background" Value="LightGray" />
                         </MultiTrigger>
    
                         <!-- The appearance of a TabItem when it's disabled (in addition to Selected=False) -->
                         <Trigger Property="IsEnabled" Value="False">
                            <Setter TargetName="Border" Property="Background" Value="DarkGray" />
                         </Trigger>
    
                         <!-- The appearance of a TabItem when the mouse hovers over it -->
                         <MultiTrigger>
                            <MultiTrigger.Conditions>
                               <Condition Property="Border.IsMouseOver" Value="True"/>
                               <Condition Property="IsSelected" Value="False"/>
                            </MultiTrigger.Conditions>
                            <MultiTrigger.EnterActions>
                               <StopStoryboard BeginStoryboardName="sbUnselected_Hover_Exit"/>
                               <BeginStoryboard x:Name="sbHover" Storyboard="{StaticResource TabItemStoryBoard_Hover}"/>
                            </MultiTrigger.EnterActions>
                            <MultiTrigger.ExitActions>
                               <BeginStoryboard x:Name="sbUnselected_Hover_Exit" Storyboard="{StaticResource TabItemStoryBoard_Unselected}"/>
                            </MultiTrigger.ExitActions>
                            <Setter TargetName="Border" Property="Background" Value="White" />
                         </MultiTrigger>
    
                         <!-- The appearance of a TabItem when it's active/selected -->
                         <Trigger Property="IsSelected" Value="True">
                            <Trigger.EnterActions>
                               <StopStoryboard BeginStoryboardName="sbUnselected_Selected_Exit"/>
                               <BeginStoryboard x:Name="sbSelected" Storyboard="{StaticResource TabItemStoryBoard_Selected}"/>
                            </Trigger.EnterActions>
                            <Trigger.ExitActions>
                               <BeginStoryboard x:Name="sbUnselected_Selected_Exit" Storyboard="{StaticResource TabItemStoryBoard_Unselected}"/>
                            </Trigger.ExitActions>
                            <Setter TargetName="Border" Property="Background" Value="LightSalmon" />
                            <Setter TargetName="Border" Property="BorderBrush" Value="LightSalmon" />
                         </Trigger>
                      </ControlTemplate.Triggers>
                   </ControlTemplate>
                </Setter.Value>
             </Setter>
          </Style>
       </Window.Resources>
    
       <TabControl x:Name="tc" Margin="5" 
                   SelectedIndex="0"
                   Background="LightSalmon"
                   BorderBrush="LightSalmon">
          <TabItem Header="Tab 1"/>
          <TabItem Header="Tab 2"/>
          <TabItem Header="Tab 3" IsEnabled="False"/>
          <TabItem Header="Tab 4"/>
          <TabItem Header="Tab 5"/>
       </TabControl>
    </Window>
    

    Änderungen gegenüber den Vorgänger-Versionen:
    Ich habe den Unselected-Trigger nun in einen MultiTrigger überführt; außerdem werden die in den Exit-Triggern gestarteten Aufrufe des Unselected-Storyboards differenziert benamst und in den jew. EnterActions explizit wieder gestoppt. Macht in meinen Augen zwar absolut keinen Sinn, aber nur so habe ich es zum Werkeln überreden können ...

    Cheers,
    Olaf
    Montag, 25. Januar 2010 13:35
  • Huhu!

    Ich habe die Geschichte inzwischen auch in meinem Blog verarbeitet. Wen's interessiert (englisch): WPF: Styling the TabControl and its TabItems - Part 2: Animating TabItems
    Cheers,
    Olaf
    Mittwoch, 27. Januar 2010 09:11