I've the following issue:
When I change orientation of my c#+xaml page from landscape to portrait or vice versa there is a half a second or so in which the user can see big blue parts on the screen, before the layout gets recalculated and re-rendered. These spots seem like leftovers from the previous orientation state.
I am looking for a way to hide or smoothen this extremely ragged and bumpy transition.
I tried adding an orientation change handler with a ProgressRing to cover it for 1 second, but it did not help - the handler executes after the blue spots.
Here is the code of my animation StoryBoard
<!-- The entire page respects the narrower 100-pixel margin convention for portrait -->
<VisualState x:Name="FullScreenPortrait">
<Storyboard>
<!-- Change the back button and the logo -->
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="backButton" Storyboard.TargetProperty="Style">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource PortraitBackButtonStyle}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="headerLogoImage" Storyboard.TargetProperty="Width">
<DiscreteObjectKeyFrame KeyTime="0" Value="430"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="headerLogoImage" Storyboard.TargetProperty="Height">
<DiscreteObjectKeyFrame KeyTime="0" Value="49"/>
</ObjectAnimationUsingKeyFrames>
<!--Change section title and navButtons to be in two rows, by moving navButtons to the second row, first column-->
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="navButtons" Storyboard.TargetProperty="(Grid.Row)">
<DiscreteObjectKeyFrame KeyTime="0" Value="1"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="navButtons" Storyboard.TargetProperty="(Grid.Column)">
<DiscreteObjectKeyFrame KeyTime="0" Value="0"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="navButtons" Storyboard.TargetProperty="(Grid.ColumnSpan)">
<DiscreteObjectKeyFrame KeyTime="0" Value="2"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="navButtons" Storyboard.TargetProperty="Margin">
<DiscreteObjectKeyFrame KeyTime="0" Value="0,15,0,0"/>
</ObjectAnimationUsingKeyFrames>
<!--Change the grid-->
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemGridView" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="itemPortraitGridView" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
Any help is appreciated, thanks!
EDIT:
I solved this by using the following method (and the help of my colleague):
/// <summary>
/// On Orientation change collapse all views, and make visible only the views for the particular new orientation
/// Also change the font size for portrait mode
/// </summary>
/// <param name="sender"></param>
private async void OnOrientationChanged(object sender)
{
headerGrid.Visibility = Visibility.Collapsed;
itemGridView.Visibility = Visibility.Collapsed;
itemPortraitGridView.Visibility = Visibility.Collapsed;
itemListView.Visibility = Visibility.Collapsed;
//Make the loading spinner temporarily visible and stop it in the StoryBoard animation for every orientation separately
LoadingView.Visibility = Visibility.Visible;
//Change the font size
if (ScreenHelper.IsInPortraitMode())
{
_viewModel.FontSizeMethod = _viewModel.GetPortraitFontSize;
}
else
{
//change font size method back
_viewModel.FontSizeMethod = _viewModel.GetLandscapeFontSize;
}
// Change visibility back to normal in case the xaml approach failed.
await Task.Delay(1000);
if (ScreenHelper.IsInPortraitMode())
itemPortraitGridView.Visibility = Windows.UI.Xaml.Visibility.Visible;
else if (ApplicationView.Value == ApplicationViewState.Snapped)
itemListView.Visibility = Windows.UI.Xaml.Visibility.Visible;
else
itemGridView.Visibility = Windows.UI.Xaml.Visibility.Visible;
headerGrid.Visibility = Visibility.Visible;
LoadingView.Visibility = Visibility.Collapsed;
}
I know you marked this complete but since I cant post a comment I have to ask here. Could you have just used easing to solve this?
Related
I had an issue changing the foreground color for selected items on Datagrids.
I found a fix that works, but it is expensive when using Datagrids with a lot of items/data and it also complicates the design tree a bit more.
This is my Coding:
...<VisualState x:Name="NormalSelected">
<Storyboard>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="BackgroundRectangle"/>
<ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.0010000" Storyboard.TargetName="contentControl" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="00:00:00" Value="Yellow"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>...
...
...<ContentControl x:Name="contentControl" Foreground="Green">
<sdk:DataGridCellsPresenter x:Name="CellsPresenter" Grid.Column="1" sdk:DataGridFrozenGrid.IsFrozen="True"/>
</ContentControl>...
This is the only way I could think of doing it in XAML. I don't know if there are any other ways of doing it in xaml rather than rapping the CellPresenter with a control everytime?
1.I cant set the visual state of my textbox, with my c# code the text shows up but the animation does not run, any ideas, or corrections on my c# code would be helpful for me to understand
below is my xaml:
<ControlTemplate x:Name="instructions_text2" TargetType="TextBox">
<Grid>
<TextBlock TextWrapping="Wrap" TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" Text="Press Start and drag!" Foreground="#FFCB1717" FontSize="30" FontFamily="AR DELANEY" />
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CustomGroups">
<VisualState x:Name="Blue">
<Storyboard x:Name="Storyboard1" RepeatBehavior="Forever">
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" >
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Collapsed</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:0.2">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" >
<EasingDoubleKeyFrame KeyTime="0" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.2" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:2" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
c# code:
TextBox instructions = new TextBox();
instructions.Template = Resources["instructions_text2"] as ControlTemplate;
instructions.Width = playArea.ActualWidth;
instructions.Height = playArea.ActualHeight;
VisualStateManager.GoToState(instructions, "Blue", true);
playArea.Children.Add(instructions);
You are trying to change the visual state of the TextBox before it's ready. In fact you are trying to change the state before it's been added to the visual tree.
Change your code to be:
TextBox instructions = new TextBox();
instructions.Template = Resources["instructions_text2"] as ControlTemplate;
instructions.Width = playArea.ActualWidth;
instructions.Height = playArea.ActualHeight;
instructions.Loaded += Instructions_Loaded;
playArea.Children.Add(instructions);
Then in the Loaded handler you can go to the state you want.
private void Instructions_Loaded(object sender, RoutedEventArgs e)
{
var result = VisualStateManager.GoToState(sender as FrameworkElement, "Blue", true);
}
NOTE: There are two versions of VisualStateManager. The one I used is in the namespace System.Windows and it's GoToState takes a FrameworkElement as the first argument - this is the one used by traditional desktop applications. There's also a VisualStateManager in Windows.UI.Xaml that takes a Control as the first argument - this is the one used by new Windows App programs.
As TextBox is a type of control:
private void Instructions_Loaded(object sender, RoutedEventArgs e)
{
var result = VisualStateManager.GoToState(sender as Control, "Blue", true);
}
should work.
Also the ControlTemplate needs a Key, not a Name *:
<ControlTemplate x:Key="instructions_text2" TargetType="TextBox">
The resources dictionary uses the Key value as it's key, so at the moment you're not finding the resource at all so instructions.Template is null.
* this may only be true for desktop applications
I'm writing a custom renderer for a submit button in order to provide a uniform style with my Xamarin.Forms application. I'm having trouble setting the foreground (text) color for a disabled button on the Windows (UWP) side of things.
changing the color for an active button is as simple as
Control.Foreground = new SolidColorBrush(Windows.UI.Colors.Green);
but attempting to figure out how to set the foreground color for a disabled button has led me down a rabbit-hole.
I would like to be able to set this without having to use XAML (like this approach) because I plan to extract these renderers later.
The best would be if you could edit the style of your button, or define it somewhere in resources and then apply it to your button, even from code.
There is other way, theoretically simpler and available from code. If you take a look at button's style, you will see the section for disabled visual state:
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="RootGrid">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlBackgroundBaseLowBrush}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledBaseMediumLowBrush}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="ContentPresenter">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledTransparentBrush}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
You see the names of brush resources which are used it that state. It should be enough to change them in code to make all disabled buttons look like you want. Though you need to remember that this will also change behavior when user changes theme and your app is suspended.
private void Button_Click(object sender, RoutedEventArgs e)
{
App.Current.Resources["SystemControlBackgroundBaseLowBrush"] = new SolidColorBrush(Colors.Yellow); // background
App.Current.Resources["SystemControlDisabledBaseMediumLowBrush"] = new SolidColorBrush(Colors.Red); // content
App.Current.Resources["SystemControlDisabledTransparentBrush"] = new SolidColorBrush(Colors.Green); // border
}
my task seemed to me as something easy (WinRT).
All I want is to change color of Rectangle depending on state.
I know how to do it in a "cool" animated way.
The thing is that I just want my color to be changed immediately without any animation.
This is a standard, cool way:
<VisualState x:Name="UnFocused">
<Storyboard Duration="1">
<ColorAnimation To="{ThemeResource LightGrayColor}"
Storyboard.TargetName="borderBrush"
Storyboard.TargetProperty="Color"/>
</Storyboard>
</VisualState>
I thought that if I change Duration to zero then the change will be instant.
It does not work this way, color did not change at all.
So I tried "0:0:0.1" but it did not change color either.
So...
What is the current approach to change color in instant using VisualState functionality?
Thank you :-)
I don't know whether WinRT has some restrictions. At least I would also have expected that zero works fine. But you have some more options and still being cool:
<ObjectAnimationUsingKeyFrames Duration="00:00:00"
Storyboard.TargetName="borderBrush"
Storyboard.TargetProperty="Color">
<DiscreteObjectKeyFrame KeyTime="00:00:00">
<DiscreteObjectKeyFrame.Value>
<SolidColorBrush Color="{ThemeResource LightGrayColor}"/>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
or
<ColorAnimationUsingKeyFrames Storyboard.TargetName="borderBrush"
Storyboard.TargetProperty="Color">
<EasingColorKeyFrame KeyTime="0"
Value="{ThemeResource LightGrayColor}" />
</ColorAnimationUsingKeyFrames>
Turning #Jacek-Wojcik's comment into an answer:
Don't set a duration on the storyboard, set it on the ColorAnimation instead:
<VisualState x:Name="UnFocused">
<Storyboard >
<ColorAnimation Duration="0" To="{ThemeResource LightGrayColor}"
Storyboard.TargetName="borderBrush"
Storyboard.TargetProperty="Color"/>
</Storyboard>
</VisualState>
I am playing with a Metro Style Application at the moment (.NET 4.5 RC [C#\XAML], VS2012 RC) and I have one problem which I am unable to solve.
I would like to achieve that my app will change the layout when hosting device is rotated (+-45). I have prepared two layouts in XAML - one for landscape and second one for portrait, and I have done some animation for Visual States:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ApplicationViewStates">
<VisualState x:Name="FullScreenLandscape"/>
<VisualState x:Name="Filled"/>
<VisualState x:Name="FullScreenPortrait">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ControlContentLandscape" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ControlContentPortrait" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Snapped"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
But I do not know what to do next. How to set my app that it will perform these animations when my device is turned into portrait mode?
I have analysed the Grid App template. And I have noticed that the main page there inherits from the LayoutAwarePage class which is present in the Common folder. The LayoutAwarePage class provide the handler for WindowSizeChanged event which is using the VisualStateManager to switch between states - this was the missing part in my current project. The solution is:
Manually handle the WindowsSizeChanged event and use the VisualStateManager class and GoToState() method
Create new project (Grid App) and fit into it the current project.