Animating ItemsControl items - wpf

I am a bit stuck as to how to approach this problem.
I have a tabcontrol on my solution and on a datatrigger I hide the tabcontrol and show an itemscontrol in its place with each item(tab) in a shape of a rectangular tile. And on selecting (simple mousedown) a 'tile' (another datatrigger ) I hide the itemscontrol and show the tabcontrol with the selected tile as the selected tab.
The above works fine without any issue. Now I am trying to see if I can animate this whole process to make it look nice visually. My goal is to make all the tiles appear nicely with a fade in effect when the itemscontrol becomes visible and then fade nice gracefully when the itemscontrol become collapsed.
My idea is to actually set all the items opacity to 0 to start with then run a DoubleAnimation setting the opacity back to 1 on the datatrigger(giving a nice fade in effect). But I am not sure really how to approach this problem.
Can anyone help me with this one please? Sorry I havent posted any xaml but my efforts trying to animate a listbox instead, failed
Edit- Posting my efforts so far. Ideally I want it to work with an ItemsControl instead of a listbox as I dont need any interactions such as SelectedItem or anything
please let me know if my approach itself is wrong so that I dont end up in wrong direction.
I started with a listbox and in my resources i got the below style. Please ignore my multi trigger as I had few more conditions earlier but left it like that.
<Style x:Key="CustomStyle" TargetType="{x:Type ListBoxItem}">
<Setter property="opacity" value="0"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBox}},Path=Visibility}" Value="true"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard Duration="0:0:1">
</ObjectAnimationUsingKeyFrames>-->
<DoubleAnimation Duration="0:0:1" Storyboard.TargetProperty="Opacity" To="1"/>
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.EnterActions>
</MultiDataTrigger>
</Style.Triggers>
</Style>
Then my listbox style
<ListBox Background="Transparent" ItemsSource="{Binding Children}" Visibility="{Binding ShowAnimation,Converter={StaticResource BooleanToVisibilityConverter}}">
<ListBox.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding ShowAnimation}" Value="True">
<Setter Property="ListBox.ItemContainerStyle" Value="{StaticResource CustomStyle}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Style>
</ListBox>

Related

How do I set the style of a child based on AlternateIndex of parent?

I'm using a HeaderedItemsControl. Each item is a 3 column Grid with a Border and TextBlock in each column. I would like the background color of the Borders in each item to alternate. (Basic alternating row background effect.) I have attempted to create a style at the UserControl level for the Grid that applies a background color to all borders within it, based on the AlternationIndex of the containing control:
<Style TargetType="Grid" x:Key="myItemsGrid">
<Style.Resources>
<Style TargetType="Border">
<Setter Property="Background" Value="Azure" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=AlternationIndex, RelativeSource={RelativeSource AncestorType=ItemsControl}}" Value="2">
<Setter Property="Background" Value="{StaticResource color_LogoLight}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Style.Resources>
</Style>
The Setter bit is working, as the borders are all "Azure". But how do I properly reference the AlternationIndex so that the border background color changes for every other row. I tried pointing the RelativeSource to HeaderedItemsControl and ItemsControl, but neither seems to be the proper target. I have browsed the live visual tree, but I can't find anything to reference, there.
Any help is appreciated.
You have to look for AlternationIndex on the Item of ItemsControl, not on ItemsControl itself! But which type you have to search in binding for? For example in ListBox it's a ListBoxItem and in ItemsControl it's a ContentPresenter.
Don't forget Path=(ItemsControl.AlternationIndex) and for your case (AlternationIndex==2) you have to set AlternationCount in ItemsControl at least to 3! So this code should work:
<DataTrigger Binding="{Binding Path=(ItemsControl.AlternationIndex), RelativeSource={RelativeSource AncestorType=ContentPresenter}}" Value="2">
<Setter Property="Background" Value="{StaticResource color_LogoLight}" />
</DataTrigger>

Time Delay on Trigger

