UWP - GridView : how to add an "Add" button after the last item? - xaml

I use a GridView to display photos and I search an elegant way to allow user to add a new item to a form.
The form contains a lot of fields: it is displayed in a Pivot, where each PivotItem represents a category of the form.
Some categories contain one or more child items: they are displayed through a Master-Detail page.
It's in this page that I need to display a list of photos: as a photo represents a "sub sub item" of the form, I wouldn't manage the add of a new photo through the CommandBar. But I would like to use an "Add" button after the last item of the GridView containing the photos.
At this time I only found a solution that partially work:
Here is the XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Photos" Grid.Row="0"/>
<StackPanel Orientation="Horizontal" Grid.Row="1">
<GridView ItemsSource="{Binding images}">
<GridView.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Gray" BorderThickness="1"
Padding="10"
Height="150" Width="190">
<Image Stretch="UniformToFill"
Source="{Binding bitmap_image}" />
</Border>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
<Border BorderBrush="Gray" BorderThickness="1"
Padding="10"
Height="150" Width="190">
<Button Command="{Binding Path=DataContext.AddPhotoCommand, ElementName=DetailsPage}"
Height="100" Width="100">
<Viewbox>
<SymbolIcon Symbol="Add"/>
</Viewbox>
</Button>
</Border>
</StackPanel>
</Grid>
As I use a StackPanel, the Add button is no longer visible if I display 3 photos...
=> Is there a better way to do this? Or do you see a an alternative? I'm looking for doing this through a DataTemplateSelector, but that would require me to create a "false" object for displaying the add button...

As I use a StackPanel, the Add button is no longer visible if I display 3 photos...
If you don't mind the button is in the next line of your last photo, you can use WinRTXamlToolkit's WrapPanel instead of StackPanel to avoid the pictures goes out of the window and put the button inside the GridView's FooterTemplate:
Xaml:
<Page
x:Class="AddButtonSample.MainPage"
xmlns:controls="using:WinRTXamlToolkit.Controls"
...
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Photos" Grid.Row="0"/>
<controls:WrapPanel Orientation="Horizontal" Grid.Row="1">
<GridView ItemsSource="{Binding images}">
<GridView.FooterTemplate>
<DataTemplate>
<Button Command="{Binding Path=AddPhotoCommand}" Height="100" Width="100">
<Viewbox>
<SymbolIcon Symbol="Add"/>
</Viewbox>
</Button>
</DataTemplate>
</GridView.FooterTemplate>
<GridView.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Gray" BorderThickness="1"
Padding="10"
Height="150" Width="190">
<Image Stretch="UniformToFill"
Source="{Binding bitmap_image}" />
</Border>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</controls:WrapPanel>
</Grid>
Result:
If you really want to put the Button side by side after the last item of GridView. The only Option is DataTemplateSelector.

The best solution might be to use a CommandBar and put the "Add" button in the very bottom of the panel, as that would be most consistent with UWP design guidelines. GridView also has a FooterTemplate, which allows you to add some XAML in the footer of the whole GridView (but not directly after the items).
If you still want to have the add item as part of the GridView contents, you will really need to use the fake item and a DataTemplateSelector. This solution is not very clean, but probably is the only simple way.

I tried to do something similar for my own app too, but there really isn't an obvious way to achieve it. A little background on what my app does: it's a flashcard app that displays decks of card in a gridview on the homepage, with an add button being at the front of the gridview. This is what I did:
for my deck class, I gave it a bool attribute isButton
in the observablecollection of decks, set the first item's isButton to true
make two datatemplates for the gridview (deck and button) and make a data template picker for the gridview, and check the isButton attribute
if isButton is true, the template picker will use the button template
otherwise use deck template

Related

uwp xbox app XYNavigation in Pivot Control

