Animate grid row height change in WP - windows-phone-8

I want to animate the grid row height change, but how to do that with dynamic "star" height values?
I have the following grid:
<Grid x:Name="baseGrid">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="3*"/>
</Grid.RowDefinitions>
<Grid x:Name="grid0" Grid.Row="0" Tap="grid0_Tap">
// Content
</Grid>
<Grid x:Name="grid1" Grid.Row="1" Tap="grid1_Tap">
// Content
</Grid>
<Grid x:Name="grid2" Grid.Row="2">
// Content
</Grid>
</Grid>
In code behind I have logic that if user clicks grid0, grid2 gets hidden. Again if user click grid1, grid2 gets its size back.
private void grid0_Tap(object sender, RoutedEventArgs e)
{
this.LayoutRoot.RowDefinitions[2].Height = new GridLength(0);
}
private void grid1_Tap(object sender, RoutedEventArgs e)
{
this.LayoutRoot.RowDefinitions[2].Height = new GridLength(3, GridUnitType.Star);
}
This works well right now, but how to animate this? I think Storyboard needs its from/to value and as Im using dynamic sizes I can not use Storyboard?

(EDITED to address the Windows Phone 8 animation storyboard question)
An animation class for GridLength doesn't exist in any XAML library.
But you can get close, or you can turn to third-party controls which do what you want.
And, for this use case, code-behind is pretty much required, instead of defining a XAML resource, since you're discovering the ActualHeight of the element at run-time:
private double storedHeight = 0.0;
private void grid0_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
//ListBox1.Visibility = Visibility.Visible;
if (storedHeight == 0.0) return;
var d = new DoubleAnimation();
d.From = 0.0;
d.To = storedHeight;
d.Duration = TimeSpan.FromMilliseconds(200);
storedHeight = 0.0;
var s = new Storyboard();
Storyboard.SetTarget(d, grid2);
Storyboard.SetTargetProperty(d, new PropertyPath("Height"));
s.Children.Add(d);
s.Begin();
}
private void grid1_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
//ListBox1.Visibility = Visibility.Hidden;
if (storedHeight > 0.0) return;
storedHeight = ListBox1.ActualHeight;
var d = new DoubleAnimation();
d.From = storedHeight;
d.To = 0.0;
d.Duration = TimeSpan.FromMilliseconds(200);
var s = new Storyboard();
Storyboard.SetTarget(d, grid2);
Storyboard.SetTargetProperty(d, new PropertyPath("Height"));
s.Children.Add(d);
s.Begin();
}

This doesn't seem like a sensible thing to want to do, and a grid animation like this is going to look awful because it's not on the compositor, but:
private void grid0_Tap(object sender, RoutedEventArgs e)
{
this.LayoutRoot.RowDefinitions[2].Height = new GridLength(0);
}
private void grid1_Tap(object sender, RoutedEventArgs e)
{
double fromHeight = this.LayoutRoot.RowDefinitions[2].ActualHeight;
this.LayoutRoot.RowDefinitions[2].Height = new GridLength(3, GridUnitType.Star);
this.LayoutRoot.Measure(new Size(this.Width, this.Height));
double toHeight = this.LayoutRoot.RowDefinitions[2].DesiredSize.Height;
MyStoryboard.From = fromHeight;
MyStoryboard.To = toHeight;
MyStoryboard.Begin();
}

Related

Bindings not set for items not visible

