I'm developing a Windows Phone 8.1 app with a MapControl as the main focal point of my app. I basically want to integrate a similar experience design wise as the Nokia Here Maps App.
The bottom black Frame can be pulled upwards to reveal its content.
How am I able to do this?
EDIT
I now have:
ExtraInfo.RenderTransform = new TranslateTransform();
ExtraInfo.ManipulationDelta += OnManipulationDelta;
ExtraInfo.ManipulationMode = Windows.UI.Xaml.Input.ManipulationModes.TranslateY;
in my constructor
The eventhandler
private void OnManipulationDelta(object sender, Windows.UI.Xaml.Input.ManipulationDeltaRoutedEventArgs e)
{
try
{
Storyboard myStoryboard = (Storyboard)this.Resources["TestStoryboard"];
TranslateTransform myTranslate = new TranslateTransform();
myTranslate.Y = e.Delta.Translation.Y;
ExtraInfo.RenderTransform = myTranslate;
Storyboard.SetTarget(myStoryboard.Children[0] as DoubleAnimation, ExtraInfo);
myStoryboard.Begin();
}
catch (Exception)
{
Debug.WriteLine("Animation called");
}
}
My XAML
<Grid>
<StackPanel>
<Maps:MapControl Height="{Binding ActualHeight, ElementName=pageRoot}" x:Name="Map" LandmarksVisible="False" ZoomLevel="{Binding zoomlevel, Mode=TwoWay, FallbackValue=8}" MapServiceToken="#######" TrafficFlowVisible="True">
<Image x:Name="NewCheckImage" Visibility="Collapsed" Maps:MapControl.Location="{Binding Center, ElementName=Map}" Maps:MapControl.NormalizedAnchorPoint=".5,.5"></Image>
</Maps:MapControl>
<StackPanel x:Name="ExtraInfo" Height="{Binding ActualHeight, ElementName=pageRoot}" Background="Black" Margin="0,-250,0,0" Canvas.ZIndex="1">
<StackPanel.RenderTransform>
<TranslateTransform X="0" Y="0">
</TranslateTransform>
</StackPanel.RenderTransform>
</StackPanel>
</StackPanel>
</Grid>
The storyboard
<Page.Resources>
<Storyboard x:Key="TestStoryboard">
<DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.Y)"
To="0">
</DoubleAnimation>
</Storyboard>
</Page.Resources>
But the panel does some jumpy unpredictable moves.
Set RenderTransform = new TranslateTransform() for your StackPanel, handle Page ManipulationDelta event and there change your (StackPanel.RenderTransform as TranslateTransform).Y value. Use Storyboard (animation on RenderTransform) to achieve effect of pulling up/down on touch release.
You should do something like this: (pseudocode)
void OnStackPanelLoaded(..)
{
StackPanel.VerticalAlingment = VerticalAlingment.Bottom;
StackPanel.RenderTransform = new TranslateTransform(0, StackPanel.ActualHeight);
// translate stackpanel out of page, be sure stackpanel has bottom alingment of grid
}
void OnManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs args)
{
var stackPanelTransform = MyStackPanel.RenderTransform as TranslateTransform;
stackPanelTransform.Y += args.Delta.Translation.Y;
}
You should also register to ManipulationCompleted event and there run close/open animation.
void OnManipulationCompleted(object sender, ManipulationCompletedEventArgs args)
{
// you can also add additional logic here to reverse open/close logic
// for example: if user touch translation delta is too small then instead of opening panel - close it like in Here
if (IsPanelOpened())
Resources["ClosePanelStoryboard"].BeginStoryboard();
else
Resources["OpenPanelStoryboard"].BeginStoryboard();
}
bool IsPanelOpened()
{
var translateTransform = myStackPanel.RenderTransform as TranslateTransform;
return translateTransform.Y > double.Epsilon;
}
Check DoubleAnimation.EasingFunction - to achieve same animation effect like in Here Drive (animated open/close).
Implementing good panel takes time. Initially it should have Opacity = 0, IsHitTest = false (because it can blink on page start - before RenderTransform is set) - you should also register to SizeChanged event in StackPanel to ensure that TranslateTransform is set correctly (sometimes you can get StackPanel.ActualHeight value of 0 - most likely in release build because they're faster).
Related
I have this code. This is just an example. I want to do it with code. How to animate the Foreground property of multiple "Run" elements in a TextBlock?
<Page
x:Class="AnimationTest.MainPage"
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:local="using:AnimationTest"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<Grid>
<TextBlock
x:Name="_textBlockElement"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="72"><Run x:Name="_run1" Text="Hel" /><Run Text="lo" />
<TextBlock.Triggers>
<EventTrigger>
<BeginStoryboard>
<Storyboard x:Name="ColorStoryboard">
<ColorAnimation
AutoReverse="True"
RepeatBehavior="Forever"
Storyboard.TargetName="_textBlockElement"
Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)"
To="Red"
Duration="0:0:2" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
</Grid>
</Page>
Oke, so this took quite some sorting out, but turned out to be quite doable.
The key is to create two storyboards in code behind with the correct animations and then add those storyboards to the resources of any parent of the Run's.
Let's start of with the XAML code, which is pretty simple:
<Grid>
<TextBlock x:Name="TestBlock"
HorizontalAlignment="Center" VerticalAlignment="Center"
PointerEntered="TestBlock_PointerEntered"
PointerExited="TestBlock_PointerExited">
<Run x:Name="Run1" Text="Test1" Foreground="Blue"/>
<Run x:Name="Run2" Text="Test2" Foreground="Green"/>
<!-- ... -->
</TextBlock>
</Grid>
For simplicity I have already defined the names and foregrounds of the Run's.
Now we need to define the storyboards and animations in code behind.
I've chosen to do this in the constructor (after InitializeComponent()!). In theory you should be able to also paste this code in the Page_Loaded event.
public MainPage()
{
InitializeComponent();
SetupStoryBoards();
}
void SetupStoryBoards()
{
// Define duration and storyboards to red and original color
var duration = new Duration(TimeSpan.FromSeconds(1));
var toRedStory = new Storyboard { Duration = duration };
// completed events can be subscribed to, to register when animation is done
//toRedStory.Completed += Story_Completed;
var toOriginalStory = new Storyboard { Duration = duration };
//toOriginalStory.Completed += ToOriginalStory_Completed;
foreach (Run r in TestBlock.Inlines)
{
// Filter out any inlines that are not a named Run
if (string.IsNullOrEmpty(r.Name))
continue;
// Define the animations
var toRedAnim = new ColorAnimation
{
Duration = duration,
To = Colors.Red,
EnableDependentAnimation = true
};
var toOriginalAnim = new ColorAnimation
{
Duration = duration,
To = (r.Foreground as SolidColorBrush).Color, // Causes animation to go back to original foreground color of Run
EnableDependentAnimation = true
};
// Add animations to the storyboards and associate animations with the Run
toRedStory.Children.Add(toRedAnim);
toOriginalStory.Children.Add(toOriginalAnim);
Storyboard.SetTargetName(toRedAnim, r.Name);
Storyboard.SetTargetName(toOriginalAnim, r.Name);
Storyboard.SetTargetProperty(toRedAnim, "(Run.Foreground).(SolidColorBrush.Color)");
Storyboard.SetTargetProperty(toOriginalAnim, "(Run.Foreground).(SolidColorBrush.Color)");
}
// Add the storyboards to the resources of any parent of the Run's for easy retrieval later and to make the animations find the Run's
// I choose the resources of the textblock that contains the Run's
TestBlock.Resources.Add("toRedStory", toRedStory);
TestBlock.Resources.Add("toOriginalStory", toOriginalStory);
}
Now to execute the animations, we add the PointerEntered and PointerExited eventhandlers, and begin the correct storyboards there:
private void TextBlock_PointerEntered(object sender, PointerRoutedEventArgs e)
{
var story = TestBlock.Resources["toRedStory"] as Storyboard;
story.Begin();
}
private void TextBlock_PointerExited(object sender, PointerRoutedEventArgs e)
{
var story = TestBlock.Resources["toOriginalStory"] as Storyboard;
story.Begin();
}
You should be able to extend this wherever needed, however I have found that EnableDependentAnimation must be set to true since otherwise it won't work.
I want my content inside my GridView to scroll when I start my storyboard. Right now the storyboard targets the gridview and not the content. How do I make it to scroll inside the gridview?
This is how my Storyboards and DataTemplates look like:
<Page.Resources
<Storyboard x:Key="CarouselStoryboard">
<DoubleAnimation
Storyboard.TargetName="CarouselTransform"
Storyboard.TargetProperty="X"/>
</Storyboard>
<DataTemplate x:Key="CatTemplate">
<Grid>
<StackPanel Margin="30,0,30,0" Background="Red">
</StackPanel>
</Grid>
</DataTemplate>
<DataTemplate x:Key="DogTemplate">
<Grid>
<StackPanel Margin="30,0,30,0" Background="Green">
</StackPanel>
</Grid>
</DataTemplate>
</Page.Resources>
And this is how my Griview looks like:
<GridView x:Name="myGridView" ItemTemplateSelector="{StaticResource MyDataTemplateSelector}"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
ScrollViewer.HorizontalScrollMode="Auto"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollMode="Disabled">
<GridView.RenderTransform>
<TranslateTransform x:Name="CarouselTransform" />
</GridView.RenderTransform>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid Orientation="Vertical"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
<Button Click="Left_Click" Content="Left" Background="Blue" HorizontalAlignment="Left"Width="405"/>
And this is how I start my storyboard using some C#
private int currentElement = 0;
private void Left_Click(object sender, RoutedEventArgs e)
{
if (currentElement < 100)
{
currentElement++;
AnimateCarousel();
}
}
private void AnimateCarousel()
{
Storyboard storyboard = (this.Resources["CarouselStoryboard"] as Storyboard);
DoubleAnimation animation = storyboard.Children.First() as DoubleAnimation;
animation.To = -200 * currentElement;
storyboard.Begin();
}
First of all, directly animate attach propery will not work due to we are lack of a reasonable built-in attached property:
What is scrollview in control template:
It's typical for a ScrollViewer control to exist as a composite part of other controls. A ScrollViewer part, along with the ScrollContentPresenter class for support, will display a viewport along with scrollbars only when the host control's layout space is being constrained smaller than the expanded content size. This is often the case for lists, so ListView and GridView templates always include a ScrollViewer.
To influence some of the behavior and properties that are from within the ScrollViewer part, ScrollViewer defines a number of XAML attached properties:
ScrollViewer.BringIntoViewOnFocusChange
ScrollViewer.HorizontalScrollBarVisibility
ScrollViewer.HorizontalScrollMode
ScrollViewer.IsDeferredScrollingEnabled
ScrollViewer.IsHorizontalRailEnabled
ScrollViewer.IsHorizontalScrollChainingEnabled
ScrollViewer.IsScrollInertiaEnabled
ScrollViewer.IsVerticalRailEnabled
ScrollViewer.IsVerticalScrollChainingEnabled
ScrollViewer.IsZoomChainingEnabled
ScrollViewer.IsZoomInertiaEnabled
ScrollViewer.VerticalScrollBarVisibility
ScrollViewer.VerticalScrollMode
ScrollViewer.ZoomMode
Animating XAML attached properties:We can only animate built-in attached properties.
So instead of the above idea we need to get the ScrollView embeded in GridView first:
Create your own GridView
public class MyGridView:GridView
{
private ScrollViewer myscrollviewer;
public ScrollViewer MyScrollViewer
{
get { return myscrollviewer; }
set { myscrollviewer = value; }
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
ScrollViewer testscrollviewer = GetTemplateChild("ScrollViewer") as ScrollViewer;
myscrollviewer = testscrollviewer;
}
}
Use it in your XAML:
<local:MyGridView x:Name="mygv".../>
Here since VerticalOffset is readonly, I cannot use DoubleAnimation to animate the scrollview. Thanks to Justin he has shared this workaround. With the help of ScrollView.ChangeView we can have some animation.
And a more simple way if you don't need the animation:
mygv.ScrollIntoView(mygv.Items[10], ScrollIntoViewAlignment.Leading);
I want a similar behavior like what happens when we tap on a TextBox. The keyboard pops up pushing the TextBox upwards. When I tap a button, the hidden Grid (PushContainer) should pop up pushing the content up. I've tried the following code with no luck. Currently, list view goes all the way down to the bottom underneath the Grid. It just pops up on top of the list view.
<Grid x:Name="MainGrid" Background="#FF97C5C5">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListView x:Name="lstView" ItemTemplate="{StaticResource StatusTemplate}" Background="#FFC91010">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
<Grid x:Name="PushContainer" Visibility="Collapsed" Grid.RowSpan="1" Grid.Row="1" Height="300"/>
</Grid>
Any help would be appreciated.
To slide a control up like the soft keyboard does animate a RenderTransform to shift it to the top. Just changing the Visibility on an object and forcing a new layout will pop rather than slide.
Here's a quick sample. You could also define the storyboard, etc. in Xaml. For demonstration purposes it's controlled by an AppBarToggleButton and will create a CompositeTransform on an element which doesn't have one.
For more information see Storyboarded animations on MSDN.
private void AppBarToggleButton_Checked(object sender, RoutedEventArgs e)
{
// Almost (but not quite) to the top of the page
SlideVertical(MainGrid,-1 * ActualHeight + 100);
}
private void AppBarToggleButton_Unchecked(object sender, RoutedEventArgs e)
{
SlideVertical(MainGrid,0);
}
private void SlideVertical(UIElement target, double endPos)
{
Storyboard sb = new Storyboard();
DoubleAnimation da = new DoubleAnimation();
// Over how long to perform the animation.
Duration dur = TimeSpan.FromMilliseconds(100);
da.Duration = dur;
sb.Duration = dur;
// From current location to endPos
var ct = target.RenderTransform as CompositeTransform;
if (ct == null)
{
// Give up if we had some other type of transform
if (target.RenderTransform != null)
return;
// That way we don't step on a non-CompositeTransform
// RenderTransform if one already exists.
// That would be bad.
ct = new CompositeTransform();
target.RenderTransform = ct;
}
double startPos = ct.TranslateY;
da.From = startPos;
da.To = endPos;
sb.Children.Add(da);
// Choose the element to slide
Storyboard.SetTarget(da, target);
// Animate the target's CompositeTransform.TranslateY
Storyboard.SetTargetProperty(da, "(UIElement.RenderTransform).(CompositeTransform.TranslateY)");
sb.Begin();
}
I have a Windows Store-style WPF application, and I just added search to it. When I click the Search button in the app bar, I set my FlyoutPresenter containing the SearchBox to Visible. This button is placed in the lower right-hand corner. It works good on computers with keyboards, but I ran into a problem when the virtual keyboard, or InputPane, opens. First, the keyboard covered the box. I solved that problem by checking and adjusting the margin of the box when the box is in focus, but when I scroll the page to the very top and bottom, the control starts moving on the page. Here is my minimal code:
XAML:
<Grid Background="White" x:Name="MainGrid">
<!-- App Bar with Search button -->
<AppBar x:Name="BAppBar" VerticalAlignment="Bottom">
<CommandBar>
<CommandBar.PrimaryCommands>
<AppBarButton Icon="Find" Label="Search" Click="Search_Click"/>
</CommandBar.PrimaryCommands>
</CommandBar>
</AppBar>
<!-- Search button and Close button -->
<FlyoutPresenter VerticalAlignment="Top" Name="SearchPop" Visibility="Collapsed">
<StackPanel Orientation="Horizontal">
<SearchBox Name="Search" GotFocus="Search_Focus" LostFocus="Search_Focus"/>
<AppBarButton Name="SearchClose" Icon="Cancel" Click="Search_Close" />
</StackPanel>
</FlyoutPresenter>
</Grid>
C#:
public partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
// Close app bar, show search box, and set margin to bottom of page
private void Search_Click(object sender, RoutedEventArgs e)
{
BAppBar.IsOpen = false;
SearchPop.Visibility = Windows.UI.Xaml.Visibility.Visible;
SearchPop.Margin = new Thickness(0, MainGrid.ActualHeight - SearchPop.ActualHeight, 0, 0);
}
// Set margin for opening/closing virtual keyboard
private void Search_Focus(object sender, RoutedEventArgs e)
{
Windows.UI.ViewManagement.InputPane.GetForCurrentView().Showing += (s, args) =>
{
double flyoutOffset = (int)args.OccludedRect.Height - SearchPop.ActualHeight;
SearchPop.Margin = new Thickness(0, flyoutOffset, 0, 0);
};
Windows.UI.ViewManagement.InputPane.GetForCurrentView().Hiding += (s, args) =>
{
SearchPop.Margin = new Thickness(0, MainGrid.ActualHeight - SearchPop.ActualHeight, 0, 0);
};
}
// Close search
private void Search_Close(object sender, RoutedEventArgs e)
{
SearchPop.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
}
What I need is for the box to not be affected by the user scrolling in the screen. In HTML, this is called Fixed Positioning. I have read that it is not natively possible in XAML, but that there are workarounds. I have read these MSDN and SO links, but they didn't really help:
http://social.msdn.microsoft.com/Forums/en-US/9779328a-a7cd-447d-a4ac-bcc952083f43/fixed-positioning-in-wpf?forum=wpf
http://social.msdn.microsoft.com/Forums/windowsapps/en-US/7349d01d-dc0e-4e1c-9327-df90e00fbebf/how-to-handle-the-appearance-of-the-onscreen-keyboard?forum=winappswithcsharp
Popup control moves with parent
You can simulate the fixed behavior in XAML in a very simple way:
<Grid Background="White" x:Name="MainGrid">
<ContentControl VerticalAligment="Stretch" HorizontalAligment="Stretch">
<!--All other visual controls, the float item will be located over all controls located here, even scrolls viewers-->
</ContentControl>
<!-- Float item -->
<SomeControl>
<!--The control you want be over in the fixed position,
you can set the layout to it, and locate it where you want
just set the Vertical/Horizontal Aligment, margin, height, width-->
</SomeControl>
</Grid>
(Sorry if code sample has some sintax errors, I had write it in the fly)
Also wpf has some controls that are displayed on a layer over all other, this elements are context menus, tooltips and adorners, you also could try them.
I hope this ideas helps.
I have a Canvas with a TextBlock like so:
<Canvas x:Name="ContentPanel" Grid.Row="1" DoubleTapped="ContentPanel_DoubleTapped">
<TextBlock x:Name="WordBlock" FontSize="226.667" FontFamily="Segoe UI Semilight" TextAlignment="Center"
RenderTransformOrigin="0.5, 0.5">
<TextBlock.RenderTransform>
<TranslateTransform x:Name="translate"/>
</TextBlock.RenderTransform>
</TextBlock>
</Canvas>
My app is such that when the user navigates to this page, the TextBlock will be centered in the Canvas and if the TextBlock's width is greater than that of the canvas, the marquee animation will occur:
private void SetAnimation()
{
Canvas.SetLeft(WordBlock, (ContentPanel.ActualWidth - WordBlock.ActualWidth) / 2);
Canvas.SetTop(WordBlock, (ContentPanel.ActualHeight - WordBlock.ActualHeight) / 2);
if (WordBlock.ActualWidth > ContentPanel.ActualWidth)
{
MarqueeAnimation.From = WordBlock.ActualWidth;
MarqueeAnimation.To = -WordBlock.ActualWidth;
MarqueeAnimation.Duration = new Duration(new TimeSpan(0, 0, 10));
MarqueeBoard.Begin();
}
}
This method is called OnNavigatedTo. I can't figure out why the TextBlock won't center because the ActualHeight and ActualWidth properties are always coming back as 0.0. I don't want to put fixed sizes because this is a Windows Store app and would like for it to be scalable across different screen sizes.
Any ideas? I'm stuck.
When OnNavigatedTo is called I don't believe the page has actually been drawn yet. I had similar confusion when trying to resize and rearrange things. The answer appeared to be to wait until the page has loaded and do the calculation then:
public ChallengePage()
{
this.InitializeComponent();
this.Loaded += ChallengePage_Loaded;
}
void ChallengePage_Loaded(object sender, RoutedEventArgs e)
{
*Do your calculations which use ActualWidth and ActualHeight in here*
}
You'll need something like that I believe. Adding the Loaded handler into your initialize for the page, and then you can call SetAnimation from in there.