I'm trying to cobble together examples of listbox items on a (rendered via canvas) that expand in size when clicked. I'm close but the items move when zoomed, I would like them to zoom from the center and stay in place. Here is sample data collection.
public class Item
{
public double X { get; set; }
public double Y { get; set; }
public string Name { get; set; }
public string Color { get; set; }
}
public class ItemsFactory
{
private List<Item> items;
public IEnumerable<Item> Items
{
get
{
return items ?? (items = new List<Item>()
{
new Item { Name = "One", X = 100, Y = 100, Color="Red" },
new Item { Name = "Two", X = 88, Y = 210, Color="Green" },
new Item { Name = "Three", X = 200, Y = 295, Color="Blue" }
});
}
}
}
And below is my WPF. Items get bigger when clicked but they also move. I have tried RenderTransformOrigin in various places with no luck.
<Window x:Class="WPFCards.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFCards"
Title="MainWindow" Height="395.268" Width="607.807" Background="Black" WindowStyle="None" WindowState="Maximized">
<Window.Resources>
<local:ItemsFactory x:Key="sampleItems" />
</Window.Resources>
<Grid DataContext="{StaticResource sampleItems}">
<ListBox ItemsSource="{Binding Items}"
Background="Black"
SelectionMode="Multiple" >
<ListBox.ItemTemplate>
<DataTemplate>
<Canvas>
<Canvas.RenderTransform>
<ScaleTransform x:Name="st" ScaleY="{Binding ScaleX, RelativeSource={RelativeSource Self}}" />
</Canvas.RenderTransform>
<Grid
Canvas.Left="{Binding X}" Canvas.Top="{Binding Y}">
<Ellipse Fill="{Binding Color}" Width="50" Height="40" />
<ContentPresenter Content="{Binding Name}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
</Canvas>
<DataTemplate.Resources>
<CubicEase x:Key="ease" EasingMode="EaseOut"/>
</DataTemplate.Resources>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Duration="0:0:0.3"
EasingFunction="{StaticResource ease}"
Storyboard.TargetName="st"
Storyboard.TargetProperty="ScaleX"
To="2"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Duration="0:0:0.3"
EasingFunction="{StaticResource ease}"
Storyboard.TargetName="st"
Storyboard.TargetProperty="ScaleX"
To="1"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
RenderTransform should be applied on ellipse container i.e. Grid and not on Canvas. Setting it on canvas is increasing the width and height of grid and hence affecting layout of ellipses in it.
Also, in case you want centric zoom, set RenderTransformOrigin="0.5,0.5" on Grid.
<Canvas>
<Grid Canvas.Left="{Binding X}" Canvas.Top="{Binding Y}"
RenderTransformOrigin="0.5,0.5">
<Grid.RenderTransform>
<ScaleTransform x:Name="st"
ScaleY="{Binding ScaleX,
RelativeSource={RelativeSource Self}}"/>
</Grid.RenderTransform>
<Ellipse Fill="{Binding Color}" Width="50" Height="40" />
<ContentPresenter Content="{Binding Name}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
</Canvas>
A bit of guessing: I think you should set
RenderTransformOrigin="0.5,0.5"
of your Canvas
Related
I want to know how to display current date and time on a WPF statusbar.
I know this is too basic a question, but I am new to .net WPF,
and I know this can be done in a Form application easily.
Thanks in advance.
Ting
Create a wpf project. In your MainWindow.xaml add the StatusBar and handle Loaded event of window.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WpfApp1.MainWindow"
Title="MainWindow" Height="450" Width="800" Loaded="Window_Loaded">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StatusBar Grid.Row="1">
<TextBlock x:FieldModifier="private" x:Name="myDateTime"/>
</StatusBar>
</Grid>
</Window>
In the MainWindow.xaml.cs add the following namespaces (if they are not exist):
using System;
using System.Windows;
using System.Windows.Threading;
And in the Loaded enevt handler you can use DispatcherTimer for update textblock text property every second:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
DispatcherTimer timer = new DispatcherTimer(TimeSpan.FromSeconds(1), DispatcherPriority.Normal, (object s, EventArgs ev) =>
{
this.myDateTime.Text = DateTime.Now.ToString("yyyy/MM/dd hh:mm:ss");
}, this.Dispatcher);
timer.Start();
}
Also there are a lot of examples for customize wpf controls using Template and Style properties. just search for it.
Styles and templates in WPF
The WPF StatusBar control
and many more.
Also you can implement your custom WPFTimer. This simple implementation get you an idea for do this.
public class WPFTimer : TextBlock
{
#region static
public static readonly DependencyProperty IntervalProperty = DependencyProperty.Register("Interval", typeof(TimeSpan), typeof(WPFTimer), new PropertyMetadata(TimeSpan.FromSeconds(1), IntervalChangedCallback));
public static readonly DependencyProperty IsRunningProperty = DependencyProperty.Register("IsRunning", typeof(bool), typeof(WPFTimer), new PropertyMetadata(false, IsRunningChangedCallback));
private static void IntervalChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
WPFTimer wpfTimer = (WPFTimer)d;
wpfTimer.timer.Interval = (TimeSpan)e.NewValue;
}
private static void IsRunningChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
WPFTimer wpfTimer = (WPFTimer)d;
wpfTimer.timer.IsEnabled = (bool)e.NewValue;
}
#endregion
private readonly DispatcherTimer timer;
[Category("Common")]
public TimeSpan Interval
{
get
{
return (TimeSpan)this.GetValue(IntervalProperty);
}
set
{
this.SetValue(IntervalProperty, value);
}
}
[Category("Common")]
public bool IsRunning
{
get
{
return (bool)this.GetValue(IsRunningProperty);
}
set
{
this.SetValue(IsRunningProperty, value);
}
}
public WPFTimer()
{
this.timer = new DispatcherTimer(this.Interval, DispatcherPriority.Normal,this.Timer_Tick ,this.Dispatcher);
this.timer.IsEnabled = false;
}
private void Timer_Tick(object sender, EventArgs e)
{
this.SetValue(TextProperty, DateTime.Now.ToString("yyyy/MM/dd hh:mm:ss"));
}
}
And now you have a control that can use it in the designer.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp1"
x:Class="WpfApp1.MainWindow"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StatusBar Grid.Row="1">
<local:WPFTimer IsRunning="True"/>
</StatusBar>
</Grid>
</Window>
You can do it this way, using wpf animation and binding, and no background code :)
<Window x:Class="WpfApp6.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
mc:Ignorable="d"
Title="MainWindow"
Height="450"
Width="800">
<Grid>
<Grid.Resources>
<!--Set x: share to get the latest every time-->
<system:DateTime x:Key="DateTime"
x:Shared="False" />
<Storyboard x:Key="Storyboard">
<!--Use keyframe animation to update datetime -->
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="DataContext"
Duration="0:0:1"
RepeatBehavior="Forever"
AutoReverse="False">
<DiscreteObjectKeyFrame KeyTime="50%"
Value="{StaticResource DateTime}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</Grid.Resources>
<!--Get datetime from DataContext-->
<TextBlock Text="{Binding RelativeSource={RelativeSource Self},Path=DataContext.Now}"
DataContext="{StaticResource DateTime}">
<TextBlock.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard Storyboard="{StaticResource Storyboard}" />
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
</Grid>
</Window>
or like this,a real clock
<Window x:Class="WpfApp6.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
mc:Ignorable="d"
Title="MainWindow"
Height="450"
Width="800">
<Window.Resources>
<FrameworkElement x:Key="time" Tag="{x:Static system:DateTime.Now}" />
<TransformGroup x:Key="transformHour">
<TranslateTransform X="{Binding Source={StaticResource time},Path=Tag.Hour}"
Y="{Binding Source={StaticResource time},Path=Tag.Minute}" />
<MatrixTransform Matrix="30 0 0.5 0 0 0" />
</TransformGroup>
<TransformGroup x:Key="transformMinute">
<TranslateTransform X="{Binding Source={StaticResource time},Path=Tag.Minute}"
Y="{Binding Source={StaticResource time},Path=Tag.Second}" />
<MatrixTransform Matrix="6 0 0.1 0 0 0" />
</TransformGroup>
<TransformGroup x:Key="transformSecond">
<TranslateTransform X="{Binding Source={StaticResource time},Path=Tag.Second}" />
<MatrixTransform Matrix="6 0 0 0 0 0" />
</TransformGroup>
<Style TargetType="{x:Type Path}">
<Setter Property="Stroke"
Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}" />
<Setter Property="StrokeThickness" Value="3" />
<Setter Property="StrokeDashCap" Value="Triangle" />
</Style>
</Window.Resources>
<Viewbox>
<Canvas Width="200" Height="200">
<Canvas.RenderTransform>
<TranslateTransform X="100" Y="100" />
</Canvas.RenderTransform>
<Path Data="M 0 -90 A 90 90 0 1 1 -0.01 -90"
StrokeDashArray="0 3.14157" />
<Path Data="M 0 -90 A 90 90 0 1 1 -0.01 -90"
StrokeDashArray="0 7.854"
StrokeThickness="6" />
<Border Background="LightBlue" Width="10" Height="80" RenderTransformOrigin="0.5 0">
<Border.RenderTransform>
<TransformGroup>
<RotateTransform x:Name="bor_Second"
Angle="{Binding Source={StaticResource transformSecond},Path=Value.OffsetX}" />
<RotateTransform Angle="180" />
</TransformGroup>
</Border.RenderTransform>
</Border>
<Border Background="LightGreen" Width="10" Height="60" RenderTransformOrigin="0.5 0">
<Border.RenderTransform>
<TransformGroup>
<RotateTransform x:Name="bor_Minute"
Angle="{Binding Source={StaticResource transformMinute},Path=Value.OffsetX}" />
<RotateTransform Angle="180" />
</TransformGroup>
</Border.RenderTransform>
</Border>
<Border Background="LightGray" Width="10" Height="40" RenderTransformOrigin="0.5 0">
<Border.RenderTransform>
<TransformGroup>
<RotateTransform x:Name="bor_Hour"
Angle="{Binding Source={StaticResource transformHour},Path=Value.OffsetX}" />
<RotateTransform Angle="180" />
</TransformGroup>
</Border.RenderTransform>
</Border>
</Canvas>
</Viewbox>
<Window.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="bor_Hour"
Storyboard.TargetProperty="Angle"
IsAdditive="True"
Duration="12:0:0"
From="0" To="360"
RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetName="bor_Minute"
Storyboard.TargetProperty="Angle"
IsAdditive="True"
Duration="1:0:0"
From="0" To="360"
RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetName="bor_Second"
Storyboard.TargetProperty="Angle"
IsAdditive="True"
Duration="0:1:0"
From="0"
To="360"
RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Window.Triggers>
</Window>
Create a clock using only xaml code in wpf
I'm developing a UWP app and trying to apply a style per control in code that would set the Grid.Column property, effectively to move the elements around the grid. However, my elements stay where they are, even though the animations run. Any setter besides Grid.* works, and I can see the styles being applied both through my code and through the live property window.
I know there are other ways to accomplish what I'm going for and I'll probably do one of those, but I want to know what's going on here. I'll post a sample of my page and the command that applies the styling:
MainPage.xaml
<Page
x:Class="UWPApp.Scorekeeper.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UWPApp.Scorekeeper"
xmlns:viewmodels="using:UWPApp.Scorekeeper.Models.ViewModels"
xmlns:converters="using:UWPApp.Scorekeeper.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mo="using:UWPApp.Scorekeeper.Models"
mc:Ignorable="d"
x:Name="MainPageElement"
d:DesignHeight="600"
d:DesignWidth="1024">
<Page.Resources>
<DataTemplate x:Key="GridHorizontalTemplate" x:DataType="local:MainPage">
<Grid MinWidth="768" MinHeight="500" Background="{ThemeResource SystemControlBackgroundAccentBrush}" ManipulationMode="All">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="69*"/>
<ColumnDefinition Width="76*"/>
<ColumnDefinition Width="76*"/>
<ColumnDefinition Width="69*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="73*"/>
<RowDefinition Height="135*"/>
<RowDefinition Height="69*"/>
<RowDefinition Height="43*"/>
</Grid.RowDefinitions>
<Button Foreground="{ThemeResource SystemControlForegroundChromeMediumBrush}" Command="{Binding ElementName=MainPageElement, Path=EnterGoalCommand}" CommandParameter="{Binding ElementName=MainPageElement,Path=StateModel.HomeID}" x:Name="HomeGoalBtn" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="1" Grid.Column="1" Margin="0,0.4,0,0" d:LayoutOverrides="LeftMargin, RightMargin" >
<Button.Resources>
<Style x:Key="FlippedStyle" TargetType="Button">
<Setter Property="Grid.Column" Value="2"/>
</Style>
<Storyboard x:Key="ToFlipAnimation">
<RepositionThemeAnimation FromHorizontalOffset="-487"/>
</Storyboard>
<Storyboard x:Key="FromFlipAnimation">
<RepositionThemeAnimation FromHorizontalOffset="487"/>
</Storyboard>
</Button.Resources>
<Viewbox Margin="40" Stretch="Uniform">
<TextBlock Text="Goal" FontSize="48" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Bottom" />
</Viewbox>
</Button>
<Button Foreground="{ThemeResource SystemControlForegroundChromeMediumBrush}" Command="{Binding ElementName=MainPageElement, Path=EnterGoalCommand}" CommandParameter="{Binding ElementName=MainPageElement,Path=StateModel.AwayID}" x:Name="AwayGoalBtn" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="1" Grid.Column="2" Margin="0,0.4,-0.2,0" d:LayoutOverrides="LeftMargin, RightMargin" >
<Button.Resources>
<Style x:Key="FlippedStyle" TargetType="Button">
<Setter Property="Grid.Column" Value="1"/>
</Style>
<Storyboard x:Key="ToFlipAnimation">
<RepositionThemeAnimation FromHorizontalOffset="487"/>
</Storyboard>
<Storyboard x:Key="FromFlipAnimation">
<RepositionThemeAnimation FromHorizontalOffset="-487"/>
</Storyboard>
</Button.Resources>
<Viewbox Margin="40" Stretch="Uniform">
<TextBlock Text="Goal" FontSize="48" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Bottom" />
</Viewbox>
</Button>
</Grid>
</DataTemplate>
</Page.Resources>
<ContentPresenter x:Name="MainContent" Margin="0,0,0,0" HorizontalAlignment="Stretch" d:LayoutOverrides="Width, Height" VerticalAlignment="Stretch">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="1000"></AdaptiveTrigger>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="MainContent.ContentTemplate" Value="{StaticResource GridHorizontalTemplate}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</ContentPresenter>
</Page>
MainPage.xaml.cs
public ICommand FlipScreen => new CommandHandler(() =>
{
if (!flipped)
{
flipped = true;
foreach (var item in this.FindChildren<FrameworkElement>())
{
object styleObject;
if (item.Resources.TryGetValue("FlippedStyle", out styleObject))
{
item.Style = (Style)styleObject;
}
object animationObject;
if (item.Resources.TryGetValue("ToFlipAnimation", out animationObject))
{
Storyboard board = animationObject as Storyboard;
if (board.GetCurrentState() != ClockState.Stopped)
{
board.Stop();
}
foreach (var subitem in board.Children)
{
Storyboard.SetTarget(subitem, item);
}
board.Begin();
}
}
}
else
{
flipped = false;
foreach (var item in this.FindChildren<FrameworkElement>())
{
item.Style = null;
object animationObject;
if (item.Resources.TryGetValue("FromFlipAnimation", out animationObject))
{
Storyboard board = animationObject as Storyboard;
if (board.GetCurrentState() != ClockState.Stopped)
{
board.Stop();
}
foreach (var subitem in board.Children)
{
Storyboard.SetTarget(subitem, item);
}
board.Begin();
}
}
}
});
FindChildren:
public static List<T> FindChildren<T>(this FrameworkElement element) where T : FrameworkElement
{
int childrenCount = VisualTreeHelper.GetChildrenCount(element);
var children = new FrameworkElement[childrenCount];
List<T> list = new List<T>();
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(element, i) as FrameworkElement;
children[i] = child;
if (child is T)
list.Add(child as T);
}
for (int i = 0; i < childrenCount; i++)
if (children[i] != null)
{
var subChild = FindChildren<T>(children[i]);
if (subChild != null)
list.AddRange(subChild);
}
return list;
}
Not sure if a default Button Style in the Resources of a Button would actually apply to that Button.
Better set the Style explicitly:
<Button ... >
<Button.Style>
<Style TargetType="Button">
<Setter Property="Grid.Column" Value="2"/>
</Style>
</Button.Style>
<Button.Resources>
...
</Button.Resources>
<Viewbox ...>
...
</Viewbox>
</Button>
I have created a custom water mark text box which is extended from text box. control template for the same is shown below.
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:WaterMarkTextBox}">
<ControlTemplate.Resources>
<Storyboard x:Key="Storyboard1">
<ThicknessAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Margin)"
Storyboard.TargetName="PART_FieldTextBlock">
<SplineThicknessKeyFrame KeyTime="0:0:0.15"
Value="0,0,10,0" />
</ThicknessAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="Storyboard2">
<ThicknessAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Margin)"
Storyboard.TargetName="PART_FieldTextBlock">
<SplineThicknessKeyFrame KeyTime="0:0:0.25"
Value="0,0,-500,0" />
</ThicknessAnimationUsingKeyFrames>
</Storyboard>
</ControlTemplate.Resources>
<Grid x:Name="PART_GridControl"
ClipToBounds="True"
Height="{TemplateBinding Height}"
Width="{TemplateBinding Width}">
<TextBlock x:Name="PART_PlaceHolderTextBlock"
Style="{StaticResource SWMLightTextBlockStyle}"
Foreground="#BDBBBB"
FontSize="{StaticResource SmallFontSize}"
Text="{TemplateBinding PlaceHolderText}"
VerticalAlignment="Center"
Margin="20,0,10,0" />
<Border Name="border"
CornerRadius="0"
Padding="2"
BorderThickness="1"
BorderBrush="DeepSkyBlue">
<ScrollViewer x:Name="PART_ContentHost"/>
</Border>
<TextBlock x:Name="PART_FieldTextBlock"
HorizontalAlignment="Right"
Foreground="#BDBBBB"
Margin="0,0,-500,0"
Style="{StaticResource SWMLightTextBlockStyle}"
FontSize="{StaticResource SmallFontSize}"
TextWrapping="Wrap"
Text="{TemplateBinding FieldText}"
VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
but while typing the textbox enters to a condition where it is having no cursor but we can type into it occurs with a probability of 1/2 chars.I wonder how it happens. Anyone is having idea how it is happening?
Two things You can do. First One is Overriding SystemParameters Properties Using reflection Like this
void LocallyDisableMouseVanish()
{
foreach (var field in typeof(SystemParameters).GetFields(BindingFlags.NonPublic|BindingFlags.Static))
if (field.Name.Contains("mouseVanish"))
field.SetValue(null, false);
}
Call this method On Focus Of your Control.
If this is Not working means You can Try something Like this Override the style of the Caret Like this
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:WaterMarkTextBox}">
<ControlTemplate.Resources>
<Storyboard x:Key="Storyboard1">
<ThicknessAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Margin)"
Storyboard.TargetName="PART_FieldTextBlock">
<SplineThicknessKeyFrame KeyTime="0:0:0.15"
Value="0,0,10,0" />
</ThicknessAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="Storyboard2">
<ThicknessAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Margin)"
Storyboard.TargetName="PART_FieldTextBlock">
<SplineThicknessKeyFrame KeyTime="0:0:0.25"
Value="0,0,-500,0" />
</ThicknessAnimationUsingKeyFrames>
</Storyboard>
</ControlTemplate.Resources>
<Grid x:Name="PART_GridControl"
ClipToBounds="True"
Height="{TemplateBinding Height}"
Width="{TemplateBinding Width}">
<TextBlock x:Name="PART_PlaceHolderTextBlock"
Style="{StaticResource SWMLightTextBlockStyle}"
Foreground="#BDBBBB"
FontSize="{StaticResource SmallFontSize}"
Text="{TemplateBinding PlaceHolderText}"
VerticalAlignment="Center"
Margin="20,0,10,0" />
<Border Name="border"
CornerRadius="0"
Padding="2"
BorderThickness="1"
BorderBrush="DeepSkyBlue">
<ScrollViewer x:Name="PART_ContentHost" />
</Border>
<TextBlock x:Name="PART_FieldTextBlock"
HorizontalAlignment="Right"
Foreground="#BDBBBB"
Margin="0,0,-500,0"
Style="{StaticResource SWMLightTextBlockStyle}"
FontSize="{StaticResource SmallFontSize}"
TextWrapping="Wrap"
Text="{TemplateBinding FieldText}"
VerticalAlignment="Center" />
<Canvas>
<Border x:Name="PART_Caret"
Visibility="Collapsed"
Canvas.Left="0"
Canvas.Top="0"
Width="5"
Height="25"
Background="Black"
BorderThickness="1">
<Border.Triggers>
<EventTrigger RoutedEvent="Border.Loaded">
<BeginStoryboard>
<Storyboard x:Name="CaretStoryBoard"
RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames Storyboard.TargetProperty="Background.Color"
Duration="0:0:0:1"
FillBehavior="HoldEnd">
<ColorAnimationUsingKeyFrames.KeyFrames>
<DiscreteColorKeyFrame KeyTime="0:0:0.750"
Value="Transparent" />
<DiscreteColorKeyFrame KeyTime="0:0:0.000"
Value="Black" />
</ColorAnimationUsingKeyFrames.KeyFrames>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Border.Triggers>
</Border>
</Canvas>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="CaretBrush "
Value="Transparent" />
And in your control code Add this
public override void OnApplyTemplate()
{
this.border = this.GetTemplateChild("PART_Caret") as Border;
base.OnApplyTemplate();
}
And add this Method
private void MoveCustomCaret()
{
var caretLocation = this.GetRectFromCharacterIndex(this.CaretIndex).Location;
if (!double.IsInfinity(caretLocation.X))
{
Canvas.SetLeft(border, caretLocation.X);
}
if (!double.IsInfinity(caretLocation.Y))
{
Canvas.SetTop(border, caretLocation.Y);
}
}
And Finally Call This method and set the visibility of the border
private void SWMTextBox_GotFocus(object sender, RoutedEventArgs e)
{
MoveCustomCaret();
border.Visibility = Visibility.Visible;
}
also in TextChangedEvent
private void CustomTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
MoveCustomCaret();
}
And Hid the Visibility in Lost_Focus_Event
private void SWMTextBox_LostFocus(object sender, RoutedEventArgs e)
{
border.Visibility = Visibility.Hidden;
}
Basically I tried to make a marquee with 2 Labels that when one goes invisible, the second one starts it's animation so it goes smooth from left to right and the text is always visible. My problem is that it does work at start but when I retry that code (second time at run time so the old animation stops, it refresh the text and start the animation again) the second label freezes when it was and it's left property of it's canvas doesn't change (Canvas.SetLeft(L_Content_Second, -L_Content_Second.ActualWidth))
C#
it's a function called StartAnimation(); It's fired when content of L_Content and L_Content_Second changes
DoubleKeyFrameCollection collection = new DoubleKeyFrameCollection();
collection.Add(
new LinearDoubleKeyFrame(
-L_Content.ActualWidth,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0)))
);
if (L_Content.ActualWidth > this.ActualWidth)
{
collection.Add(
new LinearDoubleKeyFrame(
this.ActualWidth - (L_Content.ActualWidth / 2),
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(15)))
);
collection.Add(
new LinearDoubleKeyFrame(
this.ActualWidth * 2,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(30)))
);
}
else
{
collection.Add(
new LinearDoubleKeyFrame(
this.ActualWidth - L_Content.ActualWidth,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(15)))
);
collection.Add(
new LinearDoubleKeyFrame(
this.ActualWidth + (this.ActualWidth - L_Content.ActualWidth),
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(30)))
);
}
animK.KeyFrames = collection;
animK.Duration = TimeSpan.FromSeconds(30);
animK2.KeyFrames = collection;
animK2.BeginTime = TimeSpan.FromSeconds(15);
animK2.Duration = TimeSpan.FromSeconds(30);
animK.AutoReverse = false;
animK2.AutoReverse = false;
animK.FillBehavior = FillBehavior.Stop;
animK.RepeatBehavior = RepeatBehavior.Forever;
animK2.FillBehavior = FillBehavior.Stop;
animK2.RepeatBehavior = RepeatBehavior.Forever;
Canvas.SetLeft(L_Content_Second, -L_Content_Second.ActualWidth); //This works at start, then it doesn't any more
L_Content_Second.Visibility = System.Windows.Visibility.Visible;
L_Content.BeginAnimation(Canvas.LeftProperty, animK, HandoffBehavior.SnapshotAndReplace);
L_Content_Second.BeginAnimation(Canvas.LeftProperty, animK2, HandoffBehavior.SnapshotAndReplace);
XAML
<UserControl x:Name="userControl" x:Class="Supreme.Components.ScrollLabel"
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" Height="18" Width="300" MouseDoubleClick="UserControl_MouseDoubleClick_1" Background="#00000000">
<Canvas x:Name="Container">
<Label x:Name="L_Content" Content="" HorizontalAlignment="Left" Padding="0" Height="18" Foreground="{Binding Foreground, ElementName=userControl}" VerticalAlignment="Top"/>
<Label x:Name="L_Content_Second" Content="" HorizontalAlignment="Left" Padding="0" Height="18" Foreground="{Binding Foreground, ElementName=userControl}" SizeChanged="L_Content_SizeChanged" VerticalAlignment="Top"/>
</Canvas>
</UserControl>
I tried to attempt the same via XAML
below is the sample for same, run it and see if this does what you are looking for.
<Canvas x:Name="Container">
<Canvas.Resources>
<l:PositionConverter xmlns:l="clr-namespace:CSharpWPF"
x:Key="PositionConverter" />
<Storyboard x:Key="marquee">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="L_Content"
RepeatBehavior="Forever"
Storyboard.TargetProperty="(Canvas.Left)"
Duration="0:0:10">
<SplineDoubleKeyFrame KeyTime="0:0:0"
Value="{Binding ActualWidth, ElementName=L_Content, Converter={StaticResource PositionConverter}}" />
<SplineDoubleKeyFrame KeyTime="0:0:5"
Value="{Binding ActualWidth,ElementName=Container}" />
<SplineDoubleKeyFrame KeyTime="0:0:10"
Value="{Binding ActualWidth,ElementName=Container}" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="L_Content_Second"
RepeatBehavior="Forever"
Storyboard.TargetProperty="(Canvas.Left)"
Duration="0:0:10">
<SplineDoubleKeyFrame KeyTime="0:0:0"
Value="{Binding ActualWidth, ElementName=L_Content_Second, Converter={StaticResource PositionConverter}}" />
<SplineDoubleKeyFrame KeyTime="0:0:5"
Value="{Binding ActualWidth, ElementName=L_Content_Second, Converter={StaticResource PositionConverter}}" />
<SplineDoubleKeyFrame KeyTime="0:0:10"
Value="{Binding ActualWidth,ElementName=Container}" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Canvas.Resources>
<TextBox x:Name="L_Content"
Foreground="Red"
Text="first" />
<TextBox x:Name="L_Content_Second"
Foreground="Green"
Text="second">
</TextBox>
<Button Content="Start" Canvas.Top="30">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard Storyboard="{StaticResource marquee}" />
</EventTrigger>
</Button.Triggers>
</Button>
<Canvas.Triggers>
<EventTrigger SourceName="L_Content"
RoutedEvent="SizeChanged">
<BeginStoryboard Storyboard="{StaticResource marquee}" />
</EventTrigger>
<EventTrigger SourceName="L_Content_Second"
RoutedEvent="SizeChanged">
<BeginStoryboard Storyboard="{StaticResource marquee}" />
</EventTrigger>
<EventTrigger RoutedEvent="SizeChanged">
<BeginStoryboard Storyboard="{StaticResource marquee}" />
</EventTrigger>
</Canvas.Triggers>
</Canvas>
I've used textbox in this example for easy value change, also for this I have created a converter
namespace CSharpWPF
{
public class PositionConverter:IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return -System.Convert.ToDouble(value);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
I'm trying to make an animated splash screen for my app. I have my MainPage where I show a Popup which has an animation and a textblock. I'd like to change the text of my textblock to show the status of the loading, but I can't change it. Any ideas?
Mainpage code
namespace animatedsplash
{
public partial class MainPage : PhoneApplicationPage
{
BackgroundWorker preloader;
Popup splashPop;
public MainPage()
{
InitializeComponent();
splashPop = new Popup(){IsOpen = true, Child = new Splash() };
preloader = new BackgroundWorker();
RunPreloader();
}
private void RunPreloader()
{
preloader.DoWork += ((s, args) => {
Thread.Sleep(10000);
});
preloader.RunWorkerCompleted += ((s,args) =>
{
this.Dispatcher.BeginInvoke(()=> { this.splashPop.IsOpen = false; });
});
preloader.RunWorkerAsync();
}
}
}
Splash xaml
<phone:PhoneApplicationPage
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" xmlns:ec="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions" xmlns:eim="clr-namespace:Microsoft.Expression.Interactivity.Media;assembly=Microsoft.Expression.Interactions"
xmlns:local="clr-namespace:animatedsplash"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="800"
x:Class="animatedsplash.Splash"
Orientation="Portrait"
shell:SystemTray.IsVisible="False">
<phone:PhoneApplicationPage.Resources>
<Storyboard x:Name="load">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.Rotation)" Storyboard.TargetName="image">
<EasingDoubleKeyFrame KeyTime="0" Value="-180"/>
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:2" Value="180"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</phone:PhoneApplicationPage.Resources>
<phone:PhoneApplicationPage.FontFamily>
<StaticResource ResourceKey="PhoneFontFamilyNormal"/>
</phone:PhoneApplicationPage.FontFamily>
<phone:PhoneApplicationPage.FontSize>
<StaticResource ResourceKey="PhoneFontSizeNormal"/>
</phone:PhoneApplicationPage.FontSize>
<phone:PhoneApplicationPage.Foreground>
<StaticResource ResourceKey="PhoneForegroundBrush"/>
</phone:PhoneApplicationPage.Foreground>
<i:Interaction.Triggers>
<eim:StoryboardCompletedTrigger Storyboard="{StaticResource load}">
<eim:ControlStoryboardAction Storyboard="{StaticResource load}"/>
</eim:StoryboardCompletedTrigger>
<i:EventTrigger>
<eim:ControlStoryboardAction Storyboard="{StaticResource load}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<!--TitlePanel contains the name of the application and page title--><!--ContentPanel - place additional content here-->
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Image Margin="0" Grid.Row="1" Source="/media/splashbg.jpg" Stretch="Fill" d:IsLocked="True"/>
<Image x:Name="rotator" Margin="96,288,184,312" Grid.Row="1" Source="media/splashpin.png" Stretch="Fill" d:IsLocked="True"/>
<Image x:Name="image" Margin="142,305,0,370" Grid.Row="1" Source="media/pinload.png" Stretch="Fill" HorizontalAlignment="Left" Width="75" RenderTransformOrigin="0.838,0.504">
<Image.RenderTransform>
<CompositeTransform/>
</Image.RenderTransform>
</Image>
<TextBlock x:Name="preloader_percentage" Margin="178,354,0,0" Grid.Row="1" TextWrapping="Wrap" Text="100" VerticalAlignment="Top" RenderTransformOrigin="0.5,0.593" TextAlignment="Right" HorizontalAlignment="Left" Width="35" FontFamily="Segoe WP Semibold" Height="27"/>
<TextBlock Margin="213,354,0,0" Grid.Row="1" TextWrapping="Wrap" Text="%" VerticalAlignment="Top" HorizontalAlignment="Left" Width="16" FontFamily="Segoe WP Semibold" d:IsLocked="True"/>
</Grid>
</phone:PhoneApplicationPage>
How about something like this:
In Splash.xaml.cs you would have the following:
public string Progress
{
get { return preloader_percentage.Text; }
set { preloader_percentage.Text = value; }
}
And in MainWindow.xaml.cs you change your code to look something like this:
preloader.WorkerReportsProgress = true;
preloader.ProgressChanged += (sender, e) =>
{
this.Dispatcher.BeginInvoke(() =>
(this.splashPop.Child as Splash).Progress = e.ProgressPercentage.ToString());
};
Then in your worker method you need to call preloader.ReportProgress() method several times. As far as I know, that should do it.
Note: There are a lot of design landmines here. I'd suggest getting this code to work, and you can refactor later to make it cleaner and easier to maintain.