Start An Event After Scrolling - xaml

I'm new to Windows Phone apps development, and I've created a scrolling menu using the following xaml code :
<ScrollViewer HorizontalAlignment="Left" Margin="18,0,0,0" Name="scrollViewer1" VerticalAlignment="Top" Width="450" HorizontalScrollBarVisibility="Auto" Grid.Row="1">
<StackPanel Height="Auto" Name="stackPanel1" Width="Auto">
<Button Height="620" FontSize="120" Name="gotoGmail" Width="Auto">Gmail</Button>
<Button Height="620" FontSize="120" Name="gotoYahoo" Width="Auto">Yahoo</Button>
</StackPanel>
</ScrollViewer>
I'd like to know whether it's possible to start an event once the user scrolls the menu from one button to another. If it is possible, i'd be grateful if you could explain how. If it's not , i'd like to hear about how could I do it using different tools rather than ScrollViewer. Thanks in advance !

There's no "Scrolled" event on the ScrollViewer, but what you can do is two-way bind a property to VerticalOffset. That lets you trigger an event/command from your view/viewmodel when the scroll changes.
Something like this:
<ScrollViewer VerticalOffset="{Binding VerticalOffset,Mode=TwoWay}" ...
And then in the data context:
public double VerticalOffset
{
get { return _verticalOffset; }
set
{
_verticalOffset = value;
// call "on scroll changed" actions here
}
}
private double _verticalOffset = 0;
how could I do it using different tools rather than ScrollViewer
You can of course make a scrolling menu using other approaches. I'll bet there is some nifty approach you could figure, using the WinRT transitions/animations stuff, but I'm not familiar enough with those to say. Here are some others (not sure which would be best/easiest for your scenario):
Probably using Canvas would be a quick-and-dirty way to do it (just set up buttons that set off Canvas.Left and Canvas.Top animations).
Extending ItemsControl along with a custom ControlTemplate would be a good approach if you want to create a re-usable component.
I like extending Panel, but you have to do the animations manually using a DispatcherTimer, and you have to lay out the buttons yourself using Measure and Arrange.

Related

Xamarin TapGestureRecognizer sometime does not work properly

I am currently building my mobile application using Xamarin.Forms and i encountered a problem (on both platform of ios and android) when i tried to use Xamarin.Forms gestures more particularly a tap gesture on a xaml Label. Because i want to use this label as a link.
The problem is that this tab gesture that i used does not work sometime ( approximately 5 times test = 1 time bug).
During DEBUG when the problem occured i see that the tabbing is still recognized but it did not respond in the action i set up.
It occurs on both iOS and Android devices.
Here is my XAML code:
<RelativeLayout>
<Image Source="icon_question" WidthRequest="15" HeightRequest="15"></Image>
<Label Margin="15, -3, 0, 0" HorizontalOptions="CenterAndExpand" HorizontalTextAlignment="Center"
Text="Some text" TextColor="Blue" FontSize="15" TextDecorations="Underline">
<Label.GestureRecognizers>
<TapGestureRecognizer Tapped="_tabLinkForgetPassword"></TapGestureRecognizer>
</Label.GestureRecognizers>
</Label>
</RelativeLayout>
and here is my code behind:
private void _tabLinkForgetPassword(object s, EventArgs e)
{
App.Current.MainPage = new ResetPasswordPage(false);
}
I expect that the tab respond everytime, not just sometime like this. I appreciate all your help.
As stated by AndroDevil, you have to handle the tap gesture on a parent (a Grid, StackLayout, ContentView, whatever you want). in your case, why don't you use the Relative layout ? Thus, you can tap either the Label or the Image.
When you think it doesn't work, it is just that when you tap on the empty space : between characters (or even inside the void of a char like O (but admit it, kind of hard to tap on those few pixels)) of your text.
Last, you don't need to set NumberOfTapsRequired because as far as I remember, it is the default value.

Using x:Bind inside the GridView's ItemTemplate layout User Control in UWP

