Answered Listbox template animation

  • Friday, January 21, 2011 8:11 AM
     
      Has Code

    I need to some help with ListBox and animation in wpf. Here is a snippet of the the listbox xaml code:

    <ListBox x:Name="ViewListBox" 
    	Background="Transparent" 
    	BorderBrush="Transparent"
    	VerticalAlignment="Top"
    	HorizontalAlignment="Stretch"
    	HorizontalContentAlignment="Stretch"
    	ItemsSource="{Binding Items}" 
    	ScrollViewer.HorizontalScrollBarVisibility="Disabled"
    	ScrollViewer.VerticalScrollBarVisibility="Disabled"
    	DockPanel.Dock="Top">    
    				 
    	<ListBox.ItemTemplate>
    		<DataTemplate>
    			<Border Loaded="ItemBorderLoaded">                
    				<UserControl:MyItem Padding="0" ... />
    			</Border>              
    		</DataTemplate>
    	</ListBox.ItemTemplate>
    </ListBox>

    Here is a snippet of the classes used;

    public class ViewListViewModel : INotifyPropertyChanged, IDisposable
    {
    	private ObservableCollection<ItemViewModel> m_Items;
    	public ObservableCollection<ItemViewModel> Items
    	{
    		get
    		{
    			return m_Items;
    		}
    	}
    	
    	private void deleteRow(ItemViewModel item)
    	{
    		Items.Remove(item);
    	}
    }
    
    public class ItemViewModel : INotifyPropertyChanged
    {
    
    }

    Currently this code will remove an item when the deleteRow method is executed. I would like to add an animation effect to the deleting of a row. The animation that I had in mind was to reduce the height of the row from its original to 0 within 1 second and then remove it. Basically something like this:

    DoubleAnimation ani = new DoubleAnimation();
    ani.From = item.ActualHeight;
    ani.To = 0;
    ani.Duration = new Duration(TimeSpan.FromMilliseconds(1000));
    item.BeginAnimation(DockPanel.HeightProperty, ani);
    This code example works on a dockpanel. How can I make it work on a specific item within a listbox template as described above?

    Please let me know if you need more information or if something is unclear.

All Replies

  • Friday, January 21, 2011 3:40 PM
     
      Has Code

    Here is what I got so far ( a bit closer to a solution ):

    When a row shall be deleted, I set the Visibility property on the ItemViewModel to 'Hidden' and then I added the below WPF storyboard.

    <ListBox.ItemContainerStyle>
    	<Style TargetType="ListBoxItem">	
    		<Style.Triggers>
    			<DataTrigger Binding="{Binding Visibility}" Value="Hidden">
    				<DataTrigger.EnterActions>
    					<BeginStoryboard>
    						<Storyboard>
    							<DoubleAnimation Duration="0:0:1" 
    							Storyboard.TargetProperty="Height" From="100" To="0" />
    						</Storyboard>
    					</BeginStoryboard>
    				</DataTrigger.EnterActions> 
    			</DataTrigger>
    		</Style.Triggers>
    	</Style>            
    </ListBox.ItemContainerStyle>
    
    

    This manages to reduce the height of a specific row as requested. However, one small problem remains, I need to actually delete the row from the collection.

    Subscribing to the Complete event on the StoryBoard element did not compile stating "The event 'Completed' cannot be specified on a Target tag in a Style. Use an EventSetter instead.".

    Another approach that I though about was to use the DataTrigger.ExitActions elements. What I would like is to either write a state or pass an event back to the ItemViewModel class that the animation is finished and it is safe to delete the row from the collection. Any thoughts on how to archive this?

     

  • Monday, January 24, 2011 3:18 AM
    Moderator
     
     Answered

    Hi Lars1346,

    Based on your description, I think the best solution is "Animation.Completed" event, add below code snippet to your "Delete Button":

    private void Button_Click(object sender, RoutedEventArgs e)

            {

                Test current = this.ViewListBox.SelectedItem as Test;

                ListBoxItem lbi = this.ViewListBox.ItemContainerGenerator.ContainerFromItem(current) as ListBoxItem;

                DoubleAnimation da = new DoubleAnimation();

                da.From = 10; da.To = 0;

                da.Duration = TimeSpan.FromSeconds(1);

                da.Completed += (o, e3) =>

                {

                    Tests.Remove(current);

                };

                lbi.BeginAnimation(FrameworkElement.HeightProperty, da);

            }

    I have tried, and it works welll on my side.

     

    Best regards,


    Sheldon _Xiao [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

  • Monday, January 24, 2011 10:12 AM
     
      Has Code

    Thanks for an excellent reply Sheldon_Xiao. The example worked perfectly.

    However, there is one scenario that I did not think about at the time of posting. Here is an updated version of your code:

    private void Button_RemoveAll(object sender, RoutedEventArgs e)
    {
    	IList<ItemViewModel> removeItemList = new List<ItemViewModel>();
    	// iterate collection and find multiple items to remove
    	// and insert them into removeItemList
    	
    	foreach (ItemViewModel current in removeItemList)
    	{	
    		ListBoxItem lbi = this.ViewListBox.ItemContainerGenerator.ContainerFromItem(current) as ListBoxItem;
    		DoubleAnimation da = new DoubleAnimation();
    		da.From = 10; da.To = 0;
    		da.Duration = TimeSpan.FromSeconds(1);
    		da.Completed += (o, e3) =>
    		{
    			Tests.Remove(current);
    		};
    		lbi.BeginAnimation(FrameworkElement.HeightProperty, da);
    	}
    }

    In this case I need to remove multiple rows at the same time. The problem here is that the complete callback wont work with the above code because the 'current' ItemViewModel will have changed by the time the callback is triggered. The DoubleAnimation class does not contains any Tag attribute and the input parameters does not contains any reference to the correct ItemViewModel instance (the sender argument refers to AnimationClock and e is null). So how can I execute the Completed callback with a reference to the right ItemViewModel?

     

    • Edited by Lars1346 Monday, January 24, 2011 10:30 AM
    •  
  • Monday, January 24, 2011 10:28 AM
    Moderator
     
     

    Hi Lars1346,

    As for your second issue:

    -->   I need to remove multiple rows at the same time

    I think it is another issue in this thread, I think you could post as a new thread, and then more people will jump in, and help you and me to resolve this issue, you could get better and quicker responses, and I will go on working on your issue with others.

     

    On the other hand, it will be very beneficial for others having the similar issue with this thread to find the solution.

     

    Best regards,


    Sheldon _Xiao [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

  • Monday, January 24, 2011 10:48 AM
     
     

    Thanks for your quick response. I have marked your answer as correct and made a new thread for my follow up question.

    Here is a link to the new thread: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/c5ff35b0-62d1-4e44-b0b1-f129b67da7d0

    Thanks again for your help.