I wish to attach a time delay to a mouseover event on a WPF expander I have on my form (xaml supported by VB.NET code behind). This mouseover event essentially triggers the expansion as oppose to clicking - but I'd like a short wait before the content is expanded. So far I have not managed to find anything to solve this via the wider internet.
The current xaml code to enable the trigger is:
<Style x:Key="HoverExpander" TargetType="{x:Type Expander}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="IsExpanded" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
This style is then applied to:
<Expander Style="{StaticResource HoverExpander}"
HorizontalAlignment="Right"
ExpandDirection="Left"
Height="Auto"
Width="Auto">
<!-- Content here -->
</Expander>
Note that I've stripped out other aesthetics (such as borders, gridrefs etc for readability).
I think there should be some way to set a delay on the MouseOver Trigger but haven't had much luck finding it. This could either be set in xaml or perhaps as an event in the code behind.
I'm working on this currently, so when I find a solution I shall post it here. Grateful for any ideas meantime. Thanks!
Use an EventTrigger on the MouseOver event and a Storyboard with a BooleanAnimationUsingKeyFrames instead. In the Timeline of the Storyboard, you could have KeyFrames, so that the animation waits for some time before it affects the properties you want to change.
This was the code I settled on - based on the ideas already given:
<Style x:Key="HoverExpander" TargetType="{x:Type Expander}">
<Style.Setters>
<Setter Property="IsExpanded" Value="False"/><!-- Initially collapsed -->
</Style.Setters>
<Style.Triggers>
<!-- Impose a short delay (500ms) before expanding control -->
<EventTrigger RoutedEvent="Expander.MouseEnter">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames
Storyboard.TargetProperty="IsExpanded"
Duration="0:0:0.5">
<DiscreteBooleanKeyFrame Value="True" KeyTime="100%"/><!-- I.E. after 500ms -->
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<!-- Collapse when mouse leaves control-->
<EventTrigger RoutedEvent="Expander.MouseLeave">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames
Storyboard.TargetProperty="IsExpanded"
Duration="0:0:0.1">
<DiscreteBooleanKeyFrame Value="False" KeyTime="0%"/><!-- I.E. Immediately -->
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
Then apply as before. This was tested and works in .NET 4.0. Other neat tricks could be applied if you do so wish, I found the following to be quite helpful in getting ideas:
Animation Overview (MSDN)
Storyboards Overview (MSDN)

Change visibility in EventTrigger

i have style that have rectangle which visibility=hidden.
i want change visibility when mouse enter rectangle.
forasmuch as rectangle doesn't have 'IsMouseOver' property i cant use trigger.
how i can do that? (how can change property with animation)
thanks.
I've looking for an button to write a comment, but i dont found it.
So here comes an answer.
Two things:
How should it possible to set Visisbility of an Element to Visible, if it is hidden? The MouseEnter and MouseLeave events will not be called. So the IsMouseOver Property is always False.
Second thing is, that i'm wondering that the IsMouseOver Property will not work in a trigger (i've tried it, too and....got an exception).
An alternative way is to use EventTriggers on MouseEnter and MouseLeave.
kr
sb
<Rectangle Width="400" Height="400" Fill="Red" Opacity="0">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Style.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard TargetProperty="Opacity">
<DoubleAnimation From="0" To="1" Duration="0:0:2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard TargetProperty="Opacity">
<DoubleAnimation From="1" To="0" Duration="0:0:2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
Ok, to sum up and add to what others wrote:
The rectangle does have an IsMouseOver property. So it is possible to create a trigger (inside a style) that will work with this property. However, this will not work. Why? Because as far as WPF is concerned, if the element is not visible, the mouse is never over it. In other words, is the element is hidden, IsMouseOver will always be false. Therefore, you can't use it to make the element visible when the user puts the mouse over the place where it should be.
If you are working, with a Rectangle, there is another way: instead of making it not visible, you can change the Rectangle's color to be transparent. That way, it IsMouseOver will work as it should and the following code (as an example) will do what you want:
<Rectangle Width="200" Height="200">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="Fill" Value="Transparent"></Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Fill" Value="Yellow"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
Of course, the usage depends on what exactly you want to do, which your question doesn't mention. Another way might be to create another Rectangle with the same dimensions and position as the one you need to hide/show. This new Rectangle would be transparent, but always visible. Then, you can bind your Rectangle's Visibility to this new Rectangle's IsMouseOver.
Visibility has three enumeration, Visible Hidden and Collapsed, therefore you cant directly bind to a bool property or for that matter any property that is not a Visibility property. You can write or find a converter, search on WPF Visibility Converter. Or you can try this:
Use the tag property and bind it to the visibility property, it works fine, it is simple and it is entirely in your style setters and triggers. Of course if your using your tag for something else oh well..
In this case I have two TextBlocks, I want one textblock visible when the mouse enters the other, So when the mouse is over the first, I change its tag property to Visible and bind the second text box Visibility property to the firsts tag property.
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal ">
<TextBlock Name="TextBlockTitle" Text="{Binding Title}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock }">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Tag" Value="Visible"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Tag" Value="Hidden"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBlock.Style></TextBlock>
<TextBlock Name="TextBlockAdd" Text=" + Add New" MouseLeftButtonDown="TextBlockAdd_OnMouseLeftButtonDown">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock }">
<Setter Property="Visibility" Value="{Binding ElementName=TextBlockTitle,Path=Tag}"></Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</Trigger>
<EventTrigger RoutedEvent="MouseLeftButtonDown" ></EventTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>