In the Universal Windows Platform API, how do I use x:Bind inside of a User Control (intended to be the layout for a GridView's ItemTemplate) to bind to instance properties of a GridView's ItemSource?
Background
I'm trying to re-create the layout found in Windows 10 stock apps like Sports, News, Money, etc.
I'm using a two GridViews for the main area of the app; one for "featured articles" (2 large photos w/ headlines) and one for all the other articles (smaller photos w/ headlines).
I'm able to bind to a data source that I supply in the code behind (a List where NewsItem is a POCO with a Image and Headline property) Here's the pertinent parts of the MainPage.xaml:
<Page ...
xmlns:data="using:NewsApp.Models" />
....
<GridView Name="FeaturedItems" Grid.Row="0">
<GridView.ItemTemplate>
<DataTemplate x:DataType="data:NewsItem">
<Grid Name="mainPanel" HorizontalAlignment="Stretch" Width="500" >
<Image Source="{x:Bind Image}" HorizontalAlignment="Stretch" />
<TextBlock Text="{x:Bind Headline}" />
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
....
The Image and Headline bind just fine (even though they've not been styled correctly). However, instead I think I need to bind to a User Control to get the styling options I want, control over resizing esp. when using Visual State Triggers and to simplify the XAML in general (at least, this was the technique suggested to me.)
So, I added a new User Control to the project (FeaturedItemControl.xaml), and copied in the DataTemplate's child Grid:
<UserControl ... >
<Grid Name="mainPanel" HorizontalAlignment="Stretch" Width="500" >
<Image Source="{x:Bind Image}" HorizontalAlignment="Stretch" />
<TextBlock Text="{x:Bind Headline}" />
</Grid>
</UserControl>
And then back in the MainPage.xaml, I change the DataTemplate to reference the new FeaturedItemControl:
<GridView Name="FeaturedItems" Grid.Row="0">
<GridView.ItemTemplate>
<DataTemplate x:DataType="data:NewsItem">
<local:FeaturedItemControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
However, I get the error message for both Image and Headline properties: Invalid binding path 'Headline': Property 'Headline' can't be found on type 'FeaturedItemControl'.
I've tried a few things but am flailing just throwing code at the problem without understanding what I'm doing. Any help would be greatly appreciated.
Thank you for your kind attention.
Using Depechie's answer, I formulated this little cheat cheat for posterity:
Do note that you MUST use this technique to utilize the VisualStateManager with items inside your data bound controls' (GridView, ListView) data templates.
1) Create a User Control.
2) Cut the content of the DataTemplate in your page and paste it into the User Control replacing the template's Grid.
3) Reference the User Control from inside the Data Template:
4) Modify the contents of the User Control changing x:Bind statements to utilize object.property notation:
<UserControl>
<StackPanel>
<Image Source="{x:Bind NewsItem.LeadPhoto}" />
<TextBlock Text="{x:Bind NewsItem.Headline}" />
<TextBlock Text="{x:Bind NewsItem.Subhead}" />
</StackPanel>
</UserControl>
5) Add this in the User Control's Code Behind:
public Models.NewsItem NewsItem { get { return this.DataContext as Models.NewsItem; } }
public ContactTemplate()
{
this.InitializeComponent();
this.DataContextChanged += (s, e) => Bindings.Update();
}
Well it's possible to use x:Bind in user controls, but you'll need to add some extra code behind.
I encountered the same problem in my project, you can see the result here : https://github.com/AppCreativity/Kliva/tree/master/src/Kliva/Controls
So what you need to do is, create a property in the code behind of your user control that points to the correct DataContext.
If you do that, you can use properties of that DataContext in the xaml of your control: for example:
Do note that in the constructor of your control you do need to add: DataContextChanged += (sender, args) => this.Bindings.Update(); because the datacontext will change depending on the page where your control is used!
Then on the page where you are placing this control, you'll also need to do the same to enable the x:bind to work.
You'll see this in my example on the MainPage.DeviceFamily-Mobile.xaml and MainPage.xaml.cs files.
Hope this helps.
x:Bind isn't really hierarchical like Binding/DataContext is. Additionally when you're not directly inside a DataTemplate (such as inside your user control) the object that x:Bind tries to use is 'this' rather than 'this.DataContext'. My current line of thinking on how to solve this sort of issue is to try not to use UserControls anywhere. Instead preferring DataTemplates contained within a ResourceDictionary. There are some pretty strong caveats to this approach though, you will for example crash the xaml compiler if you use x:Bind inside a data template that was created from the ResourceDictionary item template (add new item). you can find a pretty complete example here https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/XamlBind its important to note in the sample where they show the ResourceDictionary being used that its not actually just a ResourceDictionary.xaml its also a ResourceDictionary.xaml.cs (this is where the generated code from x:Bind ends up)
Another option is to add Headline and Image as properties on your user control and x:Bind them from the template, then inside the user control x:Bind as you are currently doing, but now the x:Bind generated path 'this.Headline' will exist. Unfortunately the order things are actually bound means that the x:Bind's you have inside your user control will have to be OneWay rather than the default OneTime. this is because x:Bind OneTime does the bind inside the InitializeComponent call, and any set of properties/DataContext stuff doesn't get done until after that has already run.
So to sum this up, you have two options, use data templates everywhere, or bind to properties that are directly on the user control.

