ScrollViewer not scroll up while Keyboard is active - windows-phone-8

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

Related

How to add progress bar or progress ring for webview project for windows mobile app

I have created a Webview Project for windows phone app to load google.com. It's working fine but I'm unable to add a Progress bar or Progress Ring. Can any one please help me?
namespace App2
{
public sealed partial class MainPage : Page
{
private static readonly Uri HomeUri = new Uri("http://www.google.com", UriKind.Absolute);
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
WebViewControl.Navigate(HomeUri);
HardwareButtons.BackPressed += this.MainPage_BackPressed;
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
HardwareButtons.BackPressed -= this.MainPage_BackPressed;
}
private void MainPage_BackPressed(object sender, BackPressedEventArgs e)
{
if (WebViewControl.CanGoBack)
{
WebViewControl.GoBack();
e.Handled = true;
}
}
private void Browser_NavigationCompleted(WebView sender, WebViewNavigationCompletedEventArgs args)
{
if (!args.IsSuccess)
{
Debug.WriteLine("Navigation to this page failed, check your internet connection.");
}
}
}
}
In your XAML add a progress ring over the webview so that it overlaps webview for example
<Grid>
<Grid x:Name="webViewHolder" >
<WebView x:Name="wvPage" Loaded="WebView_Loaded" NavigationCompleted="WebView_NavigationCompleted" NavigationStarting="wvPage_NavigationStarting"></WebView>
</Grid>
<ProgressRing x:Name="myProgressRing" IsActive="True" Height="90" Width="90" Background="Transparent" Foreground="#EF4D17"/>
</Grid>
Now in code Behind
private void wvPage_NavigationStarting(Windows.UI.Xaml.Controls.WebView sender, WebViewNavigationStartingEventArgs args)
{
myProgressRing.IsActive = true;
}
.
.
.
private void WebView_NavigationCompleted(Windows.UI.Xaml.Controls.WebView sender, WebViewNavigationCompletedEventArgs args)
{
myProgressRing.IsActive = false;
}

Infinite Scrolling in a ListView

I'm trying to implemented infinite scrolling in my listview.
The approach is to get the scroll bar within the listview and hook an event handler to it's scroll event.
<CollectionViewSource x:Key="PaginatedData"
IsSourceGrouped="True"
ItemsPath="CommentaryList"
Source="{Binding paginatedCommentary}"/>
<ListView x:Name="commentaryListView"
Loaded="commentaryListView_Loaded"
ItemTemplate="{StaticResource CommentaryListTemplate}"
ItemsSource="{Binding Source={StaticResource PaginatedData}}"/>
private void commentaryListView_Loaded(object sender, RoutedEventArgs e)
{
var scrollViewer = commentaryListView.GetFirstDescendantOfType<ScrollViewer>();
var scrollbars = new List<ScrollBar>(scrollViewer.GetDescendantsOfType<ScrollBar>());
var verticalBar = scrollbars.FirstOrDefault(x => x.Orientation == Orientation.Vertical);
if (verticalBar != null)
verticalBar.Scroll += BarScroll;
}
private void BarScroll(object sender, ScrollEventArgs e)
{
if (e.ScrollEventType != ScrollEventType.EndScroll) return;
var bar = sender as ScrollBar;
if (bar == null) return;
if (e.NewValue >= bar.Maximum)
{
datacontext.pageCommentaryItems();
}
}
Can't seem to figure out what I'm doing here, but it's never hitting the BarScroll event handler.
Appreciate your help. Thank you :)
I recommend you use the ListView's ViewChanged event directly:
private void OnListViewLoaded(object sender, RoutedEventArgs e)
{
var listview = sender as ListViewBase;
if (listview != null)
{
// Attach to the view changed event
_scrollViewer = listview.GetFirstDescendantOfType<ScrollViewer>();
if (_scrollViewer != null)
{
_scrollViewer.ViewChanged += OnViewChanged;
}
}
}
private void OnViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
// If scrollviewer is scrolled down at least 90%
if (_scrollViewer.VerticalOffset > Math.Max(_scrollViewer.ScrollableHeight * 0.9, _scrollViewer.ScrollableHeight - 150))
{
// Execute whatever you want
}
}
A much simpler solution would be to create an observable collection that implements the ISupportIncrementalLoading interface and bind it to your ListView. This will make the ListView do incremental scrolling without any need to implement it yourself. See https://marcominerva.wordpress.com/2013/05/22/implementing-the-isupportincrementalloading-interface-in-a-window-store-app/