Listview select "active" item [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Select ListBoxItem if TextBox in ItemTemplate gets focus
I have a ListView bound to an ObservableCollection (Listview.ItemsSource). The listview presents several textboxes that are bound to properties of the objects in the observable collection.
I would like to have the following functionality: when a user focusses a textbox the corresponding item in the listview should get selected.
I have tried things with ContainerFromElement, ContainerFromItem, etc. but can't get this "simple" functionality to work.
Any ideas...
The trick here is to use the IsKeyboardFocusWithin property on the ItemContainerStyle:
<ListView ItemsSource="{Binding}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=YourPropertyValue}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
In this example we are simply stating that IsSelected should be set to true whenever a control within that item contains the keyboard focus.
Note: this does not work in the opposite direction; selecting a particular item in the list will not automatically give focus to the contained TextBox
Edit in response to comments
As Joep pointed out, this will mean that losing keyboard focus (which will happen when a control besides the TextBox gains focus) will cause the IsSelected property to be reset to false. You can work around this by replacing the Style setter with an trigger enter action, which prevents the change from being undone when the trigger is no longer valid.
For this to work in the same way as the previous example you will need to explicitly set the SelectionMode for the ListView to Single; otherwise, multiple items can become selected at once.
<ListView ItemsSource="{Binding}" SelectionMode="Single">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames
Storyboard.TargetProperty="IsSelected">
<DiscreteBooleanKeyFrame KeyTime="0:0:0"
Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<!-- ... -->
</ListView>
The MVVM way would add extra properties to the ViewModel representing the properties that are focussed.
E.g., if the ViewModel has a property Name add a property IsNameFocussed, if it has a property Address, add a property IsAddressFocussed.
Then bind the appropriate control in the DataTemplate to the Is...Focussed property to highlight it.
All that is left is setting the Is...Focussed property in the GotFocus and LostFocus events of the textboxes. (I'd rather bind to a Focussed Property but it's not there...)

WPF/Silverlight: How to DataTrigger a Storyboard Animation in MVVM?

I have a boolean property called IsLoginWrong, I want to then play a storyboard animation if the IsLoginWrong is true. (IsLoginWrong does an OnPropertyChanged event, so I know the binding is ok) But I'm having a hard time with the syntax. This might not even be right, but I think datatriggers can only live in Styles...
<UserControl.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsLoginWrong}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource LoginWrong}"/>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Style>
But this throws an exception "A storyboard tree in a Style cannot specify a TargetName"... beause styles canno refer to items specifically.. awesome. so how do I do what I'm trying to do? (play animation if a boolean changes in mvvm)
Thanks
Within a style you cannot refer to a storyboard name. So the way I got it to work is to shove your storyboard within the actual style:
<UserControl.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsLoginWrong}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
.... PUT YOUR ACTUAL STORY BOARD IN HERE ...
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Style>
Now DataTriggers can either be put into styles or control templates, so there might be a nicer way to do this with control templates. but this is what I came up with for the time being.
One option would be to start the storyboard using the VisualStateManager. The article at http://blogs.infosupport.com/blogs/alexb/archive/2010/04/02/silverlight-4-using-the-visualstatemanager-for-state-animations-with-mvvm.aspx explains how to control the current state of the VisualStateManager from the view model using an attached property.

Resources