I am using WPF for my C# application on which i have several labels in which the texts don't fit. What I want is to make my text move inside the label so that the user can see the entire text.
Example : let's say that my text is 20 characters long, by my label has enough space for only 15 characters. What I want is that my label to display the first 15 characters (characters 1-15) then after 1 second the same characters without the first but with another at the end (so characters 2-16) then the next (characters 3-17) and so on until the last 15 characters (characters 5-20) and then I want them to start from the beginning (the characters 1-15 again).
How can I do that ? One way is (obviously) to use a Timer, but I am sure that a more elegant solution exists.
Below is code for a quick-and-dirty scrolling control that should do what you want as soon as the user mouses over the control.
<Canvas Name="brd"
ClipToBounds="True"
Margin="10"
Height="20" Width="150"
Background="White"
HorizontalAlignment="Left">
<StackPanel Name="spl1"
Orientation="Horizontal"
Canvas.Left="0">
<TextBlock Name="tbk1"
Margin="10,0"
MinWidth="{Binding ElementName=brd,Path=ActualWidth}"
Text="A display of test text that is wider than the control."/>
<TextBlock MinWidth="{Binding ElementName=brd,Path=ActualWidth}"
Margin="10,0"
Text="{Binding ElementName=tbk1,Path=Text}"/>
</StackPanel>
<Canvas.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard Name="scroll">
<Storyboard RepeatBehavior="Forever">
<DoubleAnimation To="-200" Duration="0:0:4"
Storyboard.TargetName="spl1"
Storyboard.TargetProperty="(Canvas.Left)" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<StopStoryboard BeginStoryboardName="scroll"/>
</EventTrigger>
</Canvas.Triggers>
</Canvas>
There is one part that you will need to change for proper operation, that I did not implement. The <DoubleAnimation To="-200"... will need to be changed. Ideally the value for To will be the negative of the ActualWidth property of the tbk1 control. This will require both an element binding to get the value and a ValueConverter to make that value negative.
If you don't want to go to the trouble of a converter, you can just make the To value sufficiently large to accommodate the longest text that you expect. That will get you reasonably close.
You can, of course, tweak this more to provide a smoother transition at the end of one animation cycle and the start of the next.
EDIT
OK, I couldn't leave it half finished. Below is the updated XAML for smooth operation, and the included code for the ValueConverter.
<Window.Resources>
<Converters:ChangeSignConverter x:Key="ChangeSignConverter"/>
</Window.Resources>
<Canvas Name="brd"
ClipToBounds="True"
Margin="10"
Height="20" Width="150"
Background="White"
HorizontalAlignment="Left">
<StackPanel Name="spl1"
Margin="5,0,0,0"
Orientation="Horizontal"
Canvas.Left="0">
<TextBlock Name="tbk1"
Padding="0,0,10,0"
MinWidth="{Binding ElementName=brd,Path=ActualWidth}"
Text="A display of test text that is wider than the control."/>
<TextBlock MinWidth="{Binding ElementName=brd,Path=ActualWidth}"
Text="{Binding ElementName=tbk1,Path=Text}"/>
</StackPanel>
<Canvas.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard Name="scroll">
<Storyboard RepeatBehavior="Forever">
<DoubleAnimation To="{Binding ElementName=tbk1,Path=ActualWidth,Converter={StaticResource ChangeSignConverter}}"
Duration="0:0:4"
Storyboard.TargetName="spl1"
Storyboard.TargetProperty="(Canvas.Left)" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<StopStoryboard BeginStoryboardName="scroll"/>
</EventTrigger>
</Canvas.Triggers>
</Canvas>
Converter class:
class ChangeSignConverter : IValueConverter
{
object IValueConverter.Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Convert.ToDouble(value) * -1;
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Convert.ToDouble(value) * -1;
}
}
You could also use the WPF animation functionality. You create a custom control which has a TextBlock inside. Define the control's width (which would be smaller than the textblock's width. Then define the storyboard and double animation to animate it however you want it (slower / faster, only animate to the right, animate right then left and so on).
This is just an idea. I'm currently in a rush, but I can provide you with additional code, if you need it.
I would also use Timer for functionality you described. I think there are not many other solutions, if you insist on changing Label in some specified time interval. On the other hand, I would probably use ToolTip, which I think is more intuitive for this.
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!
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 am trying to create a user control similar to a stock ticker. It will slowly scroll the items to the left. So far I am unable to get something going because:
I am new to WPF/XAML and everywhere I search seems to apply to
silvelight
I am using an ItemTemplate within a ItemsControl, and cant find any
examples showing how to add a storyboard for a item within an item template.
here is my XAML:
<UserControl x:Class="WpfApplication.Ctrls.MessageTicker"
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"
mc:Ignorable="d"
xmlns:ctrls="clr-namespace:WpfApplication.Ctrls"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Background="Honeydew">
<ItemsControl ItemsSource="{Binding}" Name="_items">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid x:Name="_panel">
<Grid.Triggers>
<EventTrigger RoutedEvent="Grid.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(Grid.RenderTransform).(TranslateTransform.X)"
Storyboard.TargetName="_panel"
From="0"
To="360"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=Word}" Grid.Column="0" Margin="0" FontWeight="Normal" FontFamily="Segoe UI Semibold"/>
<TextBlock Text="{Binding Path=Percent}" Grid.Column="1" Margin="5,0,3,0" FontFamily="Segoe UI Mono" Foreground="Blue"/>
<Polygon Points="0,10 5,0 10,10" Stroke="Black" Fill="Green" Grid.Column="2" Margin="0,3,10,0"/>
<Polygon Points="5,10 10,0 0,0" Stroke="Black" Fill="Red" Grid.Column="2" Visibility="Collapsed"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
From what I have read, I should be putting the storyboard within the template. When I add a new item to the ItemsControl, nothing happens. I need to have it start at the end of the ItemsControl and scroll to the right all the way to the beginning of the ItemsControl.
Ok so you're close, but you're missing some key things that might explain why it's not firing as you'd expect.
So your first issue lies with how you're using _panel. We'll want to actually move that to an RenderTransform on this Grid that's acting as your parent.
So instead of;
<Grid x:Name="_panel">
We'll say;
<Grid>
<Grid.RenderTransform>
<TranslateTransform x:Name="_panel">
</Grid.RenderTransform>
...
Because your TargetProperty setting in your storyboard isn't going to magically append that Transform on demand like it looks you're thinking it might (at least I've never seen it done that way I don't think).
So we have that, now let's go talk to that Transform via your Storyboard and have it talk to an actual property of your Transform thusly;
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="X"
Storyboard.TargetName="_panel">
<SplineDoubleKeyFrame KeyTime="0:0:0" Value="360" />
</DoubleAnimation>
</Storyboard>
So what we're doing is basically saying "HEY _panel, guess what? You get to animate, and before you start I want you to know you're moving 360 across your X axis"
We could add a keytime here to make this happen over more keyframes to allow it to fill in the blanks of an actual animation sequence (your ticker anime, hint hint) but for now, we're just telling that bugger to move.
Other than that, you should work. Hope this helps, cheers.
Oh and PS, you'd be surprised how much XAML can work between WPF/SL/WP, etc. etc. if you just know the fundamentals. Best of luck!
I'm trying to set an event trigger to one of my controls inside a ContentTemplate, i'm using a storyboard and a DoubleAnimation nested with a DoubleAnimationUsingKeyFrames when i set the storyboard TargetName to "ContentPopup" wich is a Grid i hold below, but i get an error telling me that:
The name 'ContentPopup' is not in the namespace 'System.Windows.Controls.Grid'.
The animation code on the control template is:
<Grid Margin="0" Width="55" Height="40">
<Grid.Triggers>
<EventTrigger RoutedEvent="m:Pushpin.MouseDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Duration="00:00:00.5000000" Storyboard.TargetName="ContentPopup" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" To="1">
...
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
...
</Grid>
The code on the grid i need to animate is:
<Grid Name="ContentPopup"
Background="White"
Opacity="0.85"
RenderTransformOrigin="0,0">
<Grid.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="0" ScaleY="0"/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Grid.RenderTransform>
...
</Grid>
MSDN says that the following needs to be done to make an object targetable
and a grid is a FrameworkElement i want the animation to run on every single one of the controls i summon on my main window using this control template, they are a lot of them so i need to use the template.
The question is:
Is there a way to assign the element as a target in the template?
So i found the right way to do it, even though grid is a FrameworkElement you can't access it from the control template, so you need to provide some binding or XAML reference to it in order to work, so with the same exact code just instead of using the Storyboard.TargetName property using the Storyboard.Target with value:
Storyboard.Target="{Binding ElementName=ContentPopup}"
or also:
Storyboard.Target="{x:Reference Name=ContentPopup}"
it worked for me
I have simple graphic images which I would like to transform on triggered events. Transform means change the width or move it to another position.
For now I use the Image element of the toolbox and Animations via Storyboard, for instance DoubleAnimation or ThicknessAnimation.
However the following issues arise:
the images flickers when changing the width
the image quality varies, does WPF support vector graphics?
Regarding 1. my question is, if other animations should be used.
So I tried the Transformation, see the code :
<Image Height="150" HorizontalAlignment="Left" Margin="12,0,0,0" Name="image1" Stretch="Fill" VerticalAlignment="Top" Source="images/side_view.jpg" Width="1244">
<Image.RenderTransform>
<ScaleTransform x:Name="Minimize" ScaleX="1.0" ScaleY="1.0"/>
</Image.RenderTransform>
</Image>
<Button Content="Next Train" Height="23" HorizontalAlignment="Left" Margin="528,233,0,0" Name="btnNext" VerticalAlignment="Top" Width="75" />
<Grid.Triggers>
<EventTrigger RoutedEvent="Button.Click" SourceName="btnNext">
<BeginStoryboard>
<Storyboard TargetName="Minimize" TargetProperty="ScaleX">
<DoubleAnimation To="0.65" Duration="0:0:2"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
Works the same as the Animation I applied to width and margin. However, it still flickers! Are there any reasonable explanations?
If you're animating the Width of an image, you're making WPF re render the image, and create it from scatch each time. It goes through both the Layout process and the Rendering process, which makes it flicker and not work as best as it could be.
The best option here is to animate a ScaleTransform. ScaleTransform is done completely in hardware via DirectX and therefor will be extremlely fast, it won't flicker, and the image quality should stay pretty much the same the whole way. (unless ofcourse you are resizing it substantially in which case you'll lose percision.)