views:

218

answers:

1

In the handler for the Completed event of a Storyboard, how do i get the element that the storyboard was being applied to?

My Storyboard is part of an ItemTemplate:

<ListBox x:Name="MyListBox" >
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Grid x:Name="Container" Height="30" >
                <Grid.Resources>
                    <Storyboard x:Name="FadeOut" BeginTime="0:0:7"  Completed="FadeOut_Completed">
                        <DoubleAnimation From="1.0" To="0.0" Duration="0:0:3" Storyboard.TargetName="Container" Storyboard.TargetProperty="Opacity" />
                    </Storyboard>
                </Grid.Resources>

                [...snip...]

            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

in the Completed event i want to grab the grid called Container so that i can do nasty things with its DataContext. Can this be done, or am i going about it the wrong way?

Thanks :)

A: 

The answer to this is that it is not possible - not in Silverlight 3 anyway.

Using a debugger i was able to find a private property of the Storyboard that when i walked it up the object tree i got to the containing template item - however i couldn't touch this from code using reflection due to the security restrictions placed upon silverlight apps (this may well be possible in WPF though).

My eventual solution involved using a Dictionary<Storyboard, Grid>, and a couple of event handlers. With the template i attached a Loaded handler, this means my handler gets called everytime an instance of the template is created and loaded (i.e. for every data item that is bound to the listbox). At this point, i have a reference to the physical instance of the template, so i can search its children for the storyboard:

private void ItemTemplate_Loaded(object sender, RoutedEventArgs e)
{
    Storyboard s = getStoryBoard(sender);
    if (s != null)
    {
        if (!_startedStoryboards.ContainsKey(s))
            _startedStoryboards.Add(s, (Grid)sender);
    }
}

private Storyboard getStoryBoard(object container)
{
    Grid g = container as Grid;
    if (g != null)
    {
        if (g.Resources.Contains("FadeOut"))
        {
            Storyboard s = g.Resources["FadeOut"] as Storyboard;
            return s;
        }
    }
    return null;
}

private Dictionary<Storyboard, Grid> _startedStoryboards = new Dictionary<Storyboard, Grid>();

Then when the storyboard's Completed event is fired, i can easily use this dictionary as a lookup to retrieve the item template it was hosted within, and from there i can get the DataContext of the item template and do the nasty things i planned:

private void FadeOut_Completed(object sender, EventArgs e)
{
    if (_startedStoryboards.ContainsKey((Storyboard)sender))
    {
        Grid g = _startedStoryboards[(Storyboard)sender];
        if (g.DataContext != null)
        {
            MyDataItem z = g.DataContext as MyDataItem;

            if (z != null)
            {
                ... do my thing ...
            }
        }
    }
}

[Note: this code has been sanitized for public viewing, excuse any small discrepancies or syntactical errors you may spot]

slugster