I am trying to trigger a storyboard animation when the click event of a button in a usercontrol
is clicked. The parent doesn't recognise the name of the button within the usercontrol
ie trying UserControl.MyButton as SourceName doesn't work. How can I access the name of the button and use in the SourceName for the EventTrigger
Here is the pseudo code of main window
<Window>
<EventTrigger RoutedEvent="Button.Click" SourceName="???" >
<BeginStoryboard >
-----------------------
</BeginStoryboard>
</EventTrigger>
<UserControl />
</Window>
Here is the user control defined in separate xaml
<UserControl>
<Button x:Name="MyButton" />
</UserControl>
Actually, it seems that SourceName does not have to be an element in the current namescope. The following code works for Button1 and not for Button2. EvenTrigger should be checking the original source name when handling the event:
<StackPanel>
<StackPanel.Triggers>
<EventTrigger RoutedEvent="Button.Click" SourceName="Button1">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation To="30" Storyboard.TargetName="TextBlock1" Storyboard.TargetProperty="FontSize" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</StackPanel.Triggers>
<TextBlock x:Name="TextBlock1" Text="Text" FontSize="12" />
<local:UserControl1 x:Name="UC" />
</StackPanel>
<UserControl>
<StackPanel>
<Button x:Name="Button1" Content="Click"/>
<Button x:Name="Button2" Content="Click"/>
</StackPanel>
</UserControl>
Related
I have a view of a viewmodel as defined below
<DataTemplate DataType="{x:Type local:TestViewModel}">
<Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" >
<Button Name="btnStart" Command="{Binding Start}" Margin="5,0,5,0" />
<Button Name="btnCancel" Command="{Binding Cancel}" Margin="5,0,5,0" />
</StackPanel>
</Grid>
</DataTemplate>
So when i click on startButton , I want to start an animation i.e i want to move an arrow image from top of my screent to bottom.
I tried defining the image in grid, defining storyboard in style and attaching datatrigger to property .But this is causing the whole ui to expand rather than the animation moving over the screen.
I want the animation to move over my stackPanel. How do i do that?
Please try the next animation example(it used the TranslateTransform of the Image):
<Grid x:Name="AnimationAreaRootGrid">
<Image x:Name="Image" HorizontalAlignment="Left" VerticalAlignment="Top" Source="Res/Koala.jpg" Width="50" Height="50" RenderTransformOrigin="1.0,1.0">
<Image.RenderTransform>
<TranslateTransform />
</Image.RenderTransform>
</Image>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Bottom" >
<Button Name="btnStart" Content="Animate Down" Margin="5,0,5,0">
<Button.Resources>
<Storyboard x:Key="DownMovingStoryBoardKey" Storyboard.TargetName="Image">
<!--From - the animation start point-->
<!--From - the animation end point-->
<DoubleAnimation Storyboard.TargetProperty="(Control.RenderTransform).(TranslateTransform.Y)"
From="0"
To="250"
Duration="0:0:1" />
</Storyboard>
</Button.Resources>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource DownMovingStoryBoardKey}"/>
</EventTrigger>
</Button.Triggers>
</Button>
<Button Name="btnCancel" Margin="5,0,5,0" Content="Animate Up">
<Button.Resources>
<Storyboard x:Key="UpMovingStoryBoardKey" Storyboard.TargetName="Image">
<!--From - the animation start point-->
<!--From - the animation end point-->
<DoubleAnimation Storyboard.TargetProperty="(Control.RenderTransform).(TranslateTransform.Y)"
From="250"
To="0"
Duration="0:0:1" />
</Storyboard>
</Button.Resources>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource UpMovingStoryBoardKey}"/>
</EventTrigger>
</Button.Triggers>
</Button>
</StackPanel>
</Grid>
Explanations
A clicking on a btnStart will trigger the Down moving animation so the image will be moved down.
A clicking on a btnCancel will trigger the Up moving animation so the image will be moved up.
Regards.
I would use a Canvas and have your Image animated by changing Canvas.Left/Canvas.Top -values.
Canvas, unlike StackPanel, is not affected by it's children when it is doing it's own layout. The downside is that you would have to locate your StackPanel's coordinates, if the animation by the StackPanel coordinates is a requirement.
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" >
<Canvas>
<Image ... />
</Canvas>
<Button Name="btnStart" Command="{Binding Start}" Margin="5,0,5,0" ></Button>
<Button Name="btnCancel" Command="{Binding Cancel}" Margin="5,0,5,0"></Button>
</StackPanel>
How about using a TranslateTransform to animate your Image? Use it with the UIElement.RenderTransform property and you won't affect the layout of the rest of your view.
More infos on WPF transforms:
https://msdn.microsoft.com/en-us/library/ms750596(v=vs.100).aspx
General overview for WPF animations:
https://msdn.microsoft.com/en-us/library/ms752312(v=vs.100).aspx
So I have an expander that I want to have the normal functionality (open and close with its own button) but I also want a different button to expand it when pressed (this button is in the header of the expander). I'm using a storyboard in an event trigger for the Button.Click which works, but after it is expanded this way the normal button doesn't work, it just stays expanded. My xaml is below, I would really prefer to keep this all in the xaml, I could come up with a way to do it in the codebehind/viewmodel myself.
<Expander x:Name="IndexExpander" IsExpanded="True" Grid.Row="4" Grid.ColumnSpan="5" Margin="10" MaxHeight="150">
<Expander.Triggers>
<EventTrigger SourceName="btnAddIndex" RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="IndexExpander" Storyboard.TargetProperty="IsExpanded" BeginTime="0:0:0.25" Duration="0:0:0.20" >
<DiscreteBooleanKeyFrame KeyTime="0" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Expander.Triggers>
<Expander.Header>
<StackPanel DockPanel.Dock="Left" Orientation="Horizontal">
<TextBlock Text="Indexes" FontWeight="Bold"/>
<!-- Add/Delete Buttons-->
<Grid Margin="10,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*"/>
<ColumnDefinition Width="50*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" x:Name="btnAddIndex" Command="{Binding AddIndexCommand}" Template="{StaticResource AddButtonTemplate}" IsEnabled="{Binding IsEditable}" Margin="0,0,5,0" />
</Grid>
</StackPanel>
</Expander.Header>
Alright, so for anyone following in my footsteps here's what I did. I got the idea from here, and adapted it until it worked correctly.
<Expander.Triggers>
<EventTrigger SourceName="btnAddCol" RoutedEvent="Button.Click">
<BeginStoryboard x:Name="ColumnExpanderStory">
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetName="ColumnExpander" Storyboard.TargetProperty="IsExpanded">
<DiscreteBooleanKeyFrame KeyTime="0" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="ToggleButton.PreviewMouseUp">
<RemoveStoryboard BeginStoryboardName="ColumnExpanderStory" />
</EventTrigger>
</Expander.Triggers>
<Expander.Header>
The problem was that the storyboard overrides any other bindings to the IsExpanded property, so it has to be removed to restore them (read more here). The suggestion was to use the ToggleButton.Checked event to remove the storyboard, but that didn't work for me, only the "Preview" events seemed to have the right timing. I started with PreviewMouseDown, but it would remove the storyboard, then on mouse up toggle the expander, meaning the first click would just flip states back and forth quickly. Using PreviewMouseUp got around that issue.
Today is a good day since I started with WPF, this for a launcher I'm creating.
Using the following code, I managed to get the result to be seen in the screenshot:
<Grid>
<ItemsControl ItemsSource="{Binding Programs}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Text}" Background="Transparent" Foreground="White" Width="128" Height="150" >
<Button.RenderTransform>
<TransformGroup>
<ScaleTransform />
</TransformGroup>
</Button.RenderTransform>
<Button.Template>
<ControlTemplate TargetType="Button">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Grid.Row="0" Source="{Binding Image}" Height="128" />
<ContentPresenter Grid.Row="1" HorizontalAlignment="Center" Margin="3,10" />
<Rectangle Grid.Row="0" Fill="{TemplateBinding Background}" />
<Rectangle Grid.Row="1" Fill="{TemplateBinding Background}" />
</Grid>
</ControlTemplate>
</Button.Template>
<Button.Resources>
<Storyboard SpeedRatio="4" x:Key="MouseEnterStoryboard" x:Name="MouseEnterStoryboard">
<ColorAnimation Storyboard.TargetProperty="(Button.Background).(SolidColorBrush.Color)" To="#22FFFFFF"></ColorAnimation>
</Storyboard>
<Storyboard SpeedRatio="4" x:Key="MouseLeaveStoryboard" x:Name="MouseLeaveStoryboard">
<ColorAnimation Storyboard.TargetProperty="(Button.Background).(SolidColorBrush.Color)" To="Transparent"></ColorAnimation>
</Storyboard>
<Storyboard Duration="00:00:00.05" x:Key="MouseClickStoryboard" AutoReverse="True">
<DoubleAnimation To="0.8" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"/>
<DoubleAnimation To="0.8" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"/>
</Storyboard>
<Storyboard x:Key="WindowLoadedStoryboard">
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="00:00:01" />
</Storyboard>
</Button.Resources>
<Button.Triggers>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<BeginStoryboard Storyboard="{StaticResource MouseEnterStoryboard}" />
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseLeave">
<BeginStoryboard Storyboard="{StaticResource MouseLeaveStoryboard}" />
</EventTrigger>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource MouseClickStoryboard}" />
</EventTrigger>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard Storyboard="{StaticResource WindowLoadedStoryboard}"></BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Screenshot:
Now, for each item in the list bound to this control, it will create a button.
How would I access this button programmatically, better yet, how would I access one of the Storyboards programatically since assigning a name (x:to them simply won't do the trick it seems...
Also, how can I animate the buttons one by one? Currently they each fade in at exact the same time (# WindowLoadedStoryboard), but I would like to let each button fade in one by one with a short delay, to create a nice effect. How would I achieve this?
Hope someone can answer these 2 questions for me!
Greetings!
Your problem with accessing the elements defined in the DataTemplate is caused because you defined those elements in a DataTemplate... those elements could be rendered in many different types of UI container controls. You can find the solution in the How to: Find DataTemplate-Generated Elements page from MSDN.
You first need to get hold of the relevant container control that contains the item that has had that DataTemplate applied to it. Next, you need to get the ContentPresenter from that container control and then you can get the DataTemplate from ContentPresenter. Finally, you can access the named elements from the DataTemplate. From the linked page:
// Getting the currently selected ListBoxItem
// Note that the ListBox must have
// IsSynchronizedWithCurrentItem set to True for this to work
ListBoxItem myListBoxItem = (ListBoxItem)(myListBox.ItemContainerGenerator.
ContainerFromItem(myListBox.Items.CurrentItem));
// Getting the ContentPresenter of myListBoxItem
ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(myListBoxItem);
// Finding textBlock from the DataTemplate that is set on that ContentPresenter
DataTemplate myDataTemplate = myContentPresenter.ContentTemplate;
TextBlock myTextBlock =
(TextBlock)myDataTemplate.FindName("textBlock", myContentPresenter);
// Do something to the DataTemplate-generated TextBlock
MessageBox.Show("The text of the TextBlock of the selected list item: " +
myTextBlock.Text);
The following XAML code creates a button with a border as its content. The border has a TextBlock as it's child. When you CLICK on the button, the border width gradually grows.
Since the text block is inside the border it is not seen until you click the button.
Now my requirements are:
There should be a text displayed on the button even before you click it.
Even after you click the button, only the border should animate while the button text remains in its center position.
<Button Name="button5" Width="100" Margin="10" HorizontalContentAlignment="Left">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="myBorder" Storyboard.TargetProperty="Width" From="0" To="94" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Button.Triggers>
<Button.Content>
<Border Background="Gray" Width="0" Name="myBorder">
<TextBlock>Search</TextBlock>
</Border>
</Button.Content>
</Button>
I will appreciate if anyone is able to provide answers with working XAML code.
I've modified your XAML only slightly:
<Button Name="button5" Width="100" Margin="10" HorizontalContentAlignment="Left">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="myRectangle" Storyboard.TargetProperty="Width" From="0" To="94" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Button.Triggers>
<Button.Content>
<Grid>
<Rectangle x:Name="myRectangle" Fill="Gray" Width="0" HorizontalAlignment="Left" />
<TextBlock Text="Search" />
</Grid>
</Button.Content>
</Button>
For the Button's content, you could use a Grid which contains your Border and the TextBlock. Since no columns or rows are specified, the TextBlock and the Border are drawn on top of each other. It situations like this where the Border does not have content, I prefer to use a Rectangle instead since it is bit lighter. Hope this helps!
Below is the data template I'm using for a listbox's ItemTemplate. It shows some simple data, and a button, which ideally should animate a Popup, also contained within the DataTemplate. Unfortunately the whole thing blow up at runtime. The error says line 52 is wrong, which is:
<EventTrigger RoutedEvent="Button.Click">
Here's the whole DataTemplate. I've used this popup before, with the same exact content templates and even the same animation elsewhere. It's only blowing up when I try to put it into a DataTemplate. I assume the animation is having difficulty finding the right animation target - I'm hoping someone here would know more.
<DataTemplate x:Key="ItemTemplate2">
<Border Width="100" Height="100" BorderThickness="4" BorderBrush="Red">
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Price}"/>
<Popup x:Name="popupContent" IsOpen="True" Margin="10,0,0,0" Grid.Row="0" >
<Popup.Child>
<Thumb x:Name="thumbContent" DragDelta="Thumb_DragDelta" Width="0" Height="0">
<Thumb.Template>
<ControlTemplate>
<local:thumbTemplate Margin="0" x:Name="df" />
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Popup.Child>
</Popup>
<Button Content="Show">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<BeginStoryboard.Storyboard>
<Storyboard x:Name="sbShowPopup">
<DoubleAnimation Duration="0:0:1" To="200" Storyboard.TargetProperty="(FrameworkElement.Width)" Storyboard.TargetName="popupContent" d:IsOptimized="True"/>
<DoubleAnimation Duration="0:0:1" To="80" Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="popupContent" d:IsOptimized="True"/>
</Storyboard>
</BeginStoryboard.Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</StackPanel>
</Border>
</DataTemplate>
You may have used this before but not in Silverlight. The only supported value for RoutedEvent in Silverlight is "FrameworkElement.LoadedEvent".
You will need the BlendSDK to do this sort of thing in Silverlight.