I am an experienced uwp developer but a beginner for uwp xbox platform. I am trying to set the XY Navigation for my app and trying to test it with keyboard (as I don't own a xbox myself).
I am using a Pivot view and I can easily navigate between the pivot items with right and left arrow keys, which makes sense. but when my settings page is selected with pivot option (settings pivot header is focused and settings pivot item is in view) then I try to shift my focus vertically downwards to the first control in the settings page (radio buttons) but I am not able to do it the focus remains on settings header and doesn't shift downward on the page.
So how can I shift the focus downwards from a pivot header to the 1st control within the page on pressing down, and vice versa i.e : when 1st control is focused I should move up to go back to the header of the pivot of that page, because I think that is the traditional navigation with pivot control on uwp xbox right?
Secondly the docs and the xbox app dev videos I watched recommended to set the focus on an element which makes sense, when the app loads, should that be done with this.Focus() method or is there a more efficient way to do it with xaml?
Code:
Pivot.xaml
<Grid x:Name="MainGrid">
<Pivot x:Uid="PivotPage" x:Name="MainPivot" >
<PivotItem x:Uid="PivotItem_OnNow">
<Frame>
<views:OnNowPage/>
</Frame>
</PivotItem>
<PivotItem x:Uid="PivotItem_Guide">
<Frame>
<views:GuidePage/>
</Frame>
</PivotItem>
<PivotItem x:Uid="PivotItem_Settings">
<Frame>
<views:SettingsPage/>
</Frame>
</PivotItem>
</Pivot>
</Grid>
Settings.xaml
<Grid>
<Grid Margin="{StaticResource MediumLeftRightMargin}">
<Grid.RowDefinitions>
<RowDefinition Height="48"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0"
x:Uid="Settings_Title"
x:Name="TitlePage"
Style="{StaticResource PageTitleStyle}" />
<StackPanel Grid.Row="1">
<TextBlock
x:Uid="Settings_Personalization"
Style="{StaticResource SubtitleTextBlockStyle}" />
<StackPanel Margin="{StaticResource SettingsSubheaderMargin}">
<TextBlock
x:Uid="Settings_Theme"
Style="{StaticResource BodyTextStyle}" />
<StackPanel Margin="{StaticResource EightTopMargin}">
<RadioButton
x:Uid="Settings_Theme_Light"
GroupName="AppTheme"
IsChecked="{x:Bind ViewModel.ElementTheme, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter=Light, Mode=OneWay}"
Command="{x:Bind ViewModel.SwitchThemeCommand}">
<RadioButton.CommandParameter>
<xaml:ElementTheme>Light</xaml:ElementTheme>
</RadioButton.CommandParameter>
</RadioButton>
<RadioButton
x:Uid="Settings_Theme_Dark"
GroupName="AppTheme"
IsChecked="{x:Bind ViewModel.ElementTheme, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter=Dark, Mode=OneWay}"
Command="{x:Bind ViewModel.SwitchThemeCommand}">
<RadioButton.CommandParameter>
<xaml:ElementTheme>Dark</xaml:ElementTheme>
</RadioButton.CommandParameter>
</RadioButton>
<RadioButton
x:Uid="Settings_Theme_Default"
GroupName="AppTheme"
IsChecked="{x:Bind ViewModel.ElementTheme, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter=Default, Mode=OneWay}"
Command="{x:Bind ViewModel.SwitchThemeCommand}">
<RadioButton.CommandParameter>
<xaml:ElementTheme>Default</xaml:ElementTheme>
</RadioButton.CommandParameter>
</RadioButton>
</StackPanel>
</StackPanel>
<TextBlock
x:Uid="Settings_About"
Style="{StaticResource SubtitleTextBlockStyle}"/>
<StackPanel Margin="{StaticResource EightTopMargin}">
<TextBlock
Text="{x:Bind ViewModel.VersionDescription, Mode=OneWay}" />
<TextBlock
x:Uid="Settings_AboutDescription"
Margin="{StaticResource EightTopMargin}" />
<HyperlinkButton
x:Uid="Settings_PrivacyTermsLink"
Margin="{StaticResource EightTopMargin}" />
</StackPanel>
</StackPanel>
</Grid>
</Grid>
The MSDN has listed several scenarios that XY navigation might not work the way you expect:
The IsTabStop or Visibility property is set wrong.
The control getting focus is actually bigger than you think—XY navigation looks at the total size of the control (ActualWidth and ActualHeight), not just the portion of the control that renders something interesting.
One focusable control is on top of another—XY navigation doesn't support controls that are overlapped.
If XY navigation is still not working the way you expect after fixing these issues, you can manually point to the element that you want to get focus using the method described in Overriding the default navigation.
Please first check these scenarios, after that, if you still could not solve this issue. Please provide a Minimal, Complete, and Verifiable example. I will help you diagnose it on my side.

How do I enable Scrollbars on a UWP GridView

I have a UWP where I am loading from an XML file and showing it in a GridView and I am trying to enable Scrollbars in a way that allows me to stack and wrap items in all the available space like in the image below. The problem that I am having is that I cannot figure out how to enable the scrollbars so that I can scroll the boxes until I get to the end of the list.
So far I have got it to do what you see in the picture, which is wrapped the way I want but it fills all the available space and doesn't allow you to scroll vertically or horizontally (I only want to scroll one way but I have tried to see if I could go either way). Through a lot of trial and error I was able to get it to scroll one row or one column at a time to the end of the list but that is not the desired result either. Here is where I am with the XAML right now (trimmed down version of the screen shot).
<GridView x:Name="DataGrid1">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid Orientation="Horizontal"
ScrollViewer.HorizontalScrollBarVisibility="Visible"
ScrollViewer.VerticalScrollBarVisibility="Disabled" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.ItemTemplate>
<DataTemplate>
<Border Width="270"
Height="200"
Margin="5"
BorderBrush="Black"
BorderThickness="2">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition Width="100*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="2"
Background="#87CEFA">
<TextBlock Margin="2"
HorizontalAlignment="Center"
FontSize="16"
FontWeight="Bold"
Text="{Binding Company}" />
</StackPanel>
<TextBlock Grid.Row="1"
Grid.Column="0"
Margin="2"
HorizontalAlignment="Right"
FontWeight="Bold"
Text="Code: " />
<TextBlock Grid.Row="1"
Grid.Column="1"
Margin="2"
Text="{Binding Code}" />
</Grid>
</Border>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
So what do I need to do to enable the scrollbars the way that I want?
Make sure your GridView is in a Grid and not a StackPanel. It does not expand in a StackPanel.
To make it scroll in a StackPanel you have to specify the height of the GridView. This was the issue with mine :)
To my knowledge gridviews that are not showing scrollbars automatically are due to stackpanel's presence. So my solution here is to try remove stackpanel what so ever, and if I find the stackpanel that's responsible replace it with other kind of panel and work my way back up. It's totally a brute force kind of approach but it works most of the time.
And another piece of advice. In that process of replacing the stackpanel try to replace it with grid and try to divide it's rows and columns with widths and heights set to auto or star sizing instead of specifying it with actual numbers to see if it works this way. If it works then work your way up speicifying it with actual numbers.
Here's your problem, in the definition of the ItemsWrapGrid you have:
ScrollViewer.VerticalScrollBarVisibility="Disabled"
this is going to mean that even if the scrollbar is shown it wont work.
Remove this line and you should get a working scrollbar.

