Nir Hasson
Topic Author
Posts: 71
Joined: 10 Nov 2013, 21:20
Contact:

Invoke Storyboard on specific DataTemplate Instance

09 Feb 2014, 17:26

I have an ItemsControl that holds multiple items with a custom DataTemplate as the ItemTemplate property.
On my model-view code I have a data manager that holds list of visible items bounded to the ItemsSource property of the ItemsControl.

The display screen can't show all items so I'd like to create some cross fade in/out animation that will be applied on certain item. I would like to play the animation and at the end of it I would like to change the content of the data collection to hold new set of data items to be displayed.
I've manage to create a Storyboard within the scope of my DataTemplate.Resources but from this point I'm kinda lost ... :?

Any suggestions to accomplish this task would be great..

Thanks
 
TheFabFab
Posts: 4
Joined: 22 Jan 2014, 21:29

Re: Invoke Storyboard on specific DataTemplate Instance

09 Feb 2014, 22:34

 
User avatar
sfernandez
Site Admin
Posts: 2997
Joined: 22 Dec 2011, 19:20

Re: Invoke Storyboard on specific DataTemplate Instance

10 Feb 2014, 14:52

As I explained in a previous post, you can obtain the container of your data item. Then you can apply the storyboard to that container:
void ApplyStoryboard(BaseComponent dataItem, Storyboard storyboard)
{
    FrameworkElement element = FindContainerFromItem(dataItem).As<FrameworkElement>();
    if (element != null)
    {
        storyboard.Completed += this.OnAnimationCompleted;
        storyboard.Begin(element);
    }
}

void OnAnimationCompleted(BaseComponent sender, TimelineEventArgs e)
{
    Storyboard storyboard = sender.As<Storyboard>();
    if (storyboard != null)
    {
        storyboard.Completed -= this.OnAnimationCompleted;
        // do something
    }
}
 
Nir Hasson
Topic Author
Posts: 71
Joined: 10 Nov 2013, 21:20
Contact:

Re: Invoke Storyboard on specific DataTemplate Instance

10 Feb 2014, 17:25

The suggested implementation didn't work for me.
Here is the code I used to grab ListBoxItem from the DataItem.
//-----------------------------------------------------------------------------
Noesis::Gui::ListBoxItem* NoesisGUIUtils::ListBoxItemFromDataItem(Noesis::Gui::ListBox* pListBox, Noesis::Core::BaseComponent* pDataItem)
	{
		// Assuming you named ItemsHost panel in your ListBox template
		// Otherwise, you have to find it iterating ListBox children with
		// VisualTreeHelper,and looking for a Panel with the IsItemsHost
		// property set to true

		Noesis::Gui::Panel* pItemsPanel = NULL;

		for (NsSize nVisualChild=0; nVisualChild < Noesis::Gui::VisualTreeHelper::GetChildrenCount(pListBox); ++nVisualChild)
		{
			Noesis::Gui::Visual* pCurVisualChild = Noesis::Gui::VisualTreeHelper::GetChild(pListBox, nVisualChild);
			Noesis::Gui::Panel* pCurPanel = NsDynamicCast<Noesis::Gui::Panel*>(pCurVisualChild);

			if (pCurPanel != NULL && pCurPanel->GetIsItemsHost())
			{
				pItemsPanel = pCurPanel;
				break;
			}
		}

		if (pItemsPanel == NULL)
		{			
			return NULL;			
		}
		

		Noesis::Gui::UIElementCollection* pCollection = pItemsPanel->GetChildren();

		// Search the requested list box item
		NsSize numItems = pCollection->Count();
		for (NsSize i = 0; i < numItems; ++i)
		{
			Noesis::Core::BaseComponent* container = pCollection->Get(i);
			Noesis::Gui::ListBoxItem* pListBoxItem = NsStaticCast<Noesis::Gui::ListBoxItem*>(container);

			// ListBoxItem contains the instantiation of data template visual tree
			Noesis::Gui::FrameworkElement* element = NsStaticCast<Noesis::Gui::FrameworkElement*>(
				pListBoxItem->GetContent());

			// Data item is set as DataContext of the DataTemplate root element
			if (element != NULL && element->GetDataContext() == pDataItem)
			{
				// Found!! :)
				return pListBoxItem;
			}
		}

		// Data item not found
		return NULL;
	}
When I use it that way the ItemsPanel is never found although I explicitly declare it in the XAML code.
I'm getting a Panel object but the GetIsItemsHost condition fails.

<ListBox x:Name="SourceListBox"
			ItemsSource="{Binding Items}"
			ItemTemplate="{StaticResource MyItemTemplate}" Width="400" HorizontalAlignment="Left" Height="600">
			<ListBox.ItemsPanel>
				<ItemsPanelTemplate x:Name="ItemsHostPanel">
					<StackPanel IsItemsHost="True"/>
				</ItemsPanelTemplate>
			</ListBox.ItemsPanel>
		</ListBox>
When I force the first found Panel to be selected as ItemsPanel I'm getting fixed number of 8 items in the element collection object and it never finds the container item.

Please advice.
Nir
 
User avatar
sfernandez
Site Admin
Posts: 2997
Joined: 22 Dec 2011, 19:20

Re: Invoke Storyboard on specific DataTemplate Instance

10 Feb 2014, 18:47

I think I didn't explain well enough how to find the ItemsHost panel.

If you are not able to name the panel yourself because you are using a predefined template for the ItemsControl, then you have to iterate through its children, including all descendants (you don't know how deep in the template's visual tree is the ItemsHost panel):
Panel* GetItemsHost(FrameworkElement* element)
{
    NsSize numChildren = VisualTreeHelper::GetChildrenCount(element);
    for (NsSize i = 0; i < numChildren; ++i)
    {
        FrameworkElement* child = NsDynamicCast<FrameworkElement*>(
            VisualTreeHelper::GetChild(element, i));

        Panel* panel = NsDynamicCast<Panel*>(child);
        if (panel != 0 && panel->GetIsItemsHost())
        {
            return panel;
        }

        panel = GetItemsHost(child);
        if (panel != 0)
        {
            return panel;
        }
    }

    return 0;
}

// ... then use that function over your items control
Panel* itemsHost = GetItemsHost(myListBox);
 
Nir Hasson
Topic Author
Posts: 71
Joined: 10 Nov 2013, 21:20
Contact:

Re: Invoke Storyboard on specific DataTemplate Instance

10 Feb 2014, 22:51

Thank you very much !
It works now :)

Who is online

Users browsing this forum: Ahrefs [Bot] and 34 guests