Keep element in view while scrolling

Simpel question, I have a windows phone page that contains a scrollviewer with inside it an image, a textblock and a richtextbox.
Now when the user starts scrolling I want to keep the textblock in view on top when the image has scrolled outside the page.
So the effect is, user starts scrolling upwards, everything scrolls upwards, when the image is outside the page, the textblock stays at the top of the page but the richtextbox keeps scrolling upwards.
Any thoughts?
Here is a way to reach this result:
First, the layout. I've set a grid, with two rows. The first is empty, and will host the header when we need to freeze it. The second row contains the scrollviewer.
Inside the scrollviewer, I've put the controls in a grid, but you can use whatever container suits you.
<ScrollViewer Grid.Row="1"
Margin="0"
Padding="0"
x:Name="ParentScroll"
ManipulationMode="Control"
MouseMove="ParentScroll_MouseMove">
<Grid x:Name="ChildGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Image Source="Picture.jpg" Grid.Row="0"/>
<TextBlock Text="Header" Grid.Row="1" x:Name="TextHeader" />
<RichTextBox Grid.Row="2" x:Name="RichText">
<Paragraph>
<Bold>RichTextBox</Bold>
<!-- More stuff -->
</Paragraph>
</RichTextBox>
</Grid>
</ScrollViewer>
I use the MouseMove event to be notified of the scrolling event. You can also dig into the template, extract the ScrollBar control, and subscribe to the ValueChanged event, as described here: http://social.msdn.microsoft.com/Forums/wpapps/en-US/81fcd34e-6ec9-48d0-891e-c53a53344553/scrollviewer-synchronization
Note that you need to set ManipulationMode to Control or the position of the controls won't be updated at a smooth rate. I guess it's due to some internal optimization.
In the code behind, I use the TransformToVisual method to compute the relative position of the controls to the ScrollViewer. This way, I can know when the header goes out of view. When it does, I remove it from the child grid, and put it outside of the ScrollViewer, in the parent grid. When the top of the RichTextBox goes out of view, I put the header back into the ScrollViewer:
private void ParentScroll_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
if (Grid.GetRow(this.TextHeader) == 1)
{
var generalTransform = TextHeader.TransformToVisual(ParentScroll);
var childToParentCoordinates = generalTransform.Transform(new Point(0, 0));
if (childToParentCoordinates.Y < 0)
{
this.ChildGrid.Children.Remove(this.TextHeader);
this.ParentGrid.Children.Add(this.TextHeader);
Grid.SetRow(this.TextHeader, 0);
}
}
else
{
var generalTransform = RichText.TransformToVisual(ParentScroll);
var childToParentCoordinates = generalTransform.Transform(new Point(0, 0));
if (childToParentCoordinates.Y > 0)
{
this.ParentGrid.Children.Remove(this.TextHeader);
this.ChildGrid.Children.Add(this.TextHeader);
Grid.SetRow(this.TextHeader, 1);
}
}
There may be less-hacky ways to reach the same results, but this solution seems to work smoothly in the emulator.
I've found a working solution myself... the complete detail is available on my blog here... it contains also the link to my demo project on GitHub.
The trick was to get hold of the VerticallScrollBar inside the ScrollViewer and to set the ManipulationMode to Control to get enough feedback on the UI thread.
With the scroll offset information of the scrollbar we than animate the specific ui element we want to keep in view.

