Silverlight: when is ActualWidth not 0? - silverlight

If I attach a callback to the loaded event of my main user control and check the ActualWidth of a grid column, that value is still 0. I find it strange, given the fact that the documentation states: "The Loaded event is raised before the final rendering, but after the layout system has calculated all necessary values for rendering."
Basically I need the ActualWidth so I can set the 'to' value of an animation (so something animates from a width of 0 to the actual width).

The ActualWidth and ActualHeight values are available during the SizeChanged event. Which works out nice, so you can update your calculations when your applications resizes if needed.
I posted about this recently using the event to randomly postion elements on the screen

I think you're probably trying to solve the problem in the wrong way.
Your best bet is to animate a ScaleTransform that's added to the Grids' RenderTransform property. Here's an example that does just that:
<Grid x:Name="LayoutRoot">
<Grid.Triggers>
<EventTrigger RoutedEvent="Grid.Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="MyScaleTransform" Storyboard.TargetProperty="ScaleX" From="0" To="1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Grid.Triggers>
<Grid Background="Blue" >
<Grid.RenderTransform>
<ScaleTransform x:Name="MyScaleTransform" />
</Grid.RenderTransform>
</Grid>
</Grid>

Can you hook or override the LayoutUpdated event in your control, rather than the Loaded event? You'd have to add some additional logic to make sure that your animation only ran once, at the appropriate time, but it might give you the additional flexibility you need.

Related

EventTriggers in ListViewItem should not get triggered by contained ItemTemplate

In an wpf application I have a ListView with Drag and Drop functionality, which inserts the dropped element at the position, where it was dropped instead of the end of the list. Thats working fine. What i now want to achieve seemed to be the simple part :) I want to make clear for the user at which position in the list the currently dragged element will be dropped. There should be a gap where the new element will be inserted.
My approach was to change the Padding of the ListViewItem at the current mousePosition.
<ControlTemplate TargetType="ListViewItem">
<Border x:Name="BorderA">
<ContentPresenter />
<Border.Triggers>
<!--padding for gap when mouse hovers element-->
<EventTrigger RoutedEvent="DragEnter" SourceName="BorderA">
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation
Storyboard.TargetProperty="Padding"
From="0,0,0,0" To="0,25,0,0"
BeginTime="0:0:0" Duration="0:0:0.5"
AutoReverse="False" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<!--back to normal padding-->
<EventTrigger RoutedEvent="DragLeave" SourceName="BorderA">
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation
Storyboard.TargetProperty="Padding"
From="0,25,0,0" To="0,0,0,0"
BeginTime="0:0:0" Duration="0:0:0.5"
AutoReverse="False" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Border.Triggers>
</Border>
</ControlTemplate>
I also need a DataTemplate to display my listItems correctly. I simplified that here, which might make it appear nearly redundand, but in reality i need to display a complex viewModel and it seems to be part of the problem.
<DataTemplate x:Key="SortableListBoxItemTemplate" DataType="item:SortableListItemViewModel">
<Border Background="{StaticResource CallToActionBrush}"
BorderThickness="{StaticResource BorderThickness}"
BorderBrush="{StaticResource PassiveBorderBrush_Strong}">
</Border>
</DataTemplate>
That seemd to work. When i hover an element while dragging another, a gap between that element and the previous list element appears. But sometimes when the gap grows the mouse position will be within this gap. That triggeres the DragLeave Event, the gap starts closing until the listItem has reached the mouse position again and so on. That causes an ugly flickering and i am not sure how to avoid that. Everything is fine as long as the DataTemplate hasn't a Background color. But thats not the visual effect i want. Is there a way to trigger that DragEnter and DragLeave events only if they were fired from my listviewItem (or BorderA)? Or is there a complete other way to achieve the effect i want?
Is your problem the unexpected DragLeave event causing the flickering?
In this case, I guess setting a transparent background to your BorderA element would solve the problem.
<ControlTemplate TargetType="ListViewItem">
<Border x:Name="BorderA" Background="Transparent">
<ContentPresenter />
<Border.Triggers>
The transparent background will enable the hit test for your whole ListViewItem including the padding you are creating as placeholder.
I found a solution.
The problem wasn't the DragLeave event, but the DragEnter event when the cursor enters the Item (coming from the gap). This triggers the animation to start again and someone stupid has told this animation to start From="0,0,0,0"... I removed that part and everything worked just fine.
For me there is still a question. Maybe in some cases removing the From-part is not an option. Is there a way to add a condition to the event trigger. Something like a Multitrigger?

