This is my XAML that works perfectly :
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:local="clr-namespace:WpfApplication1"
x:Class="WpfApplication1.MainWindow"
x:Name="Window"
xmlns:converter="clr-namespace:WpfApplication1.Image"
Title="MainWindow" Height="350" Width="525"
Loaded="MainWindow_OnLoaded">
<Window.Resources>
<Storyboard x:Key="Move" RepeatBehavior="Forever">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(local:FloatingTextbox.Position)" Storyboard.TargetName="FloatingTextbox">
<EasingDoubleKeyFrame KeyTime="0" />
<EasingDoubleKeyFrame KeyTime="0:0:6" Value="300"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<converter:InitialPositionConverter x:Key="InitialPositionConverter"/>
</Window.Resources>
<Grid Background="Black">
<Canvas Height="100" Width="411.111" Margin="45.833,80.556,0,0" Background="White">
<local:FloatingTextbox x:Name="FloatingTextbox" Height="37.222"
TextWrapping="Wrap" Text="FloatingTextbox" Width="100"
BorderThickness="1" Position="1" Canvas.Top="26.389">
<Canvas.Left>
<MultiBinding Converter="{StaticResource InitialPositionConverter}" Mode="OneWay">
<Binding Path="Position" RelativeSource="{RelativeSource Self}"/>
<Binding Path="ActualWidth" RelativeSource="{RelativeSource Self}"/>
</MultiBinding>
</Canvas.Left>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<ei:ControlStoryboardAction Storyboard="{StaticResource Move}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</local:FloatingTextbox>
</Canvas>
</Grid>
</Window>
I want to create the storyboard and realize the animation in Code, I tried this:
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
var storyboard = new Storyboard();
storyboard.RepeatBehavior = new RepeatBehavior(4);
var myDoubleAnimationUsingKeyFrames = new DoubleAnimationUsingKeyFrames();
var myEasingDoubleKeyFrame1 = new EasingDoubleKeyFrame();
myEasingDoubleKeyFrame1.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0));
var myEasingDoubleKeyFrame2 = new EasingDoubleKeyFrame();
myEasingDoubleKeyFrame2.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(6));
myEasingDoubleKeyFrame2.Value = this.ActualWidth;
myDoubleAnimationUsingKeyFrames.KeyFrames.Add(myEasingDoubleKeyFrame1);
myDoubleAnimationUsingKeyFrames.KeyFrames.Add(myEasingDoubleKeyFrame2);
Storyboard.SetTargetProperty(myDoubleAnimationUsingKeyFrames, new PropertyPath(FloatingTextbox.PositionProperty));
Storyboard.SetTargetName(myDoubleAnimationUsingKeyFrames, FloatingTextbox.Name);
storyboard.Children.Add(myDoubleAnimationUsingKeyFrames);
var eventTrigger = new System.Windows.Interactivity.EventTrigger() { EventName = "Loaded" };
var triggers = Interaction.GetTriggers(FloatingTextbox);
triggers.Add(eventTrigger);
var controlStoryboardAction = new ControlStoryboardAction();
controlStoryboardAction.Storyboard = storyboard;
controlStoryboardAction.ControlStoryboardOption = ControlStoryboardOption.Play;
eventTrigger.Actions.Add(controlStoryboardAction);
}
Note : The reason I want to realize this in code behind, is to put the window's ActualWidth instead of a 300 here :
<EasingDoubleKeyFrame KeyTime="0:0:6" Value="300"/>
I can't bind since, it's freezable, so ... that's why.
My C# Code doesn't work, what should I be missing ??
Launching the storyboard is all what lacked :
to be put in the end.
storyboard.Begin();
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
Basically I have this animation where when you click the button(s), it toggles the grid either revealing or hiding the 2nd column (which contains Button2) ... My problem is that when you spam-click the button, the animation gets queued so it needs to finish the first animation before doing the second one. The only workaround I have is to disable the button while the animation is active then re-enabling them once the animation is completed. But I'm trying to find a way where the active animation gets interrupted somehow and instead uses the current width of the grid to start the 2nd animation.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
_toggle = false;
GridBodyWidthOne = new GridLength(1, GridUnitType.Star);
GridBodyWidthZero = new GridLength(0, GridUnitType.Star);
GridBody0Width = GridBodyWidthOne;
GridBody1Width = GridBodyWidthZero;
storyboard = this.FindResource("expandBody0") as Storyboard;
storyboard.FillBehavior = FillBehavior.Stop;
storyboard.Completed += (object o, EventArgs ea) => {
GridBody0.Width = GridBodyWidthOne;
GridBody1.Width = GridBodyWidthZero;
GridBody0.BeginAnimation(ColumnDefinition.WidthProperty, null);
GridBody1.BeginAnimation(ColumnDefinition.WidthProperty, null);
GridSplitter0.IsEnabled = false;};
storyboard = this.FindResource("retractBody0") as Storyboard;
storyboard.FillBehavior = FillBehavior.Stop;
storyboard.Completed += (object o, EventArgs ea) => {
GridBody0.Width = GridBodyWidthOne;
GridBody1.Width = GridBodyWidthOne;
GridBody0.BeginAnimation(ColumnDefinition.WidthProperty, null);
GridBody1.BeginAnimation(ColumnDefinition.WidthProperty, null);
GridSplitter0.IsEnabled = true;};
}
private void Button_Click(object sender, RoutedEventArgs e)
{
ToggleMainBody1();
}
private void ToggleMainBody1()
{
double maxWidth = GridBody0.ActualWidth + GridBody1.ActualWidth;
double width0 = GridBody0.ActualWidth / maxWidth;
double width1 = GridBody1.ActualWidth / maxWidth;
GridBody0Width = new GridLength(width0, GridUnitType.Star);
GridBody1Width = new GridLength(width1, GridUnitType.Star);
if (!_toggle)
RevealMainBody1();
else
HideMainBody1();
_toggle = !_toggle;
}
private void HideMainBody1()
{
//storyboard = this.FindResource("retractBody0") as Storyboard;
//storyboard.Stop(this);
storyboard = this.FindResource("expandBody0") as Storyboard;
storyboard.Begin(this);
}
private void RevealMainBody1()
{
//storyboard = this.FindResource("expandBody0") as Storyboard;
//storyboard.Stop(this);
storyboard = this.FindResource("retractBody0") as Storyboard;
storyboard.Begin(this);
}
as for why I have FillBehavior set to 'Stop', I have a problem with the GridSplitter if it's set to 'HoldEnd'. 'HoldEnd' actually works well but not when I adjust it via the GridSplitter. So basically, the problem occurs when you double-click the toggle button (or trigerring the toggle twice without having the first animation finish).
<Window x:Class="ColumnAdjustmentV2.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:local="clr-namespace:ColumnAdjustmentV2"
xmlns:g="clr-namespace:ColumnAdjustmentV2"
mc:Ignorable="d"
Title="MainWindow" Height="650" Width="800" Loaded="Window_Loaded" SizeChanged="Window_SizeChanged" StateChanged="Window_StateChanged" >
<Window.Resources>
<Storyboard x:Key="expandBody0">
<!--<g:GridLengthAnimation BeginTime="0:0:0" Duration="0:0:0.5" Storyboard.TargetName="GridBody0" Storyboard.TargetProperty="Width" From="{Binding Path=GridBody0Width}" To="{Binding Path=GridBodyWidthOne}" />-->
<g:GridLengthAnimation BeginTime="0:0:0" Duration="0:0:0.5" Storyboard.TargetName="GridBody1" Storyboard.TargetProperty="Width" From="{Binding Path=GridBody1Width}" To="{Binding Path=GridBodyWidthZero}" />
</Storyboard>
<Storyboard x:Key="retractBody0">
<!--<g:GridLengthAnimation BeginTime="0:0:0" Duration="0:0:0.5" Storyboard.TargetName="GridBody0" Storyboard.TargetProperty="Width" From="{Binding Path=GridBody0Width}" To="{Binding Path=GridBodyWidthOne}" />-->
<g:GridLengthAnimation BeginTime="0:0:0" Duration="0:0:0.5" Storyboard.TargetName="GridBody1" Storyboard.TargetProperty="Width" From="{Binding Path=GridBody1Width}" To="{Binding Path=GridBodyWidthOne}" />
</Storyboard>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="500" />
<RowDefinition Height="100" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="GridBody0" Width="{Binding Path=GridBody0Width, Mode=TwoWay}" />
<ColumnDefinition x:Name="GridBodyDivider" Width="Auto" />
<ColumnDefinition x:Name="GridBody1" Width="{Binding Path=GridBody1Width, Mode=TwoWay}" />
</Grid.ColumnDefinitions>
<Button x:Name="Button1" Grid.Column="0" Content="Button1" Click="Button_Click" />
<GridSplitter x:Name="GridSplitter0" Grid.Column="1" Background="#FFCC0099" HorizontalAlignment="Center" Margin="0" Width="4" VerticalAlignment="Stretch" LayoutUpdated="GridSplitter_LayoutUpdated" MouseDoubleClick="GridSplitter_MouseDoubleClick" PreviewKeyDown="GridSplitter_PreviewKeyDown" PreviewMouseUp="GridSplitter_PreviewMouseUp" />
<Button x:Name="Button2" Grid.Column="2" Content="Button2" Click="Button_Click" />
</Grid>
<Grid Grid.Row="1">
<Button x:Name="Button3" Content=""/>
</Grid>
</Grid>
https://www.youtube.com/watch?v=2iE8cZC6EFQ
I have prepared a sample project that I think may be helpful for you: https://github.com/Drreamer/AnimationReverse
In this example I resize a button from min to max width using animation. To start animation or to run it in reverse direction simply click the button.
<Grid x:Name="rootGrid">
<Button x:Name="button" MinWidth="40" Width="40" Content="Expand"
local:MainWindow.AnimationDuration="0:0:2" HorizontalAlignment="Left" Click="button_Click" >
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="button"
Storyboard.TargetProperty="Width"
From="{Binding ActualWidth, ElementName=button}"
Duration="{Binding Path=(local:MainWindow.AnimationDuration), ElementName=button}">
<DoubleAnimation.To>
<MultiBinding Converter="{StaticResource ToWidthConverter}">
<Binding ElementName="button" Path="Tag" />
<Binding ElementName="button" Path="MinWidth" />
<Binding ElementName="rootGrid" Path="ActualWidth" />
</MultiBinding>
</DoubleAnimation.To>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>
</Grid>
Here I use binding to dynamically set the To and Duration properties. I store information about current animation direction in the Tag property. The Duration is calculated dynamically from the Button.Click event based on the current element width. The goal here is to make sure that animation is always aplied with the same speed.
private void button_Click(object sender, RoutedEventArgs e) {
double currentPosition = (button.ActualWidth - button.MinWidth) / (rootGrid.ActualWidth - button.MinWidth);
if (button.Tag is bool && (bool)button.Tag)
button.Tag = false;
else {
currentPosition = 1 - currentPosition;
button.Tag = true;
}
SetAnimationDuration(button, new Duration(TimeSpan.FromSeconds(5 * currentPosition)));
}
I'm trying to find a way to initiate the close of an active DialogHost from code, but have been unable to find the right syntax. I think the main challenge is accessing the DialogHost from a class outside of the MainWindow code behind.
The entirety of my XAML:
<Window x:Class="MaterialDesignTest.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:local="clr-namespace:MaterialDesignTest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
TextElement.Foreground="{DynamicResource MaterialDesignBody}"
TextElement.FontWeight="Regular"
TextElement.FontSize="13"
TextOptions.TextFormattingMode="Ideal"
TextOptions.TextRenderingMode="Auto"
Background="{DynamicResource MaterialDesignPaper}"
FontFamily="{DynamicResource MaterialDesignFont}">
<materialDesign:DialogHost Identifier="RootDialog" Loaded="DialogHost_Loaded">
<Grid>
<StackPanel>
<Button Width="100" x:Name="SearchRestore" Margin="0 150 0 0" Command="{x:Static materialDesign:DialogHost.OpenDialogCommand}" materialDesign:DialogHost.DialogClosingAttached="SearchRestore_OnDialogClosing"
Content="Restore" Click="SearchRestore_Click">
<Button.CommandParameter>
<StackPanel Margin="10">
<TextBlock Text="Restoring..."/>
<Button Name="CircleButton" Margin="0 50 0 0" Style="{StaticResource MaterialDesignFloatingActionButton}"
Command="{Binding SaveComand}" IsHitTestVisible="False"
materialDesign:ButtonProgressAssist.IsIndicatorVisible="{Binding IsSaving}"
materialDesign:ButtonProgressAssist.Value="{Binding SaveProgressButton}">
<materialDesign:PackIcon Height="24" Width="24" Foreground="White">
<materialDesign:PackIcon.Style>
<Style TargetType="materialDesign:PackIcon" BasedOn="{StaticResource {x:Type materialDesign:PackIcon}}">
<Setter Property="Kind" Value="CloudSync" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsSaveComplete}" Value="True">
<Setter Property="Kind" Value="Check" />
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="0:0:0.8" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</materialDesign:PackIcon.Style>
</materialDesign:PackIcon>
</Button>
</StackPanel>
</Button.CommandParameter>
</Button>
</StackPanel>
</Grid>
</materialDesign:DialogHost>
And the code from which I need to initiate the close (this is in a view model, not the MainWindow code behind):
private async void progressTimerTick(object sender, EventArgs e)
{
if (progress < 100 && cycles < 2)
{
if (progress == 99)
{
cycles++;
progress = 0;
}
IsSaveComplete = false;
IsSaving = true;
progress++;
SaveProgressButton = progress;
}
else
{
IsSaveComplete = true;
IsSaving = false;
progressTimer.Enabled = false;
SaveProgressButton = 0;
await PutTaskDelay();
//Close dialog here
}
}
There are 3 ways to close a dialog in MDIX:
var dialogResult = await DialogHost.Show(myDialogControl, (sender, args) =>
{
args.Session.Close(false);
});
or
DialogHost.CloseDialogCommand.Execute(null,null);
or
DialogHostInstance.IsOpen = false;
Regarding #Talha answer.
I have tried your solution, however, it didn't work. On the other hand, I found a way to fix it:
var drawer = DrawerHost.CloseDrawerCommand;
drawer.Execute(null, null);
It looks the same, but worked for me.
I want to expand a stack panel from a fixed height to the actual height:
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True" Storyboard.TargetProperty="(FrameworkElement.MaxHeight)" Storyboard.TargetName="stpAbout">
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="{Binding ActualHeight, ElementName=stpAbout, Mode=OneWay}">
<EasingDoubleKeyFrame.EasingFunction>
<CubicEase EasingMode="EaseOut" />
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
The stack panel:
<StackPanel x:Name="stpAbout" MaxHeight="200">
The code to run the storyboard:
var dia = new MessageDialog(stpAbout.ActualHeight.ToString());
await dia.ShowAsync(); // Shows an actual height of 312
var sbExpand = (Storyboard)Resources["stbExpandAbout"]; // Get the storyboard
sbExpand.Begin(); // Play the storyboard
dia = new MessageDialog(stpAbout.ActualHeight.ToString());
await dia.ShowAsync(); // Shows still an actual heigh of 312
But the stack panel doesn't expand. It collapses and has now no height at all.
before the animation
after the animation
Sample
Page:
<Page
x:Class="Juqe.Client.UWP.Pages.BlankPage1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Juqe.Client.UWP.Pages"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<Storyboard x:Name="stbExpand">
<DoubleAnimationUsingKeyFrames EnableDependentAnimation="True" Storyboard.TargetProperty="(FrameworkElement.MaxHeight)" Storyboard.TargetName="stpToExpand">
<EasingDoubleKeyFrame KeyTime="0:0:0.3" Value="{Binding ActualHeight, ElementName=stpToExpand, Mode=OneWay}">
<EasingDoubleKeyFrame.EasingFunction>
<CubicEase EasingMode="EaseOut" />
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<StackPanel x:Name="stpToExpand" MaxHeight="96">
<Grid Height="48" Background="Red" />
<Grid Height="48" Background="Green" />
<Grid Height="48" Background="Blue" />
<Grid Height="48" Background="Yellow" />
</StackPanel>
<Button Click="Button_Click">Expand</Button>
</StackPanel>
</Grid>
Code:
private async void Button_Click(object sender, RoutedEventArgs e)
{
var dia = new MessageDialog(stpToExpand.ActualHeight.ToString());
await dia.ShowAsync();
var sbExpand = (Storyboard)Resources["stbExpand"]; // Get the storyboard
sbExpand.Begin(); // Play the expand storyboard
sbExpand.Completed += async (se, ev) =>
{
dia = new MessageDialog(stpToExpand.ActualHeight.ToString());
await dia.ShowAsync();
};
}
"Solution"
I manipulated the storyboard in code after getting it:
((storyboard.Children[0] as DoubleAnimationUsingKeyFrames).KeyFrames[0] as EasingDoubleKeyFrame).Value = stpAbout.ActualHeight; // Correct the storyboard
Binding won't work here as like MSDN says:
Note Although it has an ActualHeightProperty backing field, ActualHeight does not raise property change notifications and it should be thought of as a regular CLR property and not a dependency property.
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.