I'm attempting to flip a button on a form using a storyboard. What I currently have is a storyboard that uses a custom grid animation to make certain grid rows grow (found here). The button that I have that initiates this storyboard has an image of arrows overlayed on it and it needs to be flipped to correct the direction of the control for the user to understand. The storyboard that I have works (as in it doesn't produce any errors) but it only makes the gridrow height property grow; the button image is not flipped vertically.
<!-- "Open the description box" Storyboard-->
<Storyboard x:Key="openDescription">
<local:GridLengthAnimation
Storyboard.TargetName="Row4"
Storyboard.TargetProperty="Height"
From="0*" To=".5*" Duration="0:0:1"
AccelerationRatio=".5"
DecelerationRatio=".5"/>
<!-- This is the section that needs to be tweaked -->
<DoubleAnimation
Storyboard.TargetName="btnDetailsZoom"
Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)"
From="0"
To="-1"
Duration="00:00:01"/>
</Storyboard>
<!-- Code for the button -->
<s:SurfaceButton Grid.Row="4" Grid.Column="4" VerticalAlignment="Top" Margin="25,-50,25,-50" Name="btnDetailsZoom" PreviewTouchDown="onDetailsZoom">
<s:SurfaceButton.Background>
<ImageBrush ImageSource="/uiTest2;component/Resources/doubleArrowUp.png" />
</s:SurfaceButton.Background>
</s:SurfaceButton>
If anyone knows about how to correctly use the scaletransform property within a storyboard (since I'm fairly sure that's partially correct), I would greatly appreciate some guidance.
Thanks!
It looks like you forgot to define the Transform Property on the Button, like this:
<!-- Code for the button -->
<s:SurfaceButton Grid.Row="4" Grid.Column="4" VerticalAlignment="Top" Margin="25,-50,25,-50" Name="btnDetailsZoom" PreviewTouchDown="onDetailsZoom">
<s:SurfaceButton.RenderTransform>
<ScaleTransform />
</s:SurfaceButton.RenderTransform>
<s:SurfaceButton.Background>
<ImageBrush ImageSource="/uiTest2;component/Resources/doubleArrowUp.png" />
</s:SurfaceButton.Background>
</s:SurfaceButton>
Related
I have a ListView which is bound to a List of items of the following type:
class Item : INotifyPropertyChanged
{
//Both these props have property change notification implemented. Left out for brevity.
int Price{get;set}
int Size{get;set}
}
I have a DataTemplate for this class to show in ListView
The size of the list does not change but the values of the properties of the items in the list do. Suppose I first have three items like this:
Item format : Size#Price
{(10#34), (15#37), (10#38)}
Suppose now the first item only changes due to a data update:
{(15#34), (15#37), (10#38)}
Please note that I do not remove the existing item. I only change its properties. So a property changed notification will fire for each change. I want to highlight/flash the item that changed in the ListView upon such a change. How do I do this (Preferably in XAML)?
Not sure I follow this right, Are you just looking to say flash the ListViewItem when the Size or Price property changes?
If so you should be able to do that with a Binding.TargetUpdated event trigger. You do need to make sure your binding for Size and Price in their binding also specify NotifyOnTargetUpdated=True for this event to fire.
So something like this should work fine:
<DataTemplate x:Key="SomeTemplate" DataType="viewModel:Item">
<StackPanel x:Name="stackPanel"
Background="Transparent"
Orientation="Vertical">
<!-- This prolly aint your layout. Just here as an example -->
<TextBlock Text="{Binding Size, NotifyOnTargetUpdated=True}" />
<TextBlock Text="{Binding Price, NotifyOnTargetUpdated=True}" />
</StackPanel>
<DataTemplate.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard>
<Storyboard AutoReverse="True">
<!-- From="1.0" helps prevent spam property changes skewing the opacity -->
<DoubleAnimation Duration="0:0:0.3"
Storyboard.TargetName="stackPanel"
Storyboard.TargetProperty="Opacity"
From="1.0"
To="0.3" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</DataTemplate.Triggers>
</DataTemplate>
Alternate:
From blend with xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
you should also have <ei:PropertyChangedTrigger> using which you can then say invoke a Behavior to trigger a Storyboard <ei:ControlStoryboardAction>. Personally I've only used the first approach as it's slightly simpler.
I'm seeing a very odd problem in a WPF list box where the MouseEnter trigger for each item fires less accurately for items further down in my ListBox. My MouseEnter trigger fires off an animation that increases the ScaleTransform of each item.
Example: I have a list box of 10 items. The list box is displaying the first 5 items. I hover over the first item and it scales fine. I hover over the 2nd item and I have to place the mouse a little further inside the list item control to see the effect. I hover over the third item and I have to place the mouse even further in. By the time I get to the last (5th) visible item in the list I have to place the mouse at about the mid-point of the control for the MouseEnter to fire. Then, I scroll down to the bottom to see items 6-10. Item #6 hovers just fine. #7 is less accurate, #8 even less accurate, and so on down to #10 which is very inaccurate.
Thus is looks like the vertical visible position in the list box affects the MouseEnter accuracy of each control.
EDIT: When I put this code in a clean XAML window it works fine. It must be some conflict outside of the code I have provided... I'll provide more details (or an answer) if I find something.
Any ideas on what might be causing this?
Here is my ListBox XAML:
<ListBox
MaxHeight="530"
ItemsSource="{Binding Appointments}"
Background="Transparent"
>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<views:AppointmentControl DataContext="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And here is what my AppointmentControl looks like:
<UserControl Height="155" Width="215">
<UserControl.Triggers>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<BeginStoryboard>
<Storyboard TargetProperty="RenderTransform.ScaleX">
<DoubleAnimation To="1.2" Duration="0:0:0.050" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseLeave">
<BeginStoryboard>
<Storyboard TargetProperty="RenderTransform.ScaleX">
<DoubleAnimation To="1.0" Duration="0:0:0.050" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</UserControl.Triggers>
<UserControl.RenderTransform>
<ScaleTransform ScaleX="1.0" ScaleY="1.0"></ScaleTransform>
</UserControl.RenderTransform>
<Button
Background="#FFCAEE7D"
BorderThickness="1,1,1,3"
Padding="0"
>
<StackPanel Height="125" Width="180">
<TextBlock ... />
<TextBlock ... />
<ItemsControl>
...
</ItemsControl>
</StackPanel>
</Button>
</UserControl>
What happens if you enter the ListBox starting with the fifth element? Do you still need to go halfway down to see the same effect?
The issue was that another control with a greater z-index on top of the list box was interfering with the list item hit testing. The other control had a supposedly transparent area but for whatever reason it interferes with the mouseenter. When I remove this other control the problem goes away.
I am creating a Silverlight dialog class which is more MVVM-friendly. The ChildWindow class has some awkwardness which makes it difficult to manage from a view model.
This means I need to declare my own user interface. I have all of the traditional elements in place, including a Popup control and an overlay with 50% opacity. The open/close behavior is also working.
The problem I am having is that my control template doesn't prevent the dialog body from growing in response to the user typing into a TextBox. When the amount of characters grows beyond the default size, the entire dialog body stretches horizontally. Obviously this is not normal behavior.
What I would like to accomplish is a way for the dialog body to size itself appropriately to the content, but not expand or contract in response to child controls which require more or less space. This is how the ChildWindow control behaves, but I have examined its template and I can't determine how that works.
I think I am seeing the expansion behavior because my template is based on Grids, which allow child controls as much size as they request. I have tried several permutations of StackPanel, ScrollViewer, and other layouts with no success. Does anyone have some insight into how ChildWindow accomplishes its static, content-sized layout, or perhaps another suggestion?
Here is the template I have so far, stripped down to the bare essentials:
<Popup>
<Grid x:Name="overlay">
<Border>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
...Title...
</Grid>
<Grid Grid.Row="1">
<ContentControl Content="{TemplateBinding DialogBody}" />
</Grid>
</Grid>
</Border>
</Grid>
</Popup>
When DialogBody contains a TextBox or anything else which changes the layout size, such as hidden controls, the entire content of the Popup grows. I don't want to resort to a static width and height, because I want the dialog to automatically size to the dialog body, but only initially.
Not sure that this is wat you want..., but
wyh don't you register for Loaded event of "overlay" nad when Load will fire set the heigh and width of "overlay" ActualHeight and ActualWidth accordingly.
In this way control will get the size it want initially and wont change after that.
EDIT:
you can use this to do this in declarative way.
<Grid.Triggers>
<EventTrigger RoutedEvent="Grid.Loaded">
<BeginStoryboard>
<Storyboard x:Name ="setWidth">
<DoubleAnimation Storyboard.TargetName="overlay" Storyboard.TargetProperty="Width" From="0" To="{Binding ActualWidth, ElementName=overlay}"/>
<DoubleAnimation Storyboard.TargetName="overlay" Storyboard.TargetProperty="Height" From="0" To="{Binding ActualHeight, ElementName=overlay}"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
I am using MVVM Light. I have created a window that looks like this:
<Window Name="MainWindow" ...>
<Window.Resources>
...
<viewModels:MainViewModel x:Key="mainVM" />
...
<BooleanToVisibilityConverter x:Key="visConv" />
...
</Window.Resources>
<Grid DataContext="{StaticResource mainVM}>
...
<Button Command="{Binding RaiseMyControl}" />
...
<my:MyUserControl Visibility="{Binding MyControlVisible,
Converter={StaticResource visConv}}" />
</Grid>
</Window>
So basically, the MainViewModel is a view model class for the window. It contains:
bool MyControlVisible property which is binded to MyUserControl's Visibility
property
RelayCommand RaiseMyControl command which purpose is to set the value of the
MyControlVisible property to true (default is false).
Clicking the button in the window results in the appearance of the MyUserControl - simple.
MyUserControl user control looks like this:
<UserControl ...>
<UserControl.Resources>
...
<viewModels:MyUserControlViewModel x:Key="userControlVM" />
...
</UserControl.Resources>
<Grid DataContext="{StaticResource userControlVM}>
...
<Border Width="200" Height="100" Background="Red">
<TextBlock Text="{Binding MyUserControlText}" />
</Border>
<!-- This border has a DataTrigger bound to "bool Fading" property of
the view model. When Fading is true, the border fades in through
an animation. When it is false, the border fades out. -->
...
<Button Command="{Binding CloseMyControl}" />
</Grid>
</UserControl>
Again, very simple. The MyUserControlViewModel is a view model class for the user control. It contains:
string MyUserControlText property which is binded to TextBlock's Text
property
bool Fading property which is binded to border's data template, and is used to make
the border fade in or out
RelayCommand CloseMyControl command which does two things: 1. It sets the Fading
property to false to make the border fade out, and 2. it sets the Visibility
property of the user control to Collapsed.
Here's the problem: as soon as the Visibility is set to Collapsed, the user control disappears. I need it to fade out first and then to disappear afterwards. How can I make it happen? Thanks.
Since the visibility belongs to the fade-out i would run two animations at the same time. Additionally to your fade-animation (of whatever type or composite that may be) you can create a ObjectAnimationUsingKeyFrames which sets the Visibiliy at the key time at which the fade ends.
XAML example:
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0:0:0.5">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
Additionally all storyboards and animations have a Completed event to which you could subscribe and just set the value right away.
To direct the animation at another control use Storyboard.Target for complex references, or Storyboard.TargetName for reference by name.
To animate the UserControl you could try:
Storyboard.Target="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}"
or
Storyboard.Target="{Binding RelativeSource={RelativeSource AncestorType=my:MyUserControl}}"
Both should work if the logical tree is intact.
I'd try setting the visibility to Collapsed as part of the fade out animation, not a separate line in the CloseMyControl command.
My ultimate goal is to animate the change in size between two UserControls. The way that I have attempted to achieve this has produced one major problem.
I start with a DataTemplate that contains some basic text and icon display, an 'edit' UserControl with a height set to 0 and an edit button. The edit UserControl is in a GridRow with Height="Auto", so it also starts out with a height of 0. The button has a DoubleAnimation triggered by the button click that animates the height of the UserControl from 0 to 300. This all works just fine. Here is a simplified code example.
<DataTemplate x:Key="UserTemplate" DataType="{x:Type dataTypes:User}">
...
<controls:UserView Grid.Row="1" Grid.ColumnSpan="5" x:Name="EditRow"
DataContext="{Binding}" Height="0" />
<controls:UserEditor Grid.Row="2" Grid.ColumnSpan="5" x:Name="EditRow"
DataContext="{Binding}" Height="0" />
<Button Grid.Row="0" Grid.Column="4" Name="Edit" Style="{StaticResource ButtonStyle}"
ToolTip="Edit user" Click="Button_Click">
<Image Source="/SolutionName;component/Images/Edit.png" Stretch="None" />
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Name="EditRowHeightAnimation"
Storyboard.TargetName="EditRow" Storyboard.TargetProperty="Height" From="0"
To="300" Duration="00:00:0.5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
...
</DataTemplate>
The problem is that the edit UserControl is not 300 pixels high and I won't know what height it will be at design time. I tried the following, but it doesn't work.
<DoubleAnimation Name="EditRowHeightAnimation" Storyboard.TargetName="EditRow"
Storyboard.TargetProperty="Height" From="0" To="{Binding DesiredSize.Height,
ElementName=EditRow}" Duration="00:00:0.5" />
I have also tried calling Measure() and UpdateLayout() on the edit UserControl from code behind. I commented out the button click trigger and xaml animation and added one from code behind instead... now this kind of worked, but I always got the same (wrong) DesiredSize. That is, the UserControl height would get animated, but just to the wrong height. Here is the button click handler code.
private void Button_Click(object sender, RoutedEventArgs e)
{
User currentUser = (User)CollectionViewSource.GetDefaultView(Users).CurrentItem;
ListBoxItem listBoxItem = (ListBoxItem)
(UsersListBox.ItemContainerGenerator.ContainerFromItem(currentUser));
DataTemplate itemDataTemplate = FindResource("UserTemplate") as DataTemplate;
ContentPresenter contentPresenter = listBoxItem.GetInternal<ContentPresenter>();
if (itemDataTemplate != null && contentPresenter != null)
{
UserEditor userEditor = (UserEditor)itemDataTemplate.FindName("EditRow",
contentPresenter);
userEditor.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
userEditor.UpdateLayout();
userEditor.BeginAnimation(HeightProperty, new DoubleAnimation(0,
userEditor.DesiredSize.Height, new Duration(new TimeSpan(0, 0, 1)),
FillBehavior.HoldEnd), HandoffBehavior.Compose);
}
}
So my question is how can I get the size that a UserControl would be if its container placed no size restriction upon it, when its current height is 0?
I'd also be happy to hear if there is a better way to achieve my ultimate goal. Many thanks in advance.
You can put the content you want the size of into a Canvas with ClipToBound="True". Then you can manipulate the size of the Canvas and yet the size of the content inside the Canvas will always be its full desired size.
Maybe it's easier to animate the LayoutTransform.ScaleY of your target from 0 to 1, because desired height is always 1 and no extra control is needed.