Change GridView's SelectedItem while selecting datatemplate control in phone application

The GridView in MainPage.xaml binds to an ObservableCollection of Employee. And the Employee class has got Amount(double) property that I want to edit through a TextBox. And finally when the text is entered I want to do some operation on the remaining Employee objects. I am able to get at the edited object via the INotifyPropetyChanged/PropertyChanged. But I think I cannot perform the operation here since it will trigger a cyclic PropertyChange for each of the object's update that I may perform on the other objects? Ideally, I should rely on the TextChanged event of the TextBox to do this.
The problem that I face is I am not able to get the edited object selected against the GridView's SelectedItem(SelectedEmployee). I can manage to get it selected only if I click/tap it outside the TextBox and within the row but not when I click directly in the TextBox. I wonder there is a way to trigger/update the GridView's SelectedItem when the TextBox is directly tapped?
Below my MainPage.xaml
<storeApps:VisualStateAwarePage
x:Class="SimpleCurrency.Views.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:SimpleCurrency.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:storeApps="using:Microsoft.Practices.Prism.StoreApps"
xmlns:mvvm="using:Microsoft.Practices.Prism.Mvvm"
mc:Ignorable="d"
mvvm:ViewModelLocator.AutoWireViewModel="True"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<GridView Margin="12,20,12,0" ItemsSource="{Binding Employees}" x:Name="grdEmployees"
ItemTemplate="{StaticResource EmployeeGridTemplate}" SelectedItem="{Binding SelectedEmployee, Mode=TwoWay}">
</GridView>
And the DataTemplate EmployeeTemplate
<DataTemplate x:Key="EmployeeGridTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ComboBox Width="120" ItemsSource="{Binding DataContext.Departments, ElementName=grdEmployees, Mode=TwoWay}" DisplayMemberPath="Code" SelectedValuePath="Code"/>
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Path=Amount, Mode=TwoWay}" InputScope="Number">
<!--TODO: Need to get at text chagned event-->
<interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="TextChanged">
<Core:InvokeCommandAction Command="{Binding DataContext.ConvertCommand, ElementName=grdConversions}"
CommandParameter="{Binding }"/>
</Core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</TextBox>
</Grid>
</DataTemplate>
Open to any suggestion/workaround
The tap event of the TextBox is preventing the GridView from noticing the tap event because it is "below" the TextBox tap area.
You should be able to determine which employee is bound to your TextBox (Databinding Context) and use this information to programmatically set the selected item of your GridView to the Employee Instance.

