Implement Swipe event on WP8 - windows-phone-8

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

Related

How to change Flipviewitem from Flipview dynamically using c#

I am working on Windows phone 8.1 winRT App and try to load FilpviewItem from code behind using the index property but its not working for me how we slide flipviewitem from c#
Following is my code which is not working
<FlipView x:Name="flip" Grid.Row="1" Grid.ColumnSpan="4" Grid.Column="0" Margin="0,0,0,0.333" Grid.RowSpan="2">
<FlipViewItem>
<Button Foreground="White" Background="Black" x:Name="btnAdd" Content="Add" Grid.Row="1" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Center" Tapped="btnAdd_Tapped" ></Button>
</FlipViewItem>
<FlipViewItem>
<Button x:Name="btny2" Content="mov" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center" Tapped="btny2_Tapped" ></Button>
</FlipViewItem>
<FlipViewItem>
<Button x:Name="btnUpdate" Content="Upd" Grid.Row="1" Grid.Column="2" HorizontalAlignment="Right" Tapped="btnupdate_Tapped" ></Button>
</FlipViewItem>
<FlipViewItem>
<Button x:Name="btnRemove" Content="Rem" Grid.Row="1" Grid.Column="3" HorizontalAlignment="Center" Tapped="btnRemove_Tapped" ></Button>
</FlipViewItem>
</FlipView>
private void btnAdd_Tapped(object sender, TappedRoutedEventArgs e)
{
flip.SelectedIndex++;
}
private void btnupdate_Tapped(object sender, TappedRoutedEventArgs e)
{
flip.SelectedIndex++;
}
private void btny2_Tapped(object sender, TappedRoutedEventArgs e)
{
flip.SelectedIndex++;
}
private void btnRemove_Tapped(object sender, TappedRoutedEventArgs e)
{
flip.SelectedIndex++;
}
The amusing thing here is that your code works perfectly well when I copy paste it on my system. However, you could use data binding to achieve this in a cleaner manner.
How To do it with data binding:
create a property named FlipSelectedIndex for example.
private int flipSelectedIndex = 0;
public int FlipSelectedIndex
{
get { return flipSelectedIndex; }
set
{
if (value <= flip.Items.Count - 1)
{
flipSelectedIndex = value;
RaisePropertyChanged("FlipSelectedIndex");
}
}
}
Add an INotifyPropertyChangedinterface and implement it implicitly and then add an event to it called RaisePropertyChanged do this by adding a ,INotifyPropertyChangedinterface to ClassName : Page declaration and add the following method:
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
Please Note: The PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); Only works with C# 6.0 which is available in Visual Studio 2015. For any editions you will have to use
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
Once done now it's simple: at a button tap simply use FlipSelectedIndex++; or FlipSelectedIndex--; as required.
Update the XAML with adding the SelectedIndex property of the Flipview to data binding like this: SelectedIndex="{Binding FlipSelectedIndex,Mode=TwoWay}" Use two way binding to ensure your FlipSelectedIndex also updates if you change the selected view on the Flipview from the swipe action instead of the button.
Please do remember to add that data context of the page to the code behind using DataContext="{Binding RelativeSource={RelativeSource Self}}" Else the binding won't work.
For more information you could refer to my answer on the FlipView here

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.

Using FlipView to create TabControl in Metro Apps

