Change visibility in EventTrigger - wpf

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>

Related

Slide a panel from the side using Storyboard

I have 3 grids. canvas, main and sidebar
<Grid x:Name="canvas">
<Grid x:Name="main"/>
<Grid x:Name="sidebar"/>
</Grid>
I'm trying to create an animation in my WPF MVVM application in which when I click a certain button, the main panel should move to the left (outside of the canvas) and sidebar should take its place with the same animation. Basically, it should look like the sidebar is pushing the main outside of the canvas. And when I click another button the reverse of the exact animation should run. (like the main is pushing sidebar outside of the canvas.
I tried to create a Style and add some Triggers on Visibility property of the Grids. I was somewhat successful in half of the animation but couldn't get it to reverse and animation both panels at the same time.
<Style TargetType="Grid" x:Key="VisibleAnimation">
<Setter Property="Visibility" Value="Collapsed"/>
<Setter Property="RenderTransform">
<Setter.Value>
<TranslateTransform/>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.(TranslateTransform.X)"
From="300" To="0" Duration="0:0:0.5"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
Please note that I'm looking for an MVVM solution so no code behind should be involved if possible.

Animating ItemsControl items

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>

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)

Datagrid MouseOver and Selected States when using arrow keys

I have a wpf datagrid. I have added styling to show a mouseover color on a row.
What I am trying to achieve is when the mouseover appears, and a user starts using the arrow keys to navigate up and down, the mouseover needs to disappear and only the row that the user used arrow keys to get to, is the highlighted one.
The issue is the mouse cursor has been left on the grid while the user navigates with the arrow keys and the row under the cursor holds the highlight as well as the row the went to using arrows.
Here is my sample xmal:
<DataGrid AutoGenerateColumns="True" Height="277" HorizontalAlignment="Left" Margin="0,311,0,0" Name="dataGrid1"
VerticalAlignment="Top"
Width="478" ItemsSource="{Binding Path=Persons}"
RowHeight="20"
RowHeaderWidth="35" Grid.ColumnSpan="2" >
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Background"
Value="Green" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
</DataGrid>
Thanks
You'll need to set some kind of flag when the user hits an Arrow key so that the background only changes if IsMouseOver and IsUsingArrowKeys is false. You might even be able to use the Mouse Visibility as a condition instead of using a flag
I'm not positive the exact syntax, but it should be something like this
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<!-- May need to reference RelativeSource here, not sure -->
<Condition Property="IsMouseOver" Value="False" />
<Condition Binding="{Binding IsUsingArrowKeys}" Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="Green" />
</MultiDataTrigger>
</Style.Triggers>
I would suggest highlighting the row based on thier focus triggers.
Something like this:
<EventTrigger RoutedEvent="GotFocus">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="dataGrid1" Storyboard.TargetProperty="Background" Duration="0:0:0.1" To="Green"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="LostFocus">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="dataGrid1" Storyboard.TargetProperty="Background" Duration="0:0:0.1" To="White"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
And giving focus to them manually, something like this:
private void Btn_Click(object sender, RoutedEventArgs e)
{
dataGrid1.Focus();
}
So when another row gets focus, the current row loses focus & automatically falls back to non highlighted color background.

WPF - Animating object lower/higher in visual tree

I have following problem.
I created Style for ContentControl that enables moving/dragging of specific item.
This is created with help of MoveControl (: Control) that controls mouse down/move/up events. In this class DependencyProperty IsDragging property is defined, that i want to use to fade in/out item when it changes state.
Xaml file for my syle looks something like this.
<Style x:Key="ItemStyle" TargetType="ContentControl">
<!-- ... -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl" x:Name="ctrl">
<Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=.}">
<s:MoveControl Cursor="SizeAll" Template="{StaticResource MoveThumbTemplate}" x:Name="moveThumb"/>
</Grid>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
<!-- ... -->
</Setter>
</Style>
So, i want to create animation that will be done on the ContentControl styled with ItemStyle when MoveControl.IsDragging will be set to true/false.
Thank you for help.
I figured out.
The solution was to use SourceName property and link it to object of which dependency property is used. The problem was that by default 'this' object references element's DataContext value.
If you set SourceName property to a non-null value, then the data binding operation will treat that value as the place where data is pushed to and pulled from
<ControlTemplate.Triggers>
<Trigger SourceName="moveThumb" Property="IsDragging" Value="true" >
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="1.0" To="0.3" Duration="0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</ControlTemplate.Triggers>

Resources