Windows phone 8 - Pivot Active, Inactive Header template

I'd like to use different templates for the header of my pivot, if it's selected/active id like to use template A, if it's inactive/not selected, I'd like to use template B.
What I would like to do is pretty much the same as the facebook app for windows phone, where a small triangle/arrow is displayed on the active pivot. I'm not 100% sure how this is designed, if the icon and arrow is the header template, or if it's just the arrow, or if it's pivot at all...
I would appreciate if someone would shed some light on how this kind of UI can be achieved.
Ended up with a work-around for this
I removed the pivot header entirely and created a stand alone control to navigate the pivot
The nav-viewmodel, shared between the pivot and the Nav control, have a SelectedPage property, which changes when you navigate in the Nav control:
<AppControls:Nav Grid.Row="0"/>
That gives me full control of the layout of the selected menu item.
The SelectedPage property is bound to the pivots selectedindex:
full markup:
<Grid x:Name="LayoutRoot" DataContext="{Binding Nav, Source={StaticResource Locator}}" Background="#E8E8E8">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<!--ContentPanel - place additional content here-->
<AppControls:Nav Grid.Row="0"/>
<phone:Pivot SelectedIndex="{Binding SelectedPage, Mode=TwoWay}" Grid.Row="1" x:Name="MainGui">
<phone:PivotItem x:Name="FrontPage">
<AppControls:Topnav Grid.Row="1" />
</phone:PivotItem>
<phone:PivotItem x:Name="PlacesPage">
<AppControls:PlacesControl Grid.Row="1"/>
</phone:PivotItem>
<phone:PivotItem x:Name="MapPage">
<AppControls:MapControl Grid.Row="1"/>
</phone:PivotItem>
<phone:PivotItem x:Name="SettingsPage">
<AppControls:Settings Grid.Row="1" />
</phone:PivotItem>
</phone:Pivot>
</Grid>

Biding with Pivot control on Windows Phone 8 with MVVM