CollectionViewSource "Value does not fall within the expected range."

Why does this code produce the error in a Windows 8 XAML application?
Value does not fall within the expected range.
The XAML:
<SemanticZoom>
<SemanticZoom.ZoomedInView>
<ListView
Style="{StaticResource HorizontalListViewStyle}"
SelectionMode="None"
ScrollViewer.IsHorizontalScrollChainingEnabled="False"
ItemsSource="{Binding BoardItems}"
ItemContainerStyle="{StaticResource ZoomedOutListViewItemContainerStyle}"
...
The MVVM code:
ObservableCollection<WritingBoardModel> boards = new ObservableCollection<WritingBoardModel>();
... // Add item models to boards.
CollectionViewSource v = new CollectionViewSource()
{
Source = boards,
};
this.ViewModel.Add(BoardItemsViewModelKey, v);
If I skip the CollectionViewSource and directly add the boards instance to my view model, then it all works.
I think I need to use a CollectionViewSource in order to get some semantic zoom selection behaviour to work.
So, CollectionViewSources are weird and the way you have to bind to them is weird as well. To give you an example, in order to do it 'by the book' (the way the sample projects do), I've found it practically has to be a StaticResource as such:
<Page.Resource>
<CollectionViewSource Source="{Binding Whatev}"
x:Key="WhatevSource"/>
</Page.Resource>
<GridView ItemsSource="{Binding Source={StaticResource WhatevSource}}"/>
Notice that we're not setting the source directly to the CollectionViewSource, but we're setting a 'pathless' Binding, basically using the CollectionViewSource as a DataContext (just one way to think of it, not actually technically correct).
This is the only way I've been able to get it to work, though I believe you can technically in the codebehind set the ItemsSource directly to the View of the CollectionViewSource or something similar.
In your Listview add "StaticResource" and "Source"
<ListView ItemsSource="{Binding Source={StaticResource WhatevSource}}"/>
I needed to bind to the View property of the CollectionViewSource like this:
CollectionViewSource v = new CollectionViewSource()
{
IsSourceGrouped = false,
Source = boards,
};
this.ViewModel.Add(BoardItemsViewModelKey, v.View);
Mind you, this doesn't help with my two ListViews and keeping them in selection synch in a SemanticZoom.

ApplicationBar not working after navigation Windows 8

I am developing simple app on Windows 8.
I have two UserControls: Locations and LocationsMap.
I am trying to navigate between them. For that I have added to static methods into App. They are like this
public static void ShowLocationsMap()
{
var page = new LocationsMap();
Window.Current.Content = page;
}
Navigation works fine.
But there is a problem. I am calling this method from button in ApplicationBar. XAML looks like this
<ApplicationBar x:Name="BottomAppBar" Height="88" VerticalAlignment="Bottom" Style="{StaticResource AppBarStyle}" Grid.Row="1">
<StackPanel Orientation="Horizontal">
<!-- Margin="left,top,right,bottom" -->
<StackPanel Orientation="Vertical" Margin="5,14,5,14">
<Button Content="Map" Click="MapButton_Click"></Button>
</StackPanel>
</StackPanel>
</ApplicationBar>
And I am navigating back by calling other function from next page.
The problem is that when I navigates back, ApplicationBar stopping to work. It is not showing after right click. If I set BottomAppBar.IsOpen to true, it shows up, but didn't closing.
Where is the problem?
P.S.
ApplicationBar is not working as well in case when I am navigating to other page from button on controls XAML, so problem is not on button inside AppBar.
For navigation I was using sample code downloaded from internet. That code was using static functions defined in App class, which were changing Window.Current.Content.
That was bad idea. Instead if that I just need to use Frame navigation.
When using it we need to change type of our controls from UserControl to Page.
Application bar works great with navigation now.
Hope this will help someone.