views:

506

answers:

1

Here's the situation... at the top level, I have a TabControl. Each page in the TabControl consists of a ListBox:

<TabControl>
    <TabItem Header="item 1">
        <ListBox>
            <ListBoxItem>sub item 1</ListBoxItem>
            <ListBoxItem>sub item 2</ListBoxItem>
            <ListBoxItem>sub item 3</ListBoxItem>
        </ListBox>
    </TabItem>
    <TabItem Header="item 2">
        <ListBox>
            <ListBoxItem>sub item 1</ListBoxItem>
            <ListBoxItem>sub item 2</ListBoxItem>
        </ListBox>
    </TabItem>
</TabControl>

The ListBoxes have a horizontally-oriented StackPanel as their ListTemplate:

<Style TargetType="ListBox">
    <Setter Property="ItemsPanel">
        <Setter.Value>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal"
                      VisibleChanged="onStackPanelVisibilityChange"
                      Loaded="onStackPanelLoaded"
                      VerticalAlignment="Center" HorizontalAlignment="Center" />
            </ItemsPanelTemplate>
        </Setter.Value>
    </Setter>
</Style>

You'll notice I have some event handlers on that stack panel. Those are to animate the items within the stack panel, so they fade into view sequentially. The event handlers are implemented as:

    void onStackPanelLoaded(object sender, RoutedEventArgs e)
    {
        StackPanel panel = sender as StackPanel;

        applySubItemAnimations(panel);
    }

    void onStackPanelVisibilityChange(object sender, DependencyPropertyChangedEventArgs e)
    {
        StackPanel panel = sender as StackPanel;

        if (panel.IsVisible)
        {
            applySubItemAnimations(panel);
        }
    }

    private void applySubItemAnimations(StackPanel panel)
    {
        DoubleAnimation fadeIn = new DoubleAnimation();
        fadeIn.DecelerationRatio = 0.1;
        fadeIn.Duration = new Duration(new TimeSpan(0, 0, 0, 0, 500));
        fadeIn.From = 0.0;
        fadeIn.To = 1.0;

        for (int i = 0; i < panel.Children.Count; i++)
        {
            panel.Children[i].Opacity = 0.0;
            fadeIn.BeginTime = new TimeSpan(0, 0, 0, 0, 200 * i + 50);
            panel.Children[i].BeginAnimation(UIElement.OpacityProperty, fadeIn);
        }
    }

For the most part, this works great. When you first click on (or load) a tab, the sub-items in the stack panel fade into view one-after-the-other. The problem is when you click back to a tab that's already been shown once before (ie, you're in the "VisibleChanged" event handler rather than the "Loaded" handler), all the items are already shown and they blink in order, rather than starting as hidden and being shown in order.

Here's where it gets ugly. This line:

panel.Children[i].Opacity = 0.0;

... does nothing. If I step through the code in the debugger and put a watch on "panel.Children[i].Opacity", it stays right at 1.0. No exception or anything. It just... doesn't work.

Any ideas?

+2  A: 

I have a guess on what might be happening: WPF does not remove animations once they are completed, so when your code runs through the applySubItemsAnimations method for the second time, the previous animations are still there. Therefore you could try to remove these animations by passing null as second parameter to

panel.Children[i].BeginAnimation(UIElement.OpacityProperty, null);

Afterwards, you can apply the new animation, so the whole for-loop would look like that:

for (int i = 0; i < panel.Children.Count; i++)
{            
    panel.Children[i].Opacity = 0.0;            
    fadeIn.BeginTime = new TimeSpan(0, 0, 0, 0, 200 * i + 50);            
    panel.Children[i].BeginAnimation(UIElement.OpacityProperty, null);        
    panel.Children[i].BeginAnimation(UIElement.OpacityProperty, fadeIn);        
}
EFrank
That totally worked. Thanks for the help!
dkeen
Had exactly the same issue as this and funnily enough, it sounds like dkeen is writing almost exactly the same type of control as I am! Thanks.
Luke Puplett