WP8, null value in LoopingSelector DataSource

I am using Windows Phone Toolkit nuget package (http://phone.codeplex.com/). I want to create a LoopingSelector in my WP8 application, where I can select day dates, but I don't want the LoopingSelector to show days before today in it's selection.
My control looks like this:
http://s2.postimg.org/6bgnxefnt/image.png
This is perfectly working until I select the first element of the source (today Dec 04), then my application becomes unresponsive (I can press buttons, effect are shown, but the event handlers won't execute). I guess this is an infinite loop or deadlock, but it's not in my own code (I debugged it).
The definition of the LoopingSelector's data source:
public class NextDatesDataSource : LoopingDataSource<NextDateModel>
{
public NextDatesDataSource() : base(NextDateModel.Create(DateTime.Today)) { }
protected override NextDateModel GetNext(NextDateModel relativeTo)
{
if (relativeTo.Date == DateTime.Today + TimeSpan.FromDays(10))
return null;
return NextDateModel.Create(relativeTo.Date + TimeSpan.FromDays(1));
}
protected override NextDateModel GetPrevious(NextDateModel relativeTo)
{
//if i comment the next two lines, then everything works perfect
if (relativeTo.Date == DateTime.Today)
return null;
return NextDateModel.Create(relativeTo.Date - TimeSpan.FromDays(1));
}
}
LoopingDataSource:
public abstract class LoopingDataSource<T> : ILoopingSelectorDataSource
{
protected LoopingDataSource() { selectedItem = default(T); }
protected LoopingDataSource(T initialSelection) { selectedItem = initialSelection; }
protected abstract T GetNext(T relativeTo);
protected abstract T GetPrevious(T relativeTo);
public object GetNext(object relativeTo)
{
if (relativeTo == null) return null;
return GetNext((T)relativeTo);
}
public object GetPrevious(object relativeTo)
{
if (relativeTo == null) return null;
return GetPrevious((T)relativeTo);
}
private T selectedItem;
public object SelectedItem
{
get
{
return selectedItem;
}
set
{
object oldVal = selectedItem;
selectedItem = (T)value;
if (SelectionChanged != null)
SelectionChanged(this, new SelectionChangedEventArgs(new object[] { oldVal }, new object[] { value }));
}
}
public T Selected
{
get { return (T)SelectedItem; }
set { SelectedItem = value; }
}
public event EventHandler<SelectionChangedEventArgs> SelectionChanged;
}
The definition of the control element:
<p:LoopingSelector
x:Name="DaySelector"
Grid.Column="0" Grid.Row="1"
ItemSize="230,110"
ItemMargin="6" Margin="10,5,5,5">
<p:LoopingSelector.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Header}" FontSize="28" VerticalAlignment="Top"/>
<TextBlock Text="{Binding Body}" FontSize="42" FontWeight="Bold" VerticalAlignment="Bottom"/>
</Grid>
</DataTemplate>
</p:LoopingSelector.ItemTemplate>
</p:LoopingSelector>
I do this in the constructor:
DaySelector.DataSource = new NextDatesDataSource();
Thank you very much!

Issue with Sound Recording in windowsphone8

My code works properly till page active. But after going back to another page when i again navigate on recording page it doesn't work.
here is sample code with page added.
public partial class MainPage : PhoneApplicationPage
{
private Microphone microphone = Microphone.Default;
private byte[] buffer;
private MemoryStream stream = new MemoryStream();
private SoundEffectInstance soundInstance;
private bool soundIsPlaying = false;
// Status images
private BitmapImage blankImage;
private BitmapImage microphoneImage;
private BitmapImage speakerImage;
public MainPage()
{
InitializeComponent();
// Timer to simulate the XNA Framework game loop (Microphone is
// from the XNA Framework). We also use this timer to monitor the
// state of audio playback so we can update the UI appropriately.
DispatcherTimer dt = new DispatcherTimer();
dt.Interval = TimeSpan.FromMilliseconds(33);
dt.Tick += new EventHandler(dt_Tick);
dt.Start();
microphone.BufferReady += new EventHandler<EventArgs>(microphone_BufferReady);
blankImage = new BitmapImage(new Uri("Images/blank.png", UriKind.RelativeOrAbsolute));
microphoneImage = new BitmapImage(new Uri("Images/microphone.png", UriKind.RelativeOrAbsolute));
speakerImage = new BitmapImage(new Uri("Images/speaker.png", UriKind.RelativeOrAbsolute));
}
void dt_Tick(object sender, EventArgs e)
{
try { FrameworkDispatcher.Update(); }
catch { }
if (true == soundIsPlaying)
{
if (soundInstance.State != SoundState.Playing)
{
// Audio has finished playing
soundIsPlaying = false;
// Update the UI to reflect that the
// sound has stopped playing
SetButtonStates(true, true, false);
UserHelp.Text = "press play\nor record";
StatusImage.Source = blankImage;
}
}
}
private void recordButton_Click(object sender, EventArgs e)
{
microphone.BufferDuration = TimeSpan.FromMilliseconds(500);
buffer = new byte[microphone.GetSampleSizeInBytes(microphone.BufferDuration)];
stream.SetLength(0);
microphone.Start();
SetButtonStates(false, false, true);
UserHelp.Text = "record";
StatusImage.Source = microphoneImage;
}
private void stopButton_Click(object sender, EventArgs e)
{
if (microphone.State == MicrophoneState.Started)
{
// In RECORD mode, user clicked the
// stop button to end recording
microphone.Stop();
}
else if (soundInstance.State == SoundState.Playing)
{
// In PLAY mode, user clicked the
// stop button to end playing back
soundInstance.Stop();
}
SetButtonStates(true, true, false);
UserHelp.Text = "ready";
StatusImage.Source = blankImage;
}
private void playButton_Click(object sender, EventArgs e)
{
if (stream.Length > 0)
{
// Update the UI to reflect that
// sound is playing
SetButtonStates(false, false, true);
UserHelp.Text = "play";
StatusImage.Source = speakerImage;
// Play the audio in a new thread so the UI can update.
Thread soundThread = new Thread(new ThreadStart(playSound));
soundThread.Start();
}
}
private void playSound()
{
// Play audio using SoundEffectInstance so we can monitor it's State
// and update the UI in the dt_Tick handler when it is done playing.
SoundEffect sound = new SoundEffect(stream.ToArray(), microphone.SampleRate, AudioChannels.Mono);
soundInstance = sound.CreateInstance();
soundIsPlaying = true;
soundInstance.Play();
}
private void SetButtonStates(bool recordEnabled, bool playEnabled, bool stopEnabled)
{
(ApplicationBar.Buttons[0] as ApplicationBarIconButton).IsEnabled = recordEnabled;
(ApplicationBar.Buttons[1] as ApplicationBarIconButton).IsEnabled = playEnabled;
(ApplicationBar.Buttons[2] as ApplicationBarIconButton).IsEnabled = stopEnabled;
}
}
Another page
public partial class Page1 : PhoneApplicationPage
{
public Page1()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.RelativeOrAbsolute));
}
}
Please if possible give some solution to it because my application is dependent on audio
recording..
Thank You very much....
microphone.BufferReady -= new EventHandler<EventArgs>(microphone_BufferReady);
write this code into OnNavigateFrom even.

Animate grid row height change in WP

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