I finally got my pivot control to work using MVVM in a wp8 app but I still have a question in regards to binding as thought as it works, and I could accept it as is, I'm not happy with the outcome and I'm trying to understand why this is happening. My DataContext, MainViewModel, contains multiple other ViewModels.
Scenario 1:
If I define the DataContext in the Grid (layout), and I assign the itemsSource for the pivot headers to QuickSearchTabs ViewModel and this get built ok but the listbox I have defined inside the pivotitem doesn't which is assigned the QuickSearchButtons ViewModel doesn't get built. Here is the xaml code:
<Grid x:Name="LayoutRoot" Background="Transparent" DataContext="{StaticResource MainViewModel}" >
<phone:Pivot x:Name="Pivot" ItemsSource="{Binding QuickSearchTabs}" FontSize="{StaticResource PhoneFontSizeSmall}" SelectedIndex="{Binding SelectedSearchTabIndex, Mode=TwoWay}">
<phone:Pivot.Title>
<TextBlock Text="My Search Options" />
</phone:Pivot.Title>
<phone:Pivot.HeaderTemplate>
<DataTemplate>
<ContentControl Content="{Binding Name}" />
</DataTemplate>
</phone:Pivot.HeaderTemplate>
<phone:Pivot.ItemTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding QuickSearchButtons}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Content="{Binding Name}" Grid.Row="0">
</Button>
<TextBlock Text="{Binding Description}" Grid.Row="1">
</TextBlock>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</phone:Pivot.ItemTemplate>
</phone:Pivot>
</Grid>
Scenario 2:
If I define the DataContext in the Grid (layout) and define the same DataContext within the listbox tags, it will build my header and my listbox BUT it will call my viewModel which is assigned to the ItemsSource of the listbox, multiple times. To be exact, it will call it the same number of time as the number of pivots I have. Here is the xaml code:
<Grid x:Name="LayoutRoot" Background="Transparent" DataContext="{StaticResource CriteriaViewModel}" >
<phone:Pivot x:Name="Pivot" ItemsSource="{Binding QuickSearchTabs}" SelectedIndex="{Binding SelectedSearchTabIndex, Mode=TwoWay}" >
<phone:Pivot.Title>
<TextBlock Text="My Search Options" />
</phone:Pivot.Title>
<phone:Pivot.HeaderTemplate>
<DataTemplate>
<ContentControl Content="{Binding Name}"/>
</DataTemplate>
</phone:Pivot.HeaderTemplate>
<phone:Pivot.ItemTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding QuickSearchButtons}" DataContext="{StaticResource CriteriaViewModel}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Content="{Binding Name}" Grid.Row="0">
</Button>
<TextBlock Text="{Binding Description}" Grid.Row="1">
</TextBlock>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</phone:Pivot.ItemTemplate>
</phone:Pivot>
</Grid>
As mentioned, this works, and it's not affecting me in anyway as the correct data is always displayed.
I can somehow see what's happening but why on earth would the ItemsSource be set for each of the defined pivot headers. Surely, the only important one is the one coming into visibility!
I don't know if Pivots are suppose to be used the way I'm using them. It seems, from what I've seen so far that normally a view is assigned to each PivotItem. This is not how I want my solution to work!
I just want numerous headers which are used to groups things in a specific manner and whatever is displayed under each is build dynamically but on the same view i.e. list of buttons and label.
Any ideas on how I could get scenario 1) to work and if I'm stuck with scenario 2, how to stop it from being triggered based on the number of pivot header items?
Thanks.
Problem solved!
The QuickSearchTabs was an observable collection of QuickSearchTab when it should have been an observable collection of ViewModel i.e. QuickSearchTabViewModel and within this viewModel, it will load the observable collection of relevant QuickSearchButtons for each of the tab.
Having a QuickSearchTabViewModel provides more flexibility and it will allow access to the current tab (header), and other relevant properties including everything maintain within each of these tabs such as, in my case the buttons.
Hope this helps.