i'm currently creating a Metro Style Application and want to use the FlipView control to work like the tab control in WPF and winforms, please can any one help me ?
You would put FlipViewItems in the FlipView the same way you'd put TabItems in a TabControl. To add tabs - you could have a StackPanel with TextRadioButtonStyled RadioButtons that have their check states synchronized with the FlipView selection state. Alternatively you could have a heavily styled ListView for the headers bar.
i solved it by editing the flipviewitem template and making its view like the wanted tab page view. Then i added a button on the top of every item to activate it.
We don't have Tab Control in WP8.1 but we can customize using FlipView. Flip View has a property selected index so we can set which view we want.
Create a xaml page, for example MainPage.xaml
For Tab Header
<Border BorderThickness="0,0,0,1" BorderBrush="White" >
<Grid Grid.Row="0" Background="Black" x:Name="navigateHead" >
<TextBlock x:Name="appbarSports" Text="Sports" Tapped="appbarSports_Tapped" TextAlignment="Center" Width="80" Margin="0,34,320,7" />
<TextBlock x:Name="appbarCars" Text="Cars" Tapped="appbarCars_Tapped" Margin="160,34,160,7" TextAlignment="Center" />
<TextBlock x:Name="appbarHomes" Text="Homes" Tapped="appbarHomes_Tapped" Margin="80,34,240,7" TextAlignment="Center" />
<Image x:Name="imgLine0" Source="ms-appx:///Images/white.png" Width="80" Height="3" Stretch="Fill" Margin="0,55,320,1" ></Image>
<Image x:Name="imgLine1" Source="ms-appx:///Images/white.png" Width="80" Height="3" Stretch="Fill" Margin="81,55,239,1" Visibility="Collapsed" />
<Image x:Name="imgLine2" Source="ms-appx:///Images/white.png" Width="80" Height="3" Stretch="Fill" Margin="162,55,158,1" Visibility="Collapsed" />
</Grid>
</Border>
For Flip view XAML code is
<Grid Grid.Row="1" >
<FlipView x:Name="flipControl" SelectionChanged="flipControl_SelectionChanged" >
<FlipViewItem >
<ListView x:Name="listViewForSports" >
<ListView.ItemTemplate>
<DataTemplate>
<Image Stretch="Fill" Source="{Binding SportsImage}"></Image>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</FlipViewItem>
<FlipViewItem >
<ListView x:Name="listViewForHomes">
<ListView.ItemTemplate>
<DataTemplate>
<Image Source="{Binding HomesImage}" Stretch="Fill"></Image>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</FlipViewItem>
<FlipViewItem >
<ListView x:Name="listViewForCars">
<ListView.ItemTemplate>
<DataTemplate>
<Image Source="{Binding CarsImage}" Stretch="Fill"></Image>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</FlipViewItem>
</FlipView>
</Grid>`
and write in code behind file MainPage.xaml.cs
Just initialize these globally:
public sealed partial class MainPage : Page
{
List<Sports> listOfSports;
List<Cars> listOfCars;
List<Homes> listOfHomes;
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
}
bind data to the FlipView control when page is initialized
protected override void OnNavigatedTo(NavigationEventArgs e)
{
flipControl.SelectedIndex = 0;
// TODO: Prepare page for display here.
// TODO: If your application contains multiple pages, ensure that you are
// handling the hardware Back button by registering for the
// Windows.Phone.UI.Input.HardwareButtons.BackPressed event.
// If you are using the NavigationHelper provided by some templates,
// this event is handled for you.
GetData();
}
This method will bind
public void GetData()
{
listOfSports = new List<Sports>();
for (int i = 1; i < 9; i++)
{
listOfSports.Add(new Sports() { SportsImage = #"ms-appx:///Images/Sports/image" + i.ToString() + ".jpg" });
}
listViewForSports.ItemsSource = listOfSports;
listOfCars = new List<Cars>();
for (int i = 1; i < 14; i++)
{
listOfCars.Add(new Cars() { CarsImage = #"ms-appx:///Images/Cars/image" + i.ToString() + ".jpg" });
}
listViewForCars.ItemsSource = listOfCars;
listOfHomes = new List<Homes>();
for (int i = 1; i < 9; i++)
{
listOfHomes.Add(new Homes() { HomesImage = #"ms-appx:///Images/Homes/image" + i.ToString() + ".jpg" });
}
listViewForHomes.ItemsSource = listOfHomes;
}
public class Cars
{
public string CarsImage { get; set; }
}
public class Sports
{
public string SportsImage { get; set; }
}
public class Homes
{
public string HomesImage { get; set; }
}`
I did with tap event to navigation withing flip view` private void appbarSports_Tapped(object sender, TappedRoutedEventArgs e)
{
// TextBlock a = sender as TextBlock;
// imgLine0.Margin = new Thickness(a.Margin.Left, 55, a.Margin.Right, 1);
flipControl.SelectedIndex = 0;
method(0);
}
private void appbarCars_Tapped(object sender, TappedRoutedEventArgs e)
{
// TextBlock a = sender as TextBlock;
//imgLine0.Margin = new Thickness(a.Margin.Left, 55, a.Margin.Right, 1);
flipControl.SelectedIndex = 2;
method(2);
}
private void appbarHomes_Tapped(object sender, TappedRoutedEventArgs e)
{
// TextBlock a = sender as TextBlock;
// imgLine0.Margin = new Thickness(a.Margin.Left, 55, a.Margin.Right, 1);
flipControl.SelectedIndex = 1;
method(1);
}
I also used for when the user flips so I used FlipView.SelectionChanged event
private void flipControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
FlipView viewControl = sender as FlipView;
int a = viewControl.SelectedIndex;
method(a);
}
This method() is help full for changing imageLine position
public void method(int a)
{
if (imgLine0 != null)
{
switch (a)
{
case 0:
imgLine0.Visibility = Visibility.Visible;
imgLine1.Visibility = Visibility.Collapsed;
imgLine2.Visibility = Visibility.Collapsed;
break;
case 1:
imgLine0.Visibility = Visibility.Collapsed;
imgLine1.Visibility = Visibility.Visible;
imgLine2.Visibility = Visibility.Collapsed;
break;
case 2:
imgLine0.Visibility = Visibility.Collapsed;
imgLine1.Visibility = Visibility.Collapsed;
imgLine2.Visibility = Visibility.Visible;
break;
}
}
}

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

Viewing the top items in a LongListSelector on WP8 when SIP is open

I have an app that uses a LongListSelector to display a list of items, at the bottom of the page I have a TextBox. When the TextBox is tapped, the SIP displays itself. At this point, I'm unable to then scroll to the top of the LLS.
Sample code:
XAML:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<phone:LongListSelector x:Name="TheList">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"
Style="{StaticResource PhoneTextLargeStyle}"/>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</Grid>
<Grid Grid.Row="1">
<TextBox />
</Grid>
</Grid>
C#:
public MainPage()
{
InitializeComponent();
Loaded += (sender, args) =>
{
var list = new List<string>();
for (var i = 0; i < 30; i++)
{
list.Add("This is string number " + i);
}
TheList.ItemsSource = list;
};
}
This is as much as I can see, I can pull down to string number 5, but can't see any higher:
Anyone got any ideas?
The ScrollViewer doesn't take into account the SIP so its scrolling experience is the same as when the SIP is not visible (which is why the top can't be reached). One workaround would be to add a margin to the top of the LongListSelector, (or the bottom if your textbox is at the top), when the SIP is displayed.
As there's no event for the SIP, you can handle the GotFocus and LostFocus events of the TextBox. (The 180 value was obtained via trial and error)
private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
TheList.Margin = new Thickness(0,180,0,0);
}
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
TheList.Margin = new Thickness();
}