Sorry if this is a dumb question. I am maintaining this crazy Windows Phone 8.1 RT dynamic app that I didn't write. It loads up a whole bunch of stuff to the DataContext. Things that aren't visible on the screen don't seem to get their DataContext. When I navigate away from the form the event fires. What do I need to do to fix that? Even when it scrolls into view it doesn't load. If I scoll up and click the back button I see the field populate before it goes to the previous page.
Edit - Here is some code:
<DataTemplate x:Key="com.somecompany.BarcodeboxRenderer">
<Grid HorizontalAlignment="Stretch" DataContext="{Binding Item1}">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Label}" HorizontalAlignment="Stretch" Style="{StaticResource labelstyle}" Grid.Row="0" />
<somecompany:BarcodeBox IsReadOnly="{Binding DisplayOnly}" somecompany:DynamicDataBindingPath.BindingPath="{Binding FieldId}" Grid.Row="1" />
</Grid>
</DataTemplate>
That calls this but only if it's visible:
public sealed class DynamicDataBindingPath:FrameworkElement
{
public static readonly DependencyProperty BindingPathProperty = DependencyProperty.RegisterAttached("BindingPath", typeof(string), typeof(DynamicDataBindingPath), new PropertyMetadata("", OnBindingPathPropertyChanged));
public static string GetBindingPath(FrameworkElement target)
{
try
{
return (string)target.GetValue(BindingPathProperty);
}
catch (Exception)
{
return string.Empty;
}
}
public static void SetBindingPath(FrameworkElement target, string value)
{
target.SetValue(BindingPathProperty, value);
target.Loaded += Target_Loaded;
}
private static void Target_Loaded(object sender, RoutedEventArgs e) {
var target = (FrameworkElement)sender;
if (target is ...) { ... }
else if (target is DatePicker)
{
int startingletterindex = value.IndexOf('.') + 1;
string pathtobindto = "obj." + Char.ToUpper(value[startingletterindex]) + value.Substring((startingletterindex + 1));
Binding newbind = new Binding();
newbind.Path = new PropertyPath(pathtobindto);
var contextsrc = findRealDataContext(target);
newbind.Source = contextsrc.DataContext;
newbind.Mode = BindingMode.TwoWay;
(target as DatePicker).SetBinding(DatePicker.DateProperty, newbind);
}
else if (...)
}
I turned of ListView Virtualization and it works now. I don't need it because there will never be more than a handful of rows.

Thumb.DragStarted event not working in visual studio 2013

I am trying to make a small music player for windows phone. I have added a slider functionality in the player. The slider works fine as the music plays. But I want to change the media according to how much i drag the slider, but cannot find any relevant event for it. I have tried value changed but it does not help. Also I tried Thumb.Dragstarted event but my visual studio gives an error.. this is the code so far:
XAML:
<Slider AllowDrop="True" x:Name="sld1" Thumb.DragStarted="sld1_DragStarted" HorizontalAlignment="Left" Margin="58,213,0,0" VerticalAlignment="Top" Width="351" ValueChanged="sld1_ValueChanged"/>
<MediaElement x:Name="bleep" Source="abcd.wav" AutoPlay="False" Visibility="Collapsed" MediaEnded="bleep_MediaEnded"/>
C#:
public Page1()
{
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += timer_Tick;
timer.Start();
}
private bool userIsDraggingSlider = false;
private void timer_Tick(object sender, EventArgs e)
{
if ((bleep.Source != null) && (bleep.NaturalDuration.HasTimeSpan) && (!userIsDraggingSlider))
{
sld1.Minimum = 0;
sld1.Maximum = bleep.NaturalDuration.TimeSpan.TotalSeconds;
sld1.Value = bleep.Position.TotalSeconds;
}
}
private void sld1_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
tm_passed.Text = TimeSpan.FromSeconds(sld1.Value).ToString(#"mm\:ss");
}
private void sld1_DragStarted(object sender, DragStartedEventArgs e)
{
userIsDraggingSlider = true;
}
private void sld1_DragCompleted(object sender, DragCompletedEventArgs e)
{
userIsDraggingSlider = false;
bleep.Position = TimeSpan.FromSeconds(sld1.Value);
}
But since the DragCompleted and DragStarted events are not working I cannot provide the drag functionality to the slider.
What I identified from the Thumb class is that, you can't simply add Thumb.DragStarted="sld1_DragStarted within your Slider. You'll be able to add that kind of event only for Thumb control. Refer the bottom of the article for sample code.

ScrollViewer not scroll up while Keyboard is active

How I can get behavior of form in windows Phone like Contacts >> New contacts >> Name. In this page it have many textboxes in scrollviewer. When user taps on any textbox and its get focus then the page scrolls up and header remains constant and SIP keyboard shown.
This is a my example but not it works
https://app.box.com/s/lxxcmxp8ckuottrweg52
Why?
thank you
I have modified the above code that works fine for as below.
public double OldHeight;
private TranslateTransform _translateTransform;
#region TranslateY dependency property
public static readonly DependencyProperty TranslateYProperty = DependencyProperty.Register(
"TranslateYProperty", typeof(double), typeof(Chat), new PropertyMetadata(default(double), PropertyChangedCallback));
private static void PropertyChangedCallback(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var chat = o as Chat;
#if DEBUG
Debug.WriteLine("New value:" + e.NewValue);
Debug.WriteLine("Old value:" + e.OldValue);
#endif
if (chat != null)
{
chat.UpdateTopMargin((double)e.NewValue);
}
}
public double TranslateY
{
get { return (double)GetValue(TranslateYProperty); }
set { SetValue(TranslateYProperty, value); }
}
#endregion
private void ChatPage_OnLoaded(object sender, RoutedEventArgs e)
{
var transform = ((Application.Current).RootVisual).RenderTransform as TransformGroup;
if (transform != null)
{
_translateTransform = transform.Children.OfType<TranslateTransform>().FirstOrDefault();
if (_translateTransform != null)
{
var binding = new Binding("Y")
{
Source = _translateTransform
};
BindingOperations.SetBinding(this, TranslateYProperty, binding);
}
}
}
private void UpdateTopMargin(double translateY)
{
LayoutRoot.Margin = new Thickness(0, -translateY, 0, 0);
}
Thanks
First, name your scroll viewer ScrollViewer. After that add the GotFocus and LostFocus event handlers for each text box control on the page and write the following code inside:
private void txt_LostFocus(object sender, RoutedEventArgs routedEventArgs)
{
ScrollViewer.Height = _oldHeight;
}
void txt_GotFocus(object sender, RoutedEventArgs e)
{
var transform = ((Application.Current).RootVisual).RenderTransform as TransformGroup;
if (transform != null)
{
_translateTransform = transform.Children.OfType<TranslateTransform>().FirstOrDefault();
if (_translateTransform != null)
{
var binding = new Binding("Y")
{
Source = _translateTransform
};
BindingOperations.SetBinding(this, TranslateYProperty, binding);
}
}
var clipboardVisible = false;
try
{
clipboardVisible = Clipboard.ContainsText();
}
// ReSharper disable once EmptyGeneralCatchClause
catch
{
}
ScrollViewer.Height = _oldHeight - (clipboardVisible ? 407 : 338);
}
You will need to add the following dependency property to the page:
#region TranslateY dependency property
public static readonly DependencyProperty TranslateYProperty = DependencyProperty.Register(
"TranslateYProperty", typeof(double), typeof(OrderContactPage), new PropertyMetadata(default(double), PropertyChangedCallback));
private static void PropertyChangedCallback(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
((OrderContactPage)o)._translateTransform.Y = 0;
}
public double TranslateY
{
get { return (double)GetValue(TranslateYProperty); }
set { SetValue(TranslateYProperty, value); }
}
#endregion
And also helper fields:
private double _oldHeight;
private TranslateTransform _translateTransform;
You also need to handle some events for your scroll viewer, add this to the page's constructor:
ScrollViewer.Loaded += ScrollViewerOnLoaded;
ScrollViewer.SizeChanged += ScrollViewer_OnSizeChanged;
Implement those event handlers:
private void ScrollViewerOnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
ScrollViewer.Loaded -= ScrollViewerOnLoaded;
_oldHeight = ScrollViewer.ActualHeight;
}
private async void ScrollViewer_OnSizeChanged(object sender, SizeChangedEventArgs e)
{
await ScrollToFocusedElement();
}
private async Task ScrollToFocusedElement()
{
await Task.Yield();
var focusedElement = FocusManager.GetFocusedElement() as PhoneTextBox;
if (focusedElement != null)
{
// http://stackoverflow.com/questions/1225318/how-can-i-make-the-silverlight-scrollviewer-scroll-to-show-a-child-control-with
var focusedVisualTransform = focusedElement.TransformToVisual(ScrollViewer);
var rectangle =
focusedVisualTransform.TransformBounds(
new Rect(new Point(focusedElement.Margin.Left, focusedElement.Margin.Top), focusedElement.RenderSize));
var offset = ScrollViewer.VerticalOffset + (rectangle.Bottom - ScrollViewer.ViewportHeight);
ScrollViewer.ScrollToVerticalOffset(offset);
}
}
Wow, there is a lot of code there. I am working on creating something reusable, but I am not there yet. Once I do it, I will publish it on NuGet.
Note that this only works in Portrait mode and will not work perfectly if you have the auto suggestion bar open. I didn't need to handle that in my app so I skipped it :)
Enjoy.
you can try this sample sample project
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="60" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0"
Background="#002080">
<TextBlock Text="PAGE HEADER" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
<Grid Grid.Row="1" />
<TextBox Grid.Row="2"
Text=""
x:Name="messageBox"
Background="White" LostFocus="MessageBox_OnLostFocus" />
</Grid>
public MainPage()
{
InitializeComponent();
this.Loaded += MainPage_Loaded;
}
private static double _newValue;
private static readonly DependencyProperty TranslateYProperty = DependencyProperty.Register("TranslateY", typeof(double), typeof(MainPage), new PropertyMetadata(0d, OnRenderXPropertyChanged));
private double TranslateY
{
get { return (double)GetValue(TranslateYProperty); }
}
private static void OnRenderXPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((double)e.NewValue <= _newValue)
((MainPage)d).UpdateTopMargin((double)e.NewValue);
_newValue = (double)e.NewValue;
}
private void BindToKeyboardFocus()
{
var frame = Application.Current.RootVisual as PhoneApplicationFrame;
if (frame == null) return;
var group = frame.RenderTransform as TransformGroup;
if (#group == null) return;
var translate = #group.Children[0] as TranslateTransform;
var translateYBinding = new Binding("Y") { Source = translate };
SetBinding(TranslateYProperty, translateYBinding);
}
private void UpdateTopMargin(double translateY)
{
double prevTopMargin = LayoutRoot.Margin.Top;
LayoutRoot.Margin = new Thickness(0, -translateY, 0, 0);
}

Implement Swipe event on WP8

I am creating a wp8 application for showing some html files in a browser and the structure is
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Name="PageNavigationMenu">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Height="70" Content="P" Grid.Column="0" VerticalContentAlignment="Center" Click="btnPrevious_Click" x:Name="btnPrevious" ></Button>
<Button Height="70" Content="N" Grid.Column="1" VerticalAlignment="Center" Click="btnNext_Click" x:Name="btnNext"></Button>
</Grid>
<Grid Grid.Row="1" Hold="Grid_Hold">
<phone:WebBrowser IsScriptEnabled="True" x:Name="mainBrowserControl">
</phone:WebBrowser>
</Grid>
</Grid>
Now i am using a previous button and next button to change the content in browser . I want to do it using the Swipe Left / Swipe Right on browser . Like if user Swipe left direction , borswer content should be loaded with previous page and Swipe right results to load next page.
So what are the events i have to listen to implement swipe feature
There are two possibilities (AFAIK):
You can use XNA TouchPanel (more information about it you can find here: Working with TouchInput, Detecting Gestures and this blog) for this:
public MainPage()
{
InitializeComponent();
TouchPanel.EnabledGestures = GestureType.Flick;
myWebbrowser.ManipulationCompleted += myWebbrowser_ManipulationCompleted;
}
private void myWebbrowser_ManipulationCompleted(object sender, System.Windows.Input.ManipulationCompletedEventArgs e)
{
if (TouchPanel.IsGestureAvailable)
{
GestureSample gesture = TouchPanel.ReadGesture();
switch (gesture.GestureType)
{
case GestureType.Flick:
if (e.FinalVelocities.LinearVelocity.X < 0)
LoadNextPage();
if (e.FinalVelocities.LinearVelocity.X > 0)
LoadPreviousPage();
break;
default:
break;
}
}
}
Or you can use Silverlight Toolkit as described in this answer or other here.
EDIT - small remark
Only watch out, because when you use XNA, you sometimes need to do (for example OnNavigatedTo):
FrameworkDispatcher.Update();
Otherwise your App will sometimes crash.
Hope this helps.
EDIT 2 - code example after comment
In case your ManipulationEvent is handeled first (for example by Pivot or Map), I tried to subscribe to Touch.FrameReported - as I've tested - it should work:
public MainPage()
{
InitializeComponent();
TouchPanel.EnabledGestures = GestureType.Flick;
Touch.FrameReported += Touch_FrameReported;
}
private void Touch_FrameReported(object sender, TouchFrameEventArgs e)
{
if (TouchPanel.IsGestureAvailable)
{
GestureSample gesture = TouchPanel.ReadGesture();
switch (gesture.GestureType)
{
case GestureType.Flick:
if (gesture.Delta.X > 0)
MessageBox.Show("Right");
if (gesture.Delta.X < 0)
MessageBox.Show("Left");
break;
default:
break;
}
}
}
NEXT EDIT (after next comments) - better implementation and example of disabling up, down gestures:
If you don't want to activate for up (little left)/down (little right), you have to describe conditions yourself. Be aware that there is a very little chance (if any) that user will move his finger only left/right/up/down - so you have to include some margin. There are many solutions for that, you have to try and play with it, the simpliest solutions just compare deltaX and deltaY of gesture:
public MainPage()
{
InitializeComponent();
TouchPanel.EnabledGestures = GestureType.Flick | GestureType.HorizontalDrag;
Touch.FrameReported += Touch_FrameReported;
}
TouchPoint firstPoint;
private void Touch_FrameReported(object sender, TouchFrameEventArgs e)
{
TouchPoint mainTouch = e.GetPrimaryTouchPoint(ContentPanel);
if (mainTouch.Action == TouchAction.Down) firstPoint = mainTouch;
else if (mainTouch.Action == TouchAction.Up && TouchPanel.IsGestureAvailable)
{
double deltaX = mainTouch.Position.X - firstPoint.Position.X;
double deltaY = mainTouch.Position.Y - firstPoint.Position.Y;
if (Math.Abs(deltaX) > 2 * Math.Abs(deltaY))
{
if (deltaX < 0) MessageBox.Show("Right.");
if (deltaX > 0) MessageBox.Show("Left.");
}
}
}

WinRT FlipView like control in WP8

In my app I need to display a collection of Images exactly like in the Windows Phone 8 Photo App where you can swipe right and left between the images.
I've tried both the Panorama and Pivot control but both controls don't behave like WinRTs FlipView.
Panorama fits quite well but appears to have the "Right-Peek" Amount hardwired into the control. (please correct me if I'm wrong)
Pivot in turn shows blackness during swipes (finger still down) and only displays the next image when you release your finger and the control scrolls the next item into place.
Any suggestions?
Here is the customized FlipView control for WP8 like WINRT FlipView Control...
Step 1 : Add a new Usercontrol and name it as "FlipView.xaml"
Step 2 : Add following xaml in "FlipView.xaml"
<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">
<ContentPresenter Name="contentPresenter"/>
<Button BorderThickness="0" Name="leftButton" FontSize="70" Margin="-25" HorizontalAlignment="Left" VerticalAlignment="Center" Content="<" Click="Button_Click"/>
<Button BorderThickness="0" Name="rightButton" FontSize="70" Margin="-25" HorizontalAlignment="Right" VerticalAlignment="Center" Content=">" Click="Button_Click_1"/>
</Grid>
Step 3 : Add the following code in the "FlipView.cs"
public partial class FlipView : UserControl
{
public FlipView()
{
InitializeComponent();
Datasource = new List<object>();
SelectedIndex = 0;
}
private IList Datasource;
public static readonly DependencyProperty ItemTemplateProperty =
DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(FlipView), new PropertyMetadata(default(DataTemplate)));
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set
{
SetValue(ItemTemplateProperty, value);
contentPresenter.ContentTemplate = value;
contentPresenter.Content = SelectedItem;
}
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IList), typeof(FlipView), new PropertyMetadata(default(IList)));
public IList ItemsSource
{
get { return (IList)GetValue(ItemsSourceProperty); }
set
{
SetValue(ItemsSourceProperty, value);
Datasource = value;
SelectedIndex = SelectedIndex;
}
}
public static readonly DependencyProperty SelectedIndexProperty =
DependencyProperty.Register("SelectedIndex", typeof(int), typeof(FlipView), new PropertyMetadata(default(int)));
public int SelectedIndex
{
get { return (int)GetValue(SelectedIndexProperty); }
set
{
SetValue(SelectedIndexProperty, value);
rightButton.Visibility = leftButton.Visibility = Visibility.Visible;
if (SelectedIndex == 0)
{
leftButton.Visibility = Visibility.Collapsed;
}
if (SelectedIndex + 1 == Datasource.Count)
{
rightButton.Visibility = Visibility.Collapsed;
SelectedItem = Datasource[SelectedIndex];
}
if (Datasource.Count > SelectedIndex + 1)
{
SelectedItem = Datasource[SelectedIndex];
}
}
}
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(object), typeof(FlipView), new PropertyMetadata(default(object)));
public object SelectedItem
{
get { return (object)GetValue(SelectedItemProperty); }
set
{
SetValue(SelectedItemProperty, value);
contentPresenter.Content = SelectedItem;
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
SelectedIndex--;
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
SelectedIndex++;
}
}
Step 4 : Now at the mainpage, add the namespace to use the flipview Usercontrol
Example:
xmlns:FlipViewControl="clr-namespace:ImageFlip" (Note: It differs according to your Solution name).
Step 5 : Using the namespace, add the flipview control as follow as..
<Grid x:Name="LayoutRoot" Background="Transparent">
<FlipViewControl:FlipView Name="imgViewer">
<FlipViewControl:FlipView.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}" Stretch="Fill"/>
</DataTemplate>
</FlipViewControl:FlipView.ItemTemplate>
</FlipViewControl:FlipView>
</Grid>
Step 6 : Add the following code in mainpage.cs
// Constructor
public MainPage()
{
InitializeComponent();
// Sample code to localize the ApplicationBar
//BuildLocalizedApplicationBar();
imgViewer.ItemsSource = new List<string> { "/Images/1.jpg", "/Images/2.jpg", "/Images/3.jpg" };
}
Hope this will help.
Thanks
There is no direct equivalent to the FlipView in Windows Phone. The Panorama and Pivot controls have very different functionalities and are designed fro different purposes.
Telerik have a SlideView control which is very similar to the native control used by the photos app.
You can also get the Telerik controls free as part of the Nokia Premium Developer Program. (Worth investigating if you don't have a Dev Center subscription.)
I know it is not the same solution, but maybe you can tweak this coverflow example here... so that the images are not stacked but side by side?