WPF smooth transition for databound property updates

I have a control that has its margin bound to a property of my view model:
<Grid Margin="{Binding Path=Property1, Converter={StaticResource Converter1}}"></Grid>
How do I get a smooth animation between successive updates to the Margin property? I want the margin to slide for a short amount of time instead of a discrete jump. Preferably a xaml solution.
Edit:
This is different than the other questions on this site, because I would need the "From" in a thickness animation to be bound to the previous value, and "To" to be bound to the updated value. It seems like a hack to just add another property to the view model for this.
Found the solution; the animation only needs to bind to the "From" and it will animate the way I want.
<Grid Margin="{Binding Path=Property1,
NotifyOnTargetUpdated=True,
Converter={StaticResource Converter1}}">
<Grid.Triggers><EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard><StoryBoard>
<ThicknessAnimation Storyboard.TargetProperty="Margin"
Duration="00:00:00.5"
From="{Binding Path="Property1" Converter={StaticResource Converter1}}"/>
</StoryBoard></BeginStoryboard>
</EventTrigger></Grid.Triggers>
</Grid>
You can use ThicknessAnimation:
<BeginStoryboard>
<Storyboard>
<!-- BorderThickness animates from left=1, right=1, top=1, and bottom=1 to
left=28, right=28, top=14, and bottom=14 over one second. -->
<ThicknessAnimation
Storyboard.TargetProperty="Margin"
Duration="0:0:1.5" FillBehavior="HoldEnd" From="1,1,1,1" To="28,14,28,14" />
</Storyboard>
</BeginStoryboard>
You just need to bind the properties From and To

WPF rolldown animation

I want to create a container in WPF, which will display variable number of items. These items will be attached to the container via a Collection through a DependencyProperty.
I would like to display that container rolled up as long as items are not set. Then, when the items are set, I would like to animate rolling the container down to the size, when all items will be available.
I'm novice in WPF animations, but they seem quite straightforward for me. The thing I don't know is how to specify start and end size of the container without entering precise sizes in pixels. I need to animate from (size without additional controls) to (size with additional controls) and I quite have no idea, where to start.
How can I specify such relative sizes in WPF animation storyboard?
You can do something like this using the ActualHeight property:
<Grid Name="LayoutRoot">
<Grid Name="Container" ClipToBounds="True">
<ListBox ItemsSource="{Binding YourCollection}" ... >
<ListBox.Triggers>
<EventTrigger RoutedEvent="ListBox.SizeChanged">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Container"
Storyboard.TargetProperty="Height" To="{Binding ActualHeight, ElementName=LayoutRoot,
FallbackValue=0, Mode=OneWay}" Duration="00:00:0.3" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ListBox.Triggers>
</ListBox>
</Grid>
</Grid>
Note that I didn't use the From property of the DoubleAnimation... this will enable the animation to 'grow' as each item is added.

WPF ToolTip: Fade In/Out. Is it a good idea to fix the ToolTip position?

