Binding Storyboard to ActualHeight doesn't work - c#

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.

Related

C#/WPF Handling active Storyboard Animations when spamming the 'Toggle'

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)));
}

Is there a way to use Storyboard with ListView/GridView items?

Here is my question:
I have a GridView in my UWP app. Each item in the GridView, keeps a button. Like following:
<GridView ItemsSource="{x:Bind ExampleItems, Mode=OneWay}" x:Name="mDataGridView">
<GridView.ItemTemplate>
<DataTemplate x:Name="DataTemplate" x:DataType="local:ItemTemplate">
<StackPanel Height="100" Width="100" Background="OrangeRed" x:Name="rootPanel">
<Grid Width="155" Height="210" Background="Red" x:Name="myGrid"/>
<Button x:Name="mOpenDetailButton" Click="mOpenDetailButton_Click" Margin="0, 30, 20,0"/>
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
I also have a pre-defined animation in a Storyboard like following
<Storyboard x:Name="OpenStoryboard">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="grid" x:Name="mOpenAnimation" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleY)">
<EasingDoubleKeyFrame KeyTime="00:00:00" Value="0.095"/>
<EasingDoubleKeyFrame KeyTime="00:00:00.1" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Name="CloseStoryboard">
<DoubleAnimation Storyboard.TargetName="grid" Duration="00:00:00.1" x:Name="mCloseAnimation" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleY)" To="0.095"/>
</Storyboard>
OK, here comes the question, is it possible to use the animations defined in the Storyboards with the GridView items? What I want is:
When user clicks on the mOpenDetailButton button, OpenStoryboard will be played, and the ScaleY value of grid changes with it.
Any help would be appreciated, thank you.
You can put the Storyboard in the StackPanel inside DataTemplate and define a RenderTransform on the your myGrid as the target object of StackPanel. In this case, it simplifies the specification of target object and more convenient to get Storyboard in code-behind and play it. For example:
.xaml:
<GridView ItemsSource="{x:Bind ExampleItems, Mode=OneWay}" x:Name="mDataGridView">
<GridView.ItemTemplate>
<DataTemplate x:Name="DataTemplate" x:DataType="local:ItemTemplate">
<StackPanel Height="100" Width="100" Background="OrangeRed" x:Name="rootPanel">
<StackPanel.Resources>
<Storyboard x:Name="OpenStoryboard">
<DoubleAnimationUsingKeyFrames
Duration="0:0:0.2"
Storyboard.TargetProperty="ScaleY"
Storyboard.TargetName="transform" >
<EasingDoubleKeyFrame KeyTime="00:00:00" Value="0.095"/>
<EasingDoubleKeyFrame KeyTime="00:00:00.1" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</StackPanel.Resources>
<Grid Width="100" Height="50" Background="Red" x:Name="myGrid">
<Grid.RenderTransform>
<CompositeTransform
x:Name="transform"
ScaleX="1" ScaleY="0" />
</Grid.RenderTransform>
</Grid>
<Button x:Name="mOpenDetailButton" Content="Mybutton" Click="mOpenDetailButton_Click" Margin="20"/>
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
.cs:
First, get its parent panle(StackPanel) via the button, then get its Storyboard animation and play it.
private void mOpenDetailButton_Click(object sender, RoutedEventArgs e)
{
UIElement mybutton = sender as UIElement;
StackPanel stackPanel = FindParent<StackPanel>(mybutton);
object storyvalue = null;
stackPanel?.Resources.TryGetValue("OpenStoryboard", out storyvalue);
Storyboard storyboard = value as Storyboard;
storyboard?.Begin();
}
public static T FindParent<T>(DependencyObject element)
where T : DependencyObject
{
while (element != null)
{
DependencyObject parent = VisualTreeHelper.GetParent(element);
T candidate = parent as T;
if (candidate != null)
{
return candidate;
}
element = parent;
}
return default(T);
}

Resizing window with menu

I've come across a problem with making menus within a re-sizeable window. I've made a test app to demonstrate my problem below:
I'm animating a settings menu that transitions down from the top by changing the grid margin. When the settings menu is pushed above the window by changing the margin, if I resize the window height I can see the hidden menu rather than seeing the main screen (green) extending.
How Can I change it so the main grid (green) extends rather than being able to see the hidden settings menu?
Code:
<Window x:Class="ResizeTest.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:ResizeTest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
MinWidth="800"
MinHeight="450"
MaxWidth="800"
MaxHeight="900">
<Window.Resources>
<Storyboard x:Key="ShowRightMenu">
<ThicknessAnimation Storyboard.TargetProperty="Margin"
Storyboard.TargetName="gridMenu"
From="0, -450, 0, 450"
To="0"
DecelerationRatio="0.9"
Duration="0:0:1" />
</Storyboard>
<Storyboard x:Key="HideRightMenu">
<ThicknessAnimation Storyboard.TargetProperty="Margin"
Storyboard.TargetName="gridMenu"
From="0"
To="0, -450, 0, 450"
DecelerationRatio="0.9"
Duration="0:0:1" />
</Storyboard>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Background="Green">
</Grid>
<Grid x:Name="gridMenu" Background="Blue"
Margin="0, -450, 0, 450"
Grid.Column="1">
<TextBlock Text="Well hello there!" Foreground="White" FontSize="60"
VerticalAlignment="Center" HorizontalAlignment="Center" />
</Grid>
<Button HorizontalAlignment="Left" Width="100" Height="50" Margin="10" Content="Clicky Me"
Click="Button_Click" />
</Grid>
</Window>
I would suggest that you change the To and From Properties of the ThicknessAnimation so that it is up-to-date with the height of the window. And additionaly that at the start of an animation you set the Visibility of the menu to Visible and at the end to Collapsed if the menu should not be visible at the moment.
For this I would suggest that you add a field that holds the openness state of the menu
private bool menuOpen = false;
And alter one of the stroyboards to this
<Storyboard x:Key="MenuStoryboard">
<ThicknessAnimation Storyboard.TargetProperty="Margin"
Storyboard.TargetName="gridMenu"
DecelerationRatio="0.9"
Duration="0:0:1" />
</Storyboard>
and remove the other.
You should also remove the margin of the Grid and add Collapsed as a default Visibility
<Grid x:Name="gridMenu" Background="Blue"
Visibility="Collapsed"
Grid.Column="1">
...
</Grid>
And then in the Click-Event-Handler you can adjust the Storyboard to the situation and run the animation.
private void Button_Click(object sender, RoutedEventArgs e)
{
var storyBoard = this.FindResource("ShowRightMenu") as Storyboard;
var animation = storyBoard.Children[0] as ThicknessAnimation;
if (menuOpen)
{
animation.To = new Thickness(0, -this.Height, 0, Height);
animation.From = new Thickness(0);
}
else
{
animation.From = new Thickness(0, -this.Height, 0, this.Height);
animation.To = new Thickness(0);
}
this.gridMenu.Visibility = Visibility.Visible;
storyBoard.Begin();
void animationCompleated(object sender2, EventArgs e2)
{
if (!menuOpen)
{
this.gridMenu.Visibility = Visibility.Collapsed;
}
storyBoard.Completed -= animationCompleated;
}
storyBoard.Completed += animationCompleated;
menuOpen = !menuOpen;
}

Creating Storyboard with DoubleAnimationUsingKeyFrames in Code Behind C#

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();

How do I change the Textblock value on my splash screen?

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.

Categories