I have a TextBlock on my page with Text value null (""). When I click a button I want to change the Text value for this TextBlock, pause half a second and then move the TextBlock one pixel at a time to a certain point.
I tried using Thread.Sleep(), but so far, I have a problem. I click the button, the UI thread pauses for half a second, then the TextBlock suddenly appears and starts moving. I want it to appear as soon as I click the button.
P.S.: I know Thread.Sleep() doesn't work. I am willing to use anything that works.
Storyboards and animations are the preferred mechanism for moving items on the screen. For one thing, they are optimized to work with the phones threading model. For another, putting your UI thread to sleep is a bad idea as you are making a non responsive application.
Here's a quick example of how to move a texblock with a story board.
The UI elements.
<Grid
x:Name="ContentPanel"
Grid.Row="1"
Margin="12,0,12,0">
<TextBlock
Margin='79,263,177,307'
Name='textBlock1'
Text='TextBlock'
RenderTransformOrigin="0.5,0.5">
<TextBlock.RenderTransform>
<CompositeTransform />
</TextBlock.RenderTransform>
</TextBlock>
<Button
Content="Button"
Height="80"
Margin="116,0,188,144"
VerticalAlignment="Bottom"
Click='Button_Click' />
</Grid>
The storyboard, defined in the page resources section.
<phone:PhoneApplicationPage.Resources>
<Storyboard
x:Name="MoveTextBlockStoryboard">
<DoubleAnimationUsingKeyFrames
Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateX)"
Storyboard.TargetName="textBlock1">
<EasingDoubleKeyFrame
KeyTime="0"
Value="0" />
<EasingDoubleKeyFrame
KeyTime="0:0:1.1"
Value="120" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames
Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateY)"
Storyboard.TargetName="textBlock1">
<EasingDoubleKeyFrame
KeyTime="0"
Value="0" />
<EasingDoubleKeyFrame
KeyTime="0:0:1.1"
Value="-105" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
The code that changes the text and starts the storyboard.
private void Button_Click(object sender, RoutedEventArgs e) {
textBlock1.Text = "new text";
MoveTextBlockStoryboard.Begin();
}
Try using a storyboard instead of writing code to do this. I think it will perform better for you than a manual approach.
Related
Im recently working on some simple Imageviewer.
Now it came to my mind, it might be a nice feature, to do some context-sensitve actions like Zooming and rotating.
To implement these functions is not my problem, but the ContextMenu is.
I've decided to not use a ContextMenu-Element, instead im going to use a popup.
Reasons for PopUp:
Less Styling
Better Positioning
IsOpen is Bindable (ContextMenu is NOT bindable on IsOpen against all Articles regarding this)
Here comes the trouble:
<Image x:Name="PART_ImgCurrent" VerticalAlignment="Center" HorizontalAlignment="Center" Stretch="Uniform" Grid.Row="0" Grid.Column="0"
Source="{Binding ElementName=PART_PreviewPanel, Path=SelectedItem.Source}">
<Image.LayoutTransform>
<RotateTransform Angle="0"></RotateTransform>
</Image.LayoutTransform>
<Image.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="PART_ImgCurrent" Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:3" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Image.Triggers>
</Image>
<Popup IsHitTestVisible="False" Focusable="False" PlacementTarget="{Binding ElementName=PART_ImgCurrent}" AllowsTransparency="True" StaysOpen="True"
IsOpen="{Binding ElementName=PART_ImgCurrent, Path=IsMouseOver, Mode=OneWay}"
Placement="Right" HorizontalOffset="-42" VerticalOffset="2">
<StackPanel Opacity="0.5" VerticalAlignment="Stretch">
<Button Content="Ugly Button" Height="40" Width="40"></Button>
<Button Content="Ugly Button" Height="40" Width="40"></Button>
<Button Content="Ugly Button" Height="40" Width="40"></Button>
</StackPanel>
</Popup>
As you can see, im binding IsOpen of Popup to IsMouseOver on Image which results in a funny Disco-BlinkenLights-Behavior when i try to click a button inside the Popup.
What has this to do with the Title?
AcrobatReader has this
This is almost exactly the behavior im looking for. How is this thing called?
Or had someone ever similar issues and could provide a solution?
Sorry for the delay, soon as I thought I had a second I got busy again. Anyway, here's one of several ways I can think of accomplishing your goal, give it a shot. I sort of assumed it may not be just images you want this for and if you threw the resource stuff in a dictionary and kept your naming consistent (or even better, just target the nested UIElement) you could use it all over the place. Notice the Grid is acting as what would be the Image in this example.
I generally make things open for future added interactions and stuff, so in this case I would probably just make the image source the background brush for Grid or place it as a child. That way if you decide to add other objects in there or say other effects and stuff you've got a good start point.
Anyway I digress, so try out the concept example below and see if it's what you're after. If not, like I said there's several other ways I can think of to accomplish your goal so just let me know. :)
<!-- HitTestVisibility Area -->
<Grid x:Name="ImagePlaceholder"
Height="500" Width="500"
Background="LightBlue">
<Grid.Resources>
<Storyboard x:Key="OnMouseEnter">
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)"
Storyboard.TargetName="FakePopUp">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="OnMouseLeave">
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)"
Storyboard.TargetName="FakePopUp">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</Grid.Resources>
<Grid.Triggers>
<EventTrigger RoutedEvent="UIElement.MouseEnter">
<BeginStoryboard Storyboard="{StaticResource OnMouseEnter}"/>
</EventTrigger>
<EventTrigger RoutedEvent="UIElement.MouseLeave">
<BeginStoryboard Storyboard="{StaticResource OnMouseLeave}"/>
</EventTrigger>
</Grid.Triggers>
<!-- Overlay -->
<Border Name="FakePopUp" Visibility="Collapsed"
Margin="0,0,0,25" Background="SlateGray"
Height="50" CornerRadius="20" Padding="10"
HorizontalAlignment="Center" VerticalAlignment="Bottom">
<StackPanel Orientation="Horizontal">
<Button Content="Eins Bier"/>
<Button Content="Zwei Bier" Margin="10,0"/>
<Button Content="Drei Bier"/>
</StackPanel>
</Border>
</Grid>
I went with Storyboards attached to the parent instead of direct triggers with TargetName like I said, because I could think of a bunch of other instances features might want to be added that would make sense. Even something simple like adding a transition duration for a nice fade effect or maybe a translate y to slide it up while fading etc, etc, etc.
Anyway, hope this helps. Cheers!
I don't know how to name this, but the purpose is to when I click the right mouse button and then the left button right after (without opening the popup that usually opens with the right mouse button), it should popup this "settings menu" with an animation like this one:
or just a "size in". If it is not possible or its to complicate to open with the mouse buttons, it could be done with any key/combination from the keyboard.
Anyone has any idea how I could do this? Im using C# WPF and Blend.
I may have a partial answer for you. You can use a Radial Menu as the popup. There are many paid Radial Menu controls, but this (nugget package here) one seems open source and looks good (I haven't used it).
Regarding that loading animation, Expression Drawing might have a convenient Arc drawing to do that.
<ed:Arc x:Name="arc" Stretch="None" Height="64" Width="64" ArcThicknessUnit="Pixel" EndAngle="360" HorizontalAlignment="Stretch" Stroke="Red" StrokeThickness="5" StartAngle="0" />
And a simple storyboard can animate it.
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(ed:Arc.EndAngle)" Storyboard.TargetName="arc">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="360"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
Visual Studio Blend should help you do it very easily.
This is more for advertising deals to potential customers so there is no human interaction component to this.
Right now I just have the elements bound to an ItemsControl and a storyboard animation loops through. Unfortunately I want to show 4 items at a time, pause on them for 10 seconds, then show the next 4. I could have 5 coupons, I could have 30, so I can't enter anything in statically except that I know my visible width (they will be rotating horizontally) is 1920px.
My current implementation, which displays 1 to 4 then 5 to 8 and loops back to "1" is:
<ItemsControl Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" ItemsSource="{Binding Path=VisibleDigitalCoupons}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<DataTemplate.Resources>
<Storyboard x:Key="RotateDigitalCoupons" BeginTime="0:0:0" Duration="0:0:10" RepeatBehavior="Forever" Completed="RotateDigitalCouponsCompleted">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.X)" Storyboard.TargetName="digitalCouponView">
<EasingDoubleKeyFrame KeyTime="0:0:5" Value="0"></EasingDoubleKeyFrame>
<EasingDoubleKeyFrame KeyTime="0:0:10" Value="-1920">
<EasingDoubleKeyFrame.EasingFunction>
<CubicEase EasingMode="EaseInOut"></CubicEase>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</DataTemplate.Resources>
<views:DigitalCouponView x:Name="digitalCouponView" Margin="40,40,20,20" Height="240" Width="420" RenderTransformOrigin="0.5,0.5" >
<views:DigitalCouponView.RenderTransform>
<TransformGroup>
<TranslateTransform/>
</TransformGroup>
</views:DigitalCouponView.RenderTransform>
</views:DigitalCouponView>
<DataTemplate.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard x:Name="RotateDigitalCoupons_BeginStoryboard" Storyboard="{StaticResource RotateDigitalCoupons}"/>
</EventTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Attempts to use the Completed event to fire off a refresh of the "visible coupons" and reuse the animation have met with failure because the animation is on repeatbehavior forever. However, even with that off, I don't get completed event firing, so that's a dead end AFAIK.
Anyone have any ideas or dealt with this before? Is my process flawed somehow?
Create a Dispatch Timer which contains a state machine which will execute the logic depending on the current state and handle the data driven components of what will be displayed. Within the timer turn on and off the animations as required.
You will need to make the animations more generic of course, but you have the framework which can be leveraged by the timer.
I'm having trouble getting a storyboard value to bind to my viewmodel. I've tried many varieties of binding xaml, with no luck. The high level goal is to be able for the ViewModel to change the start and end of an animation's trajectory - seems like a common requirement, but haven't been able to find any examples of it. Many people say "In MVVM you should never try to access the storyboard from the ViewModel" which seems like horeshit if you need to change the start and end points of the animation on the fly. In any case, I've shown my first, naive example here:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:FishTank" x:Class="FishTank.FishTankControlView"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="100" >
<UserControl.Resources>
<Storyboard x:Key="Storyboard1">
<DoubleAnimationUsingKeyFrames x:Name="xTransform" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="image">
<SplineDoubleKeyFrame KeyTime="0" Value="-155"/>
<SplineDoubleKeyFrame KeyTime="0:0:4.5" Value="521"/>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames x:Name="yTransform" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="image">
<SplineDoubleKeyFrame KeyTime="0" Value="{Binding YPos1}"/>
<SplineDoubleKeyFrame KeyTime="0:0:4.5" Value="{Binding YPos2}"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>
<UserControl.DataContext>
<local:FishTankControlViewModel/>
</UserControl.DataContext>
<UserControl.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard Storyboard="{DynamicResource Storyboard1}"/>
</EventTrigger>
</UserControl.Triggers>
<Grid Background="Transparent">
<Image x:Name="image" Width="100" Height="100" Source="pack://siteoforigin:,,,/Resources/Fish orange_right.png" RenderTransformOrigin="0.5,0.5">
<Image.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Image.RenderTransform>
</Image>
</Grid>
I want to have Ypos1 and Ypos2 bound to the start and end points of the Y transformation. I've tried various variations using RelativeSource and FindAncestor, but it's all pretty much over my head. I would like a solution that allows me to stick with the 3.5 framework if possible.
I tried changing the "StaticResource Storyboard1" to "DynamicResource Storyboard1" with no luck.
Note - As a sanity check, I confirmed I am able to bind normal controls like buttons and textblocks to these two properties, Ypos1 and Ypos2, so I'm pretty sure the basic plumbing is working...
Thanks much,
Randy
You can't bind those because they need to be freezable, pretty sure the framework told you so as well.
If anything you can completely recreate or modify the animation with new values, using a ValueConverter or a subclass that encapsulates the animation being modified.
I just wanna ask if there's a way where I could put an object (circle) at the end of a particular line path.
Similar to this:
--------------------------------------------O
Start End
Right now, I have the following code for tracing the line:
<Grid x:Name="LayoutRoot" >
<Path Stroke="Red" StrokeThickness="4" x:Name="path4" Data="{Binding MyProperty1}" >
<Path.StrokeDashArray>
<System:Double>500</System:Double>
<System:Double>1000</System:Double>
</Path.StrokeDashArray>
</Path>
</Grid>
where the data of my path (e.g. M532,668 L523,695 361,663 101,678 117,638) varies.
my animation looks like this...
<Storyboard x:Key="Story1" RepeatBehavior="Forever">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="path1"
Storyboard.TargetProperty="(Shape.StrokeDashOffset)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="500"/>
<SplineDoubleKeyFrame KeyTime="00:00:08" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
Any suggestions?
There are at least a couple of ways you could do this; which is best probably depends on the relationship of the circle to the line.
If the circle is conceptually part of the same shape as the line, change your Path to include an ellipse (arc) at the end of the line. This could be done by changing the path Data, either by adding a circle to the end or by adding another Figure to the PathGeometry.
If the circle is conceptually a separate component, and you just want to place that component next to the line, you can just use a StackPanel with its Orientation set to Horizontal:
<StackPanel Orientation="Horizontal">
<Path /> <!-- The line -->
<Ellipse /> <!-- The circle -->
</StackPanel>
(Note: in some scenarios, you can do this using the EndLineCap property. That won't work in this case though because it looks like you want the circle to be bigger than the stroke thickness. Line caps are always the same thickness as the line.)