I want to fade in/out a ToolTip how can I achieve that?
Also, I wonder if its a good idea to fix the position of the ToolTip? eg. always at bottom? I guess the ToolTip may go off screen? And suppose I want to do that, how can I do that? Position the ToolTip a fixed position relative to the parent
The position is relative to the parent unless you specify otherwise. If you want it to display somewhere else you can use
<TextBox ToolTipService.PlacementTarget="{Binding ElementName=displayToolTipHere}">
To specify the position you use Placement. Either ToolTipService.Placement in TextBox or Placement inside the ToolTip like in my example below. I don't think ToolTip has any built in "fade in/fade out" but you can use an animation. Don't think you can make it fade out though since it instantly closes once the mouse leaves the Control. If you want a fade out effect you should probably use a Popup instead.
<TextBox>
<TextBox.ToolTip>
<ToolTip Placement="Bottom"
Content="Some ToolTip Content">
<ToolTip.Triggers>
<EventTrigger RoutedEvent="ToolTip.Opened">
<BeginStoryboard>
<Storyboard TargetProperty="Opacity">
<DoubleAnimation From="0.0" To="1.0" Duration="0:0:2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ToolTip.Triggers>
</ToolTip>
</TextBox.ToolTip>
</TextBox>

Animation in DataGrid of Silverlight Application - How?

I'm starting to kick Silverlight around (although it currently feels the other way around) by rewritting an existing ASP.NET application - as good a place to start as any I thought.
I have 'mastered' pulling data from a database, through a service and into a datagrid and also populating image elements in the rows. So far so good.
Now i'm stuck and have a headache!
I want to add a simple animation (a green rectangle moving over a red rectangle to display a % progress) to the last column of each row, but can't get it wired up.
My xaml looks like this:
<data:DataGrid.Columns>
.
.
.
.
<data:DataGridTemplateColumn>
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel x:Name="AnimationPanel">
<StackPanel.Resources>
<Storyboard x:Key="ShowProgress">
<DoubleAnimation Storyboard.TargetProperty="Width"
Storyboard.TargetName="goalProgressBar_Complete"
From="0" To="50"
Duration="0:0:5">
</DoubleAnimation>
</Storyboard>
</StackPanel.Resources>
<Rectangle x:Name="goalProgressBar_Complete"
Width="1" Height="100"
Fill="#0aa60e"></Rectangle>
</StackPanel>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
</data:DataGrid.Columns>
which is quite obviously wrong (because it doesn't work) and I am starting to think that it just is not possible to add animation to a Datagrid in this way. I've Googled hard looking for a piece of code and while there have been some tantilising results they have not amounted to anything.
Am I trying to do something that Silverlight simply does not support or am I missing something really straightforward?
Thanks in advance.
Update:
Although I'm not out of the woods yet I have managed to get the animation running with the appropriate column.
The answer provided by MasterMax led me to look at EventTriggers and the revised xaml looks like this:
<data:DataGridTemplateColumn>
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Rectangle x:Name="GoalProgress" Height="100" Width="1" Fill="Green">
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Width"
Storyboard.TargetName="GoalProgress"
From="0" To="50" Duration="0:0:5">
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
All I need to do now is bind in the % value in the 'To' property and add my underlying Red rectangle. No doubt this will not prove as easy as I'm currently hoping but then it wouldn't be fun otherwise would it ;-)
Try attaching it to the EventTrigger for the Datagrid. Something similar to this:
<Window.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Name="EntranceAnimation"
Storyboard.TargetName="MainWindow"
Storyboard.TargetProperty="Opacity"
From="0.0"
To="1.0"
Duration="0:0:3">
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Window.Triggers>
In your case you'd be looking for the DataGrid.Loaded event (or something similar). This works for me, and I'm fading the whole window in on page loaded.
It looks to me like you aren't actually playing the storyboard. You need to add mouse enter and mouse leave handlers for your rectangle and then play and stop your storyboard accordingly. Make sure to both stop and then play the storyboard in the enter command (otherwise you get some interesting side-effects).
Update
Okay, having spent my day neck deep in Silverlight, I'm not much closer to helping. I was looking at the generic.xaml file inside the Silverlight assembly containing the DataGrid. You might consider taking a look in there. From what I can see, they achieve some of this for the row details transition with control templating though I haven't looked at the corresponding code yet to determine exactly how.
for that type of application, an animation may not be the best solution. you may want to try binding the width property of the moving rectangle to a variable in your application, or adjust the width with each tick as the progress changes.

Resources