Scrolling a ListView - screen height detection needed - xaml

I am working on a WinRT app which contains a listview. The Listview has grown recently and I need to put a vertical scrollbar around it.
So far I have hardcoded the height in the Grid to 500.
However I want to know how to set the height to detect how much space is available. This may vary depending on the device being used. How do I do that?
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="500"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal">
<TextBlock Text="*" FontSize="40" FontWeight="Bold" Foreground="Red"/>
<TextBlock Text=" = Required " FontSize="20"/>
</StackPanel>
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" >
<ListView
ItemsSource="{Binding Path=Survey.SelectedSection.Questions, Mode=TwoWay}"
IsSwipeEnabled="False"
SelectionMode="None"
Background="White"
ItemTemplateSelector="{StaticResource ResourceKey=QuestionDisplay}"
ItemContainerStyle=
"{StaticResource ResourceKey=QuestionListViewItemContainerStyle}" />
</ScrollViewer>
</Grid>

So like we discussed. ListView has a ScrollViewer alread embedded in its template. The reason it would invoke scrolling with a fixed height as opposed to naturally was a fixed boundary was provided to invoke it.
By adjusting your layout so that it's parent panel (and up the parent tree) didn't include StackPanel and using Grid instead with star * sizing it allowed a boundary to invoke it as desired. The reason for this is whereas StackPanel will only consume the space required, regardless of the space available. A Grid will consume whatever space is provided while restricting its children in its layout providing that boundary necessary to invoke the scrolling of the embedded ScrollViewer which has the attached property of ScrollViewer.VerticalScrollBarVisibility set to Auto by default.
Glad you found your fix, welcome to the wonderful world of XAML which once you get used to it, you'll generally find is a lot easier to work with than it's cousin HTML. Cheers :)

Related

ScrollViewer not scrolling when Height set to Auto or VerticalAlignment set to Stretch

I'm trying to allow vertical scrolling on a list of settings, using an ItemsControl inside of a ScrollViewer. The scrolling works when I set the Height of the ScrollViewer to a constant like 400, but stops working when I set the height to auto or set the VerticalAlignment to stretch.
Here's my code
<ScrollViewer VerticalAlignment="Stretch" >
<ItemsControl ItemsSource="{x:Bind _settingsViewModel.Settings}"
ItemTemplate="{StaticResource SettingTemplate}"/>
</ScrollViewer>
How can I set the Height of the ScrollViewer to fit the size of the screen but still allow scrolling?
Edit:
I should also mention that my page exists as a content page inside of a NavigationView.
Here is everything inside my navigation view:
<StackPanel>
<breadcrumb:BreadcrumbControl
x:Name="breadcrumbTrail"
DisplayMemberPath="Title"
HomeText="Home"
Seperator="/"
OverFlow="..."
HomeSelected="breadcrumbTrail_HomeSelected"
/>
<Frame x:Name="ContentFrame" Margin="24" Navigated="Frame_Navigated">
<Frame.ContentTransitions>
<TransitionCollection>
<NavigationThemeTransition/>
</TransitionCollection>
</Frame.ContentTransitions>
</Frame>
</StackPanel>
According to this post a ScrollView cannot exist inside of a stack panel without directly setting the height of the of the ScrollView. Since this ScrollView exists inside of a Frame that is nested inside of a StackPanel you will run into the issue described.
You could use a Grid instead of a StackPanel to achieve the effect you are looking for.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="25"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<breadcrumb:BreadcrumbControl
x:Name="breadcrumbTrail"
DisplayMemberPath="Title"
HomeText="Home"
Seperator="/"
OverFlow="..."
HomeSelected="breadcrumbTrail_HomeSelected"
Grid.Row="0"
/>
<Frame x:Name="ContentFrame" Margin="24" Navigated="Frame_Navigated" Grid.Row="1">
<Frame.ContentTransitions>
<TransitionCollection>
<NavigationThemeTransition/>
</TransitionCollection>
</Frame.ContentTransitions>
</Frame>
</Grid>
The problem is that you need to cap the dimension the ScrollViewer object can occupy!
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
So what did we define above?
Defined a parent layout container of type Grid.
The Grid layout container has 3 rows, with the middle row taking as much space as required, while the first and last will have the real estate leftovers divided equally between them.
Now we decide to define the ScrollViewer inside the middle row.
This is a problem! You have defined your ScrollViewer inside a row which will define it's height according to the content that it holds.
This definitely goes against the concept of a ScrollViewer! How can you scroll something, if you are setting it to take as much space as required to hold all of its children? Even if the size doesn't go "over" the screen size, it will still not be possible to actually scroll through anything since you have not set a maximum height for the ScrollViewer, and therefore all the content can actually be displayed without any scrolling at all.
The problem can get even worse, when the content inside the ScrollViewer occupies so much real estate, that it will eat all the space you would want to have for the other layout areas. And in this worse situation, it is still not possible to scroll through anything, despite existing layout which is not being shown to us ...
How can we resolve this? Either by setting the MaxHeight dependency property of your ScrollViewer, as you have already done, or by changing the definition of the layout container.
Now, I will simply set the middle row to have 2* times the size of the other 2 rows (now there exists a limitation on the size of the ScrollViewer):
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
Now we can simply define the ScrollViewer as part of the middle row, like this:
<ScrollViewer Grid.Row="1" ...>
If the content insides it occupies more than the calculate *2** size, you will see the ScrollViewer scrolling in action, just like you want to!
Just as a minor suggestion, I generally like to define some of the Attached Properties defined in the ScrollViewer class, such as the ScrollViewer.VerticalScrollBarVisibility and the ScrollViewer.VerticalScrollMode in the definition of the object (for focused the horizontal scroll behavior, we have the same logic but for the attached properties HorizontalScrollMode and HorizontalScrollBarVisibility)
In this case I would define them as Attributes of the ScrollViewer, rather than an attached property defined in the children of the ScrollViewer, simply because it becomes easier to understand the developer intentions and i prefer the Auto definition for the ScrollMode behavior.
<ScrollViewer Grid.Row="1"
VerticalScrollBarVisibility="Visible"
VerticalScrollMode="Auto"> ... </ScrollViewer>

Did ScrollViewer change between 8.1 and 10?

I have a Windows 8.1 Store app I am moving to 10 as a Universal app. On 8.1 it works fine. On 10 (both Windows and Phone) it will not scroll. You can see a viewport worth of entries, but it stubbornly refuses to show a scrollbar on mouseover, move if you drag with your finger on a touch screen, scroll when I use the down arrow key to move from item to item, or respond to mouse wheel if there's a mouse - all of which the 8.1 version does.
Has something happened between 8.1 and 10 that might cause this?
There are a number of ScrollViewers in the system and they all do this. Here is one, implicitly on a gridview:
<GridView
x:Name="itemListViewSnapped"
AutomationProperties.AutomationId="ItemListView"
AutomationProperties.Name="Grouped Items"
Grid.Row="5"
Margin="20,5,10,0"
Padding="10,0,0,60"
Background="{StaticResource TimeBucketsSections}"
ItemsSource="{Binding Source={StaticResource ItemsViewSource}}"
ItemTemplate="{StaticResource Bucket80ItemTemplate}"
SelectionMode="Single"
SelectedItem="{Binding SelectedBucketViewModel}"
SelectionChanged="ItemGridViewSelectionChanged"
IsItemClickEnabled="True"
ItemClick="ItemView_ItemClick"
Visibility="{Binding BucketsVisable}"
ScrollViewer.HorizontalScrollMode="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.VerticalScrollMode="Enabled"
ScrollViewer.BringIntoViewOnFocusChange="False">
I'm leaving all the binding and background and whatnot in there on the offchance they're relevant. The ScrollViewer. parts are what matter, imo.
Not sure this will help but I have had issues with ScrollViewer when in a GridView. I usually wrap the element in a ScrollViewer and turn the one in the GridView off for more stable behaviour.
<ScrollViewer ScrollViewer.HorizontalScrollMode="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.VerticalScrollMode="Enabled">
<GridView ScrollViewer.HorizontalScrollMode="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollMode="Disabled">
</GridView>
</ScrollViewer>

Windows Phone App - How to make text wrapping depending on device width

In my app I have a list box that contains a stack panel with text block items. The text block items have a text wrapping or text trimming property to avoid that the text block items slide outside the visible range.
As far as I know the text wrapping and text trimming property need a fixed width to insert a line break. For this reason, I set a fixed width for the title (Width="456") and for the description (Width="432"):
<ListBox x:Name="CategoryList" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,0,0,17">
<StackPanel>
<TextBlock
Style="{StaticResource PhoneTextLargeStyle}"
Text="{Binding Name}"
TextTrimming="WordEllipsis"
Width="456"
/>
<TextBlock
Style="{StaticResource PhoneTextSubtleStyle}"
Margin="12,-6,12,0"
Text="{Binding ContentDescription}"
TextWrapping="Wrap"
Width="432"
/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The problem now is that when I turn the phone, the fixed width for the horizontal mode is too small. Is there a way to put in place of the fixed width a width depending on the device width?
Screenshot:
in general its never a good idea in XAML to use fixed width/height (for "whole" page(100%)) since XAML was designed to do this job for you, so you might concentrate on your layout and the technology covers the aspect of different screens and sizes.
So, if you use a fixed width to display sth. on the whole page its a very good indicator that something is wrong.
In your case you've got the use of StackPanels wrong. StackPanels are designed in a way, that their size is not limited in the direction of their orientation. This means, a StackPanel that has got its orientation set to horizontal might grow indefinetely in width and a sp with orientation set to vertical wouldn't be limited in its height.
Now this leads us to your problem: you have used an indefintely-in-width-growing element in an indefinetely-in-height-growing element (two stackpanels).
Even though you might by some tricks limit their size (e.g. that could be achieved by some data bindings), this isn't what should be done.
I'd say - since in the parent StackPanel nothing is stacking - substitute it with some other panel/container (or just remove it) and your problem will be gone.
Example
<DataTemplate>
<!-- If you need more child elements uncomment the following line -->
<!-- <Grid> -->
<StackPanel Margin="0,0,0,17">
<TextBlock ... />
<!-- no need to set width on the following textblock -->
<TextBlock ... TextWrapping="Wrap" />
</StackPanel>
<!-- </Grid> -->
</DataTemplate>

How to rearrange WrapGrid contents when zoom level changes?

I am developing a Windows 8 Metro application whose layout is pretty simple. It consists of a single page with a WrapGrid enclosed in an ItemsControl, which is in turn enclosed in a ScrollViewer. This is the XAML code of the application main page:
<Page ...>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid x:Name="MainGrid" Margin="120,140,32,0">
<ScrollViewer x:Name="ScrollView"
VerticalScrollBarVisibility="Auto"
HorizontalAlignment="Stretch">
<ItemsControl x:Name="itemsControl" HorizontalAlignment="Stretch">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid Orientation="Horizontal"
HorizontalChildrenAlignment="Stretch"
Margin="0"
HorizontalAlignment="Center">
<WrapGrid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition />
<RepositionThemeTransition />
</TransitionCollection>
</WrapGrid.ChildrenTransitions>
</WrapGrid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
</Grid>
<Page.BottomAppBar>
...
</Page.BottomAppBar>
</Page>
There is also an user control of which new instances are created and added to the ItemsControl programmatically when the user clicks on a certain button in the application bar. As expected by the fact of using a WrapGrid, the control instances are stacked sequentially in a single row until there is no more room in the screen, at which point they appear in a new row and it is necessary to scroll down in order to see them. So far so good.
Now I want to implement a feature and I don't know how to achieve it. What I want is the following: when the user zooms out in the application, causing the controls to appear smaller, I want the new available space to be used so that more controls can be displayed per row; instead, the current behavior is that the ItemsControl itself is reduced and the extra surrounding space is unused.
For example, imagine that the user adds 10 controls. There is room for 4 controls in one row, so that 3 rows of controls are displayed, with 4, 4 and 2 controls. If the user zooms out and now there is room for 7 controls in a row, I want the ItemsControl to rearrange itself so that now there are only two rows with 7 and 3 controls. How could I achieve this?
I hope I have explained myself properly. Please don't hesitate to ask if my question is not clear enough. Thank you very much!

XAML: How to restrict a datagrid to its parents width

I have a [silverlight] WizardContainer control that hosts a number of wizard pages. The wizard fits nicely on its host form. If the page has narrow content it doesn't expand to fill the container. So I set HorizontalContentAlignment to Stretch. This works.
However if the wizard page contains a datagrid with lots of columns it stretches the page instead of autoscrolling itself - as its width is not fixed. If the following XAML is on a usercontrol with a width of 350 I want the grid to be 350 and have its own scrollbars. If the WizardContainer is made smaller than the page minwidth then the MainScroller should come into play.
<Grid x:Name="LayoutRoot" >
<ScrollViewer x:Name="MainScroller" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" >
<ContentControl Margin="4" x:Name="WizardContainer" HorizontalContentAlignment="Stretch">
<Grid Background="Red" x:Name="WizardPage" MinWidth="300">
<sdk:DataGrid HorizontalAlignment="Left" Height="120" >
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Width="150"/>
<sdk:DataGridTextColumn Width="150"/>
<sdk:DataGridTextColumn Width="150"/>
</sdk:DataGrid.Columns>
</sdk:DataGrid>
</Grid>
</ContentControl>
</ScrollViewer>
</Grid>
Note if I fix the width of the datagrid everything in this XAML works. But I want the grid to expand as the user resizes the form containing the wizardcontainer.
You have the DataGrid wrapped in ScrollViewer. This, effectively, tells the DataGrid that it has infinite available width. Since the DataGrid is not constrained, it'll take as much width as it's columns desire.
You can set HorizontalScrollBarVisibility="Disabled"if that fits your design (i.e. you need only vertical scrolling from your ScrollViewer). This will disable scrolling horizontally and will constrain the DataGrid on the horizontal axis.
DataGrid has a ScrollViewer in it's ControlTemplate. As a broad general rule: try avoiding a ScrollViewer-in-a-ScrollViewer situations. It's (almost) always a headache to debug and eventually you'll have to set something as a fixed size (or calculate the size on the fly).