I simply want to open up the WPF Popup with a delay, sort of like a ToolTip.
How can I achieve this?
And, by the way, Popup.PopupAnimation = PopupAnimation.Fade ... fades in too quickly. I want at least half a second in there.
You can create a style to be applied to the Popup in the following way:
<Style x:Key="TooltipPopupStyle" TargetType="Popup">
<Style.Triggers>
<DataTrigger Binding="{Binding PlacementTarget.IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="OpenPopupStoryBoard" >
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsOpen" FillBehavior="HoldEnd">
<DiscreteBooleanKeyFrame KeyTime="0:0:0.25" Value="True"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<PauseStoryboard BeginStoryboardName="OpenPopupStoryBoard"/>
<BeginStoryboard x:Name="ClosePopupStoryBoard">
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsOpen" FillBehavior="HoldEnd">
<DiscreteBooleanKeyFrame KeyTime="0:0:1" Value="False"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<PauseStoryboard BeginStoryboardName="ClosePopupStoryBoard" />
</Trigger.EnterActions>
<Trigger.ExitActions>
<PauseStoryboard BeginStoryboardName="OpenPopupStoryBoard"/>
<ResumeStoryboard BeginStoryboardName="ClosePopupStoryBoard" />
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
Then, whenever you want to use it, you would write markup similar to this (notice the binding for the PlacementTarget):
<TextBlock x:Name="TargetControl" Text="Hover over me!" />
<Popup PlacementTarget="{Binding ElementName=TargetControl}" Style="{StaticResource TooltipPopupStyle}">
<Border BorderBrush="Red" BorderThickness="1" Background="White">
<TextBlock Text="This is a Popup behaving somewhat like the tooltip!" Margin="10" />
</Border>
</Popup>
The answer cplotts pasted is good but may not apply in your case because it leaves the animation attached to the IsOpen property, effectively locking it in place and preventing it from being changed via direct property setting, binding, and other ways. This may make it difficult to use with your code, depending on how you are using it.
If that is the case, I would switch to starting a DispatcherTimer when you want to open a popup after some delay, like this:
_popupTimer = new DispatcherTimer(DispatcherPriority.Normal);
_popupTimer.Interval = TimeSpan.FromMilliseconds(100);
_popupTimer.Tick += (obj, e) =>
{
_popup.IsOpen = true;
};
_popupTimer.Start();
For a ToolTip-like behavior this could be done on MouseEnter. If you want to cancel the popup opening for some reason (such as if the mouse leaves the control before the popup appears), just:
_popupTimer.Stop();
Update
As cplotts obseved in the comment, you will also want to set _popup.IsOpen = false in some situations in the MouseLeave event, depending on your logic for handling the mouse enter / exit events between your control and the popup. Be aware that you usually don't want to blindly set IsOpen=false on every MouseLeave event, because it may do so when the popup appears over it. This would in some situations lead to a flickering popup. So you'll need some logic there.
First off ... the credit for this answer goes to Eric Burke. He answered this very question posted in the WPF Disciples group. I thought it would be useful to put this answer out on StackOverflow too.
Basically, you need to animate the IsOpen property of the Popup with with a DiscreteBooleanKeyFrame.
Check out the following xaml (which can easily be pasted into Kaxaml or another loose xaml editing utility):
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<ContentPresenter>
<ContentPresenter.ContentTemplate>
<DataTemplate>
<Grid>
<CheckBox
x:Name="cb"
Width="100"
Height="40"
Content="Hover Over Me"
/>
<Popup
x:Name="popup"
Placement="Bottom"
PlacementTarget="{Binding ElementName=cb}"
>
<Border Width="400" Height="400" Background="Red"/>
</Popup>
</Grid>
<DataTemplate.Triggers>
<Trigger SourceName="cb" Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard x:Name="bsb">
<Storyboard>
<BooleanAnimationUsingKeyFrames
Storyboard.TargetName="popup"
Storyboard.TargetProperty="IsOpen"
FillBehavior="HoldEnd"
>
<DiscreteBooleanKeyFrame KeyTime="0:0:0.5" Value="True"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<StopStoryboard BeginStoryboardName="bsb"/>
</Trigger.ExitActions>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
</Page>
Please note that I modified his original solution slightly ... to trigger the IsOpen on mouse over versus checking the CheckBox as he had it. All in the attempt to make Popup behave a little like ToolTip.
System.Windows.Controls.ToolTip tp = new System.Windows.Controls.ToolTip();
System.Windows.Threading.DispatcherTimer tooltipTimer =
new System.Windows.Threading.DispatcherTimer
(
System.Windows.Threading.DispatcherPriority.Normal
);
private void TooltipInvalidCharacter()
{
tp.Content =
"A flie name cannot contain any of the following character :" +
"\n" + "\t" + "\\ / : * ? \" < > |";
tooltipTimer.Interval = TimeSpan.FromSeconds(5);
tooltipTimer.Tick += new EventHandler(tooltipTimer_Tick);
tp.IsOpen = true;
tooltipTimer.Start();
}
void tooltipTimer_Tick(object sender, EventArgs e)
{
tp.IsOpen = false;
tooltipTimer.Stop();
}
You can extend the XAML for this solution so the popup stays open as long as the mouse is over it, then disappears automatically.
I modified the sample as follows:
Create a "ClosePopop" animation which sets IsOpen to False after 0.5 seconds. I made this a resource because it's used twice.
For the control's IsMouseOver trigger, add an ExitAction that starts the ClosePopup animation. This gives the user a chance to move the mouse over the popup before it closes. I named this animation"bxb"
Add a Trigger to the popup's IsMouseOver property. On mouseover, stop (but don't remove) the original "bxb" ClosePopup animation. This leaves the popup visible; removing the animation here will make the popup close.
On the popup's mouseout, start a new ClosePopup animation then remove the "bxb" animation. The last step is critical because otherwise the first, stopped "bxb" animation will keep the popup open.
This version turns the pop blue while the mouse is over it so you can see the sequence of events with Kaxaml.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DataTemplate x:Key="TooltipPopup">
<Grid>
<CheckBox
x:Name="cb"
Width="100"
Height="40"
Content="Hover Over Me"/>
<Popup
x:Name="popup"
Placement="Bottom"
PlacementTarget="{Binding ElementName=cb}">
<Border x:Name="border" Width="400" Height="400" Background="Red"/>
</Popup>
</Grid>
<DataTemplate.Resources>
<Storyboard x:Key="ClosePopup">
<BooleanAnimationUsingKeyFrames
Storyboard.TargetName="popup"
Storyboard.TargetProperty="IsOpen"
FillBehavior="Stop">
<DiscreteBooleanKeyFrame KeyTime="0:0:0.5" Value="False"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</DataTemplate.Resources>
<DataTemplate.Triggers>
<Trigger SourceName="cb" Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard x:Name="bsb" >
<Storyboard>
<BooleanAnimationUsingKeyFrames
Storyboard.TargetName="popup"
Storyboard.TargetProperty="IsOpen"
FillBehavior="HoldEnd">
<DiscreteBooleanKeyFrame KeyTime="0:0:0.5" Value="True"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<StopStoryboard BeginStoryboardName="bsb"/>
<BeginStoryboard x:Name="bxb" Storyboard="{StaticResource ClosePopup}"/>
</Trigger.ExitActions>
</Trigger>
<Trigger SourceName="popup" Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="Blue"/>
<Trigger.EnterActions>
<StopStoryboard BeginStoryboardName="bxb"/>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource ClosePopup}"/>
<RemoveStoryboard BeginStoryboardName="bxb"/>
</Trigger.ExitActions>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</Page>
Related
I'm getting an error with the following details:
Source Name property cannot be set within Style. Triggers section
<Rectangle Margin="121,163,0,248" HorizontalAlignment="Left" Width="33" Height="34">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="Fill" Value="Blue"></Setter>
<Style.Triggers>
<EventTrigger SourceName="myButton" RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="Fill.Color" To="Orange"
Duration="0:0:1" AutoReverse="True" RepeatBehavior="Forever"
BeginTime="0:0:0">
</ColorAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
I want to change the rectangle fill color with Color Animation tag when click on button.
Like it says, you cannot use source name in a style like that.
You can use a data trigger instead. Set say a bool property in your viewmodel from your button's command of click.
Then start your storyboard with a datatrigger binding that bool property and comparing value.
You can probably easily Google datatrigger and storyboard but here's a so question includes an example.
WPF Data Triggers and Story Boards
Btw.
Routed events are rarely very useful IME. Binding icommand is way more practical. Usually.
Edit:
Here's a quick and dirty sample using a togglebutton. Since this approach uses binding it can reference controls by name. Binding is resolved at run time.
<Grid>
<Rectangle>
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="Fill" Value="Blue"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, ElementName=StartStop}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="ColourStoryboard">
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="Fill.Color" To="Orange"
Duration="0:0:1" AutoReverse="True" RepeatBehavior="Forever"
>
</ColorAnimation>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="ColourStoryboard"/>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
<ToggleButton Content="Start Stop"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Name="StartStop"/>
</Grid>
</Window>
Paste the grid inside a mainwindow, spin it up. When you click the togglebutton it sets ischecked true so the rectangle animates to orange and back to blue. When you click the button again, ischecked becomes false and the animation stops.
You could instead write code in a button handler that set a bound property which is in the datacontext and bind the datatrigger to that bound property. That's what the markup in the link is doing with IsBusy.
I'm brand spanking new to WPF and XAML, so I'm sorry if this is a silly question:
I am trying to start a StoryBoard on a TextBlock that changes the text. I want to start this animation when the TextBlock becomes visible. It looks like the only events you can trigger inside of a TextBlock.Triggers bracket is an EventTrigger. If this is so, as far as I can see an EventTrigger needs a routed event, but IsVisibilityChanged isn't one. Since I can't use that, any ideas as to what I should do instead?
I have attached a sample of my code that isn't working (doesn't compile), just to illustrate what it is that I am trying to do:
<TextBlock Foreground="LightGray" Text="Payfast Running" Name="AnimatedTextBlock">
<TextBlock.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<StringAnimationUsingKeyFrames
Storyboard.TargetProperty="(TextBlock.Text)"
Duration="0:0:1.5"
Storyboard.TargetName="AnimatedTextBlock"
RepeatBehavior="Forever">
<DiscreteStringKeyFrame Value="Payfast Running" KeyTime="0:0:0"/>
<DiscreteStringKeyFrame Value="Payfast Running." KeyTime="0:0:0:5"/>
<DiscreteStringKeyFrame Value="Payfast Running.." KeyTime="0:0:1"/>
<DiscreteStringKeyFrame Value="Payfast Running..." KeyTime="0:0:1:5"/>
</StringAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</TextBlock.Triggers>
</TextBlock>
Lastly, I need to do this in markup, not CodeBehind, if possible.
You might need to wrap that all up into a style.
<TextBlock Foreground="LightGray" Text="Payfast Running" Name="AnimatedTextBlock">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Visibility, RelativeSource={RelativeSource TemplatedParent}}" Value="Visible">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<StringAnimationUsingKeyFrames
Storyboard.TargetProperty="(TextBlock.Text)"
Duration="0:0:1.5"
Storyboard.TargetName="AnimatedTextBlock"
RepeatBehavior="Forever">
<DiscreteStringKeyFrame Value="Payfast Running" KeyTime="0:0:0"/>
<DiscreteStringKeyFrame Value="Payfast Running." KeyTime="0:0:0:5"/>
<DiscreteStringKeyFrame Value="Payfast Running.." KeyTime="0:0:1"/>
<DiscreteStringKeyFrame Value="Payfast Running..." KeyTime="0:0:1:5"/>
</StringAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
"I want to start this animation when the TextBlock becomes visible."
You could bind your TextBlock's Visibility to a boolean property (note that you have to add a resource for boolean-to-visibility conversion):
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
...
<TextBlock Visibility="{Binding Path=ReadyToPlay, Converter={StaticResource BoolToVisConverter}}"/>
And then, in your ReadyToPlay property's setter method, you could add an if statement that checks for whether or not to start your Storyboard:
if (value) {
// start Storyboard:
// find Storyboard instance and call Begin() method on it
}
By doing this, when ReadyToPlay becomes true, the TextBlock will become visible and the Storyboard will begin simultaneously.
I want a control (e.g. a GroupBox) to show a grow animation when it becomes visible and a shrink animation, when the visibility is changed to "Collapsed".
Therefore, I created a style which implements an animated grow and shrink effect as shown here in a small sample application (shown below).
However, only the grow animation is shown. Instead of showing the shrink animation, the groupbox disappears at once.
Can anyone tell me, why?
And even better, how to fix it?
<Window x:Class="ShrinkTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="FrameworkElement" x:Key="ExpandableElement">
<Setter Property="RenderTransformOrigin" Value="0.5 0" />
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform/>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY" From="0" To="1" Duration="0:0:0.5" AccelerationRatio="0.2" DecelerationRatio="0.4"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
<Trigger Property="Visibility" Value="Hidden">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY" From="1" To="0" Duration="0:0:0.5" AccelerationRatio="0.2" DecelerationRatio="0.4"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Margin="8" Width="140" Click="ButtonBase_OnClick">Expand / Shrink</Button>
<TextBlock Grid.Row="1" Text="--- Header ---"/>
<GroupBox x:Name="GroupBox" Grid.Row="2" Header="GroupBox" Style="{StaticResource ExpandableElement}" >
<StackPanel Orientation="Vertical">
<TextBlock Text="Test Test Test"/>
<TextBlock Text="Test Test Test"/>
<TextBlock Text="Test Test Test"/>
<TextBlock Text="Test Test Test"/>
<TextBlock Text="Test Test Test"/>
</StackPanel>
</GroupBox>
<TextBlock Grid.Row="3" Text="--- Footer ---"/>
</Grid>
</Window>
I had a similar problem. Just think about it for a minute... your problem is that you can see your animation when it's visible, but you can't when it is hidden. That is also your answer to why... because it is hidden. I know, that's fairly unsatisfactory answer, but that's just how it is.
As to how to fix it... well saying it is simple, but implementing it is not. Simply put, you have to run your animation until it ends and then set the Visibility to Hidden. So unfortunately this means that nice, simple setting the Visibility property in the Trigger is no longer viable... it's ok to make it visible, just not for hiding.
In my case, I have a whole framework that I built my animations into. Basically speaking though, when I remove items from the collections, internally the item is not actually removed, but instead its exit animation is started. Only when that animation is complete will the internal collection actually remove the item.
So if you can be bothered, then you'll have to implement something like this where, rather than setting the Visibility property to Hidden, you set another property to true which triggers the animation and when the Completed event from that animation is called, then you set the Visibility property to Hidden.
Sheridan is right. As soon as a control becomes invisible, it doesn't matter, which animation you apply to it. :-)
So I created a special ExpandingContentControl:
public class ExpandingContentControl : ContentControl
{
public static readonly DependencyProperty IsExpandedProperty = DependencyProperty.Register(
"IsExpanded", typeof(bool), typeof(ExpandingContentControl), new PropertyMetadata(false));
public bool IsExpanded
{
get { return (bool)GetValue(IsExpandedProperty); }
set { SetValue(IsExpandedProperty, value); }
}
public ExpandingContentControl()
{
Visibility = IsExpanded ? Visibility.Visible : Visibility.Collapsed;
}
}
But there was also a problem with the style: Creating two triggers which are bound to different values of the same property obviously doesn't work.
Instead, I'm now using just one trigger where the EnterAction implements growing and the ExitAction implements shrinking the control:
<Style TargetType="controls:ExpandingContentControl" >
<Setter Property="RenderTransformOrigin" Value="0.5 1" />
<Setter Property="RenderTransform">
<Setter.Value>
<ScaleTransform/>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsExpanded" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" >
<DiscreteObjectKeyFrame Value="{x:Static Visibility.Visible}" KeyTime="00:00:00"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY" From="0" To="1"
Duration="0:0:0.3" DecelerationRatio="0.4"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="RenderTransform.ScaleY" From="1" To="0" Duration="0:0:0.2" />
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame Value="{x:Static Visibility.Collapsed}" KeyTime="00:00:0.2"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
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.
I've got a button with an image in it and it's being styled by the following:
<ControlTemplate x:Key="IconButton" TargetType="Button">
<Border>
<ContentPresenter Height="80" Width="80" />
</Border>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard TargetProperty="Opacity">
<DoubleAnimation From="1" To="0.5" Duration="0:0:0.5" />
<DoubleAnimation From="0.5" To="1" Duration="0:0:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<BeginStoryboard>
<Storyboard TargetProperty="Width">
<DoubleAnimation From="80" To="95" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Button is as follows:
<Button Template="{StaticResource IconButton}" Name="btnExit">
<Image Source="Images/Exit.png" />
</Button>
The problem is that the width doesn't change when my mouse goes over. (Or at least - the width of the image does not...)
I believe there is a "scale" transform I can use to enlarge the button and all it's contents? how would I do that here...?
Thanks.
Your template seems to be pretty minimal but I'm assuming your only just getting started on it, however this will help you get started with using a ScaleTransform as opposed to animating the width.
The ScaleTransform can be applied to the RenderTransform property of either the Button itself or just the Border of your template. This could be a TransformGroup if you want to do more than just Scale (i.e. a composite transform consisting of other tranforms such as Translate, Rotate, Skew) but to keep it simple and for examples sake something like the following applies a single ScaleTransform value to the Button:
<Button Template="{StaticResource IconButton}" Name="btnExit">
<Button.RenderTransform>
<ScaleTransform ScaleX="1.0" ScaleY="1.0"></ScaleTransform>
</Button.RenderTransform>
<Image Source="Images/Exit.png" />
</Button>
or this to apply to the Border of the ControlTemplate:
<ControlTemplate x:Key="IconButton" TargetType="Button">
<Border Background="Blue" x:Name="render">
<Border.RenderTransform>
<ScaleTransform ScaleX="1.0" ScaleY="1.0"></ScaleTransform>
</Border.RenderTransform>
<ContentPresenter Height="80" Width="80" />
</Border>
...
...
Next you will want to change your MouseEnter trigger to target that property and for width you will want to target the ScaleX property of the ScaleTransform. The following Storyboard will scale the Button 2.5 times in the X direction (add TargetName="render" to <Storyboard... if you have chosen to apply the Transform to the Border as opposed to the Button).
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<BeginStoryboard>
<Storyboard TargetProperty="RenderTransform.ScaleX">
<DoubleAnimation To="2.5" Duration="0:0:0.2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
If you were to use a TransformGroup with a number of transforms you would change the TargetProperty value to something like RenderTransform.(TransformGroup.Children)[0].ScaleX assuming the ScaleTransform is the first child of the group.
This should get you up and running with what you need and you can take it where you want from there...
HTH