Why do ListView.HeaderTemplate and ListView.ItemTemplate (UWP XAML) display differently even though I use the exact same XAML? - xaml

I have a ListView on my form and I'm setting up the ListView.HeaderTemplate and it looks like what I expect it to look like.
However, if I copy the exact Grid definition into my ListView.ItemTemplate it does not layout the same.
Altered Two Column Value Colors (Cyan and Green)
I have altered the color of the two Item column values so you can differentiate where each is showing up. I have two <x:String> values defined so data will show up in the preview layout of Visual Studio but when it runs the two layouts still look different even though they are defined the same.
No Grid.Row Defined On ItemTemplate TextBlocks
The one difference you'll notice in the XAML is that the ItemTemplate TextBlocks each only have its Grid.Column set (no Grid.Row set). That was a test and it looks the same either way.
Literally Copy / Pasted HeaderTemplate to ItemTemplate
I literally copied the HeaderTemplate to the ItemTemplate and made only the changes mentioned.
Why don't they layout the same?
<ListView.HeaderTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Margin="7 0 0 0" Grid.Row="0" Grid.Column="0" Foreground="#ff0000" Text="Date" />
<TextBlock Margin="0 0 15 0" Grid.Row="0" Grid.Column="1" Foreground="#ff0000" Text="Entry Count" />
</Grid>
</DataTemplate>
</ListView.HeaderTemplate>
EDIT
If I change the ItemTemplate ColumnDefinitions to look like the following:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
Then it does get closer to what I expect, but I'm still not sure why I have to do this differently.

By default, ListViewItem won't stretch its content. Set HorizontalContentAlignment as Stretch in ItemContainerStyle.
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>

Related

Xamarin.Forms. Optimization ListView

Can you give recommendations on how to optimize the ListView?
It slows down when scrolling.
My viewCell looks like this:
<Grid BackgroundColor="{Binding ListViewCustomizer.ItemBorderColor, Source={x:Static theme:ThemeManager.Theme}}" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<Grid Margin="0, 0, 0, 1"
BackgroundColor="{Binding ListViewCustomizer.ItemBackgroundColor, Source={x:Static theme:ThemeManager.Theme}}" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<Grid.Triggers>
<DataTrigger Binding="{Binding IsRead}" TargetType="Grid" Value="true">
<Setter Property="BackgroundColor" Value="#bfe3fa" />
</DataTrigger>
</Grid.Triggers>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="4*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="6*"/>
<RowDefinition Height="4*"/>
</Grid.RowDefinitions>
<ContentView Grid.RowSpan="2" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Margin="1, 1">
<ffimageloading:CachedImage x:Name="mainImage"
Source="news_placeholder.png"
LoadingPlaceholder="news_placeholder.png"
DownsampleToViewSize="false"
CacheDuration="{x:Static constant:ImageConfig.PreviewImageCacheDuration}"
ErrorPlaceholder="news_placeholder.png" Aspect="AspectFill"
HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
Transformations="{Binding IsRead, Converter={StaticResource BoolToTransformationConverter}}">
<ffimageloading:CachedImage.DownsampleHeight>
<extensions:OnDeviceType x:TypeArguments="x:Double" Phone="130" Tablet="200"/>
</ffimageloading:CachedImage.DownsampleHeight>
</ffimageloading:CachedImage>
</ContentView>
<Grid Grid.Column="1" RowSpacing="0" Margin="10,10,10,0">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label Style="{StaticResource BaseListItemLabelStyle}" FormattedText="{Binding ., Converter={StaticResource NewsItemToFormattedStringConveter}}"/>
<ctrl:ExtendedLabel
Grid.Row="1"
MultilineTrimming="True"
x:Name="content"
Style="{StaticResource ListItemContentLabelStyle}" />
</Grid>
</Grid>
</Grid>
Maybe something to replace something. Maybe should I replace all bindings with code-behind?
Help me please.
You packed quite a lot of stuff into that viewcell. Also changing data binding to code behind won't give you significant benefits performancewise.
What is happening here is that all of these elements need to run layout passes, which requires time (performance). Now multiply that with the amount of items in your ListView. Good news however is, that this multiplication factor will be in your favour once you improve your view to do less layouting.
What I would suggest:
1) Don't use "Auto" height definitions, since that will need multiple layout passes until the final height has been determined
2) Reduce the grids to one single grid and work with RowSpan and ColumnSpan properties to set your elements
3) Is there a reason why you are using a content view? If I remember correctly you should be able to put the ffimageloading.cachedimage directly into a grid.
For more information about how to optimize your xamarin forms layout performance, I would recomment this article: https://xamarininsider.com/2017/08/03/optimizing-layout-performance-in-xamarin-forms/

ListView gets not scrollable

I have a proble which seems pretty common and has been asked alot, but I can't find a fixing solution for my problem.
So I try to use 3 listviews in in one page all shall have a title and an explaining image, but instead of designing all 3 ListViews in one page I outsourced one listview with image and title into a control, which I use in my page.
The 3 Controls are placed in a grid. When the listview items get filled thy scrollbar should become visible if the remaining space is no longer enough but it won't show.
I provided a sandbox project where I placed the control and etc. like in the application I'm working on. SampleProject
Their you just need to press start and the listviews get filled. But they don't show the scrollbar.
Thanks in advance!
Edit 1:
As requested I share my code below. If you open up the sample project then you do not need to read further until a second edit is done.
Control containing listview:
<Grid>
<Grid x:Name="Section"
HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid x:Name="grdTitleArea"
HorizontalAlignment="Stretch"
Height="50">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<BitmapIcon
VerticalAlignment="Center"
HorizontalAlignment="Center"
Tapped="grdTitleArea_Tapped"
UriSource="ms-appx:///Assets/area.png"
Height="40" />
<TextBlock
VerticalAlignment="Center"
HorizontalAlignment="Stretch"
Tapped="grdTitleArea_Tapped"
Text="Area"
Grid.Column="1" />
</Grid>
<!--<ScrollViewer
VerticalAlignment="Stretch"
VerticalScrollBarVisibility="Auto"
VerticalScrollMode="Enabled"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
HorizontalScrollBarVisibility="Hidden"
HorizontalScrollMode="Disabled"
>-->
<ListView x:Name="ListView"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.VerticalScrollMode="Auto"
Grid.Row="1">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Grid
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
BorderThickness="1"
Margin="1"
Height="50">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding ActionDescription}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<!--</ScrollViewer>-->
</Grid>
</Grid>
Control which contains the control above 3 times:
<Grid x:Name="ProgressControl">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<local:SynchronizeSettingsControl
Visibility="Visible"
x:Name="Settings" />
<local:SynchronizeSectionControl
x:Name="ActualAction"
Visibility="Visible"
Grid.Row="1" />
<local:SynchronizeSectionControl
x:Name="Error"
Visibility="Visible"
Grid.Row="2" />
<local:SynchronizeSectionControl
x:Name="Log"
Visibility="Visible"
Grid.Row="3" />
</Grid>
Page which contains the control which contains the listview:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="50" />
<RowDefinition Height="*" />
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="1"
Text="Demo"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Width="70"
VerticalAlignment="Stretch"
Content="Useless Button" />
</Grid>
<Controls:SynchronizeControl
x:Name="ctlSync"
Grid.Row="2"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" />
<Button VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
x:Name="btnStart"
Content="Start"
Tapped="btnStart_Tapped"
Grid.Row="3" />
</Grid>
The problem is you used Auto height in the page. This means basically that the page tells the local:SynchronizeSectionControl control: "You can use whichever height you want".
The control then has * as the height of the second row which means "use the rest space available". But because the page offered essentially "infinite height", the ListView height will stretch as much as possible to accommodate for all its items and hence it doesn't scroll, as its height is big enough to display everything, although it is cut off and not visible, because the window height is of course limited.
The thing is you used the Auto property for the height of your rows in your control.
This works fine is the control you use uses a definite space. Like a button or similar stuff. But when the control can extend indefinitely the allocation for the space gets screwed up.
Basically the control displays at its maximum size but extends way over its boundaries.
You can prevent that when you use the * as a Height value.
This will lead to the control taking up all the space available. You can further limit this with using the MaxHeight property.
If you do it that way it will display a scrollviewer when necessary and it will even resize when you change the window size.

Canvas causes contentcontrol children to spill out over control

I have the following XAML:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="11" />
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<ListView ... Grid.Column="2"/>
<controls:GridSplitter
Grid.Column="1"
Width="11"
ResizeBehavior="BasedOnAlignment"
ResizeDirection="Auto"
Background="Gray"
Foreground="White"
FontSize="13">
<controls:GridSplitter.Element>
<Grid>
<TextBlock HorizontalAlignment="Center"
IsHitTestVisible="False"
VerticalAlignment="Center"
Text=""
Foreground="Black"
FontFamily="Segoe MDL2 Assets">
</TextBlock>
</Grid>
</controls:GridSplitter.Element>
</controls:GridSplitter>
<Canvas Canvas.ZIndex="1">
<ContentControl MaxWidth="750" Content="{Binding CAV, Mode=TwoWay}" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" />
</Canvas>
</Grid>
Without the Canvas wrapped around the ContentControl in the first column, the children of the ContentControl properly stay within the content control and I can make it wider to see more of the children horizontally.
When I add the Canvas and set the ZIndex, the children of the content control spill out over the gridsplitter and the ListView without respecting the width of the contentcontrol.
The effect I'm trying to get it to allow expanding the width of the content control with the grid splitter and having that content control expand "over" the listview (instead of reducing the width of the listview).
What am I missing? I'm confused as to why the width of the contentcontrol isn't being respected suddenly just because I wrap it in a Canvas. Or should I not be using Canvas to get the "overlay" effect I want?
Canvas doesn't stretch its children. It only gives them the space they need. In fact, you don't have to wrap your ContentControl inside a Canvas to set the Canvas.ZIndex; it is an attached property that can be attached to the ContentControl directly. If you want a panel that allows its children/child to stretch, try a Border(single child only) or a Grid.
However, setting the ZIndex here seems to be unnecessary to me. Since the GridSplitter simply resizes the width of the columns, there won't be any overlay happening here.
If you want the overlay effect, you need to create another Grid with the same column layout and place your ListView there, something like the following -
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="11" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Grid x:Name="ListView" Background="LightGreen" Grid.Column="2" />
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="11" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<controls:GridSplitter Grid.Column="1" Background="LightBlue">
</controls:GridSplitter>
<Grid x:Name="ContentControl" Background="LightPink" Opacity="0.5" />
</Grid>
</Grid>
As an addendum Canvas tells the layout tree it uses no space. It doesn't care about the size of it's children, despite however many children it might have. For reference, here's the Canvas' MeasureOverride:
protected override Size MeasureOverride(Size constraint)
{
Size childConstraint = new Size(Double.PositiveInfinity, Double.PositiveInfinity);
foreach (UIElement child in InternalChildren)
{
if (child == null) { continue; }
child.Measure(childConstraint);
}
return new Size();
}
Which effectively says "I'm not taking up any space and my children can use whatever space they want".

How do I prevent a column in Silverlight xaml Grid from taking up the entire row?

I want to display two columns in my Grid. The first is a textblock that is sometimes longer than the row meant to hold it. The second is a button. Is there a way to give the textblock as much room as possible while still leaving room for the button to go immediately after?
When I use the following code, the textblock will sometimes push the button outside the viewable area of the grid.
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Description}" HorizontalAlignment="Stretch" />
<Button Margin="4,0" Height="0" Width="16" Grid.Column="1" MinWidth="20" VerticalAlignment="Center" />
</Grid>
I've tried setting the first column definition Width="*", but then the button is always at the very end of the row, when I want it next to the text. Any suggestions?
This is in Silverlight 4.
Thanks.
EDIT
I want the grid to resize as the user changes the window size, so setting a hard limit on the grid size is no good. That being said, I was able to manually set the MaxWidth in the code behind when the TextBlock loads and when the window changes size. It's clunky, but it works.
Following will surely work..
<Grid x:Name="LayoutRoot" Background="White" Width="250">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="textBlock" Grid.Column="0" Text="Text Box" VerticalAlignment="Top"/>
<Button x:Name="button" Grid.Column="1" Content="Button" VerticalAlignment="Top" Width="60"/>
</Grid>
Add Following Line to xaml.cs file in constructor..
public MainPage()
{
InitializeComponent();
textBlock.MaxWidth = LayoutRoot.Width - button.Width;
}
Let me know if there is any issue with it.
Thanks..

How to get a TextBlock to right-align?

How do I get the TextBlock in my status bar below to align to the right?
I've told it to:
HorizontalAlignment="Right"
TextAlignment="Right"
but the text is still sitting unobediently on the left. What else do I have to say?
<Window x:Class="TestEvents124.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300"
MaxWidth="700" Width="700"
>
<DockPanel HorizontalAlignment="Stretch" Margin="0,0,0,0" Width="Auto">
<StatusBar Width="Auto" Height="25" Background="#888" DockPanel.Dock="Bottom" HorizontalAlignment="Stretch">
<TextBlock
Width="Auto"
Height="Auto"
Foreground="#fff"
Text="This is the footer."
HorizontalAlignment="Right"
TextAlignment="Right"
/>
</StatusBar>
<GroupBox DockPanel.Dock="Top" Height="Auto" Header="Main Content">
<WrapPanel Width="Auto" Height="Auto">
<TextBlock Width="Auto" Height="Auto" TextWrapping="Wrap" Padding="10">
This is an example of the content, it will be swapped out here.
</TextBlock>
</WrapPanel>
</GroupBox>
</DockPanel>
</Window>
I've had a play with your code and managed to make it look "right" (no pun intended) by using a StatusBarItem rather than a TextBlock:
<StatusBar Width="Auto" Height="25"
Background="#888" DockPanel.Dock="Bottom"
HorizontalAlignment="Stretch" >
<StatusBarItem Foreground="#fff"
HorizontalContentAlignment="Right">This is the footer</StatusBarItem>
</StatusBar>
Not sure what's happening with the TextBlock - all my experience says that some combination of HorizontalContentAlignment and HorizontalAlignment (on both the StatusBar and the TextBlock) should achieve what you want. Anyway - hopefully the StatusBarItem will work for you.
<StatusBar>
<StatusBar.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<StatusBarItem Grid.Column="0">
<TextBlock>something</TextBlock>
</StatusBarItem>
<Separator Grid.Column="1" />
<StatusBarItem Grid.Column="2">
<TextBlock>logged in</TextBlock>
</StatusBarItem>
</StatusBar>
This example won't mess up your Separator. Based on an example taken from http://kent-boogaart.com/blog/the-perfect-wpf-statusbar
You shouldn't put a Separator in a StatusBarItem, it will reduce your separator to a dot.
For anyone who is looking for the answer to the question in the title (not necessarily for use in a status bar), I found a Label to be better than a TextBlock for having control over alignment and still feeling semantically correct.
Seems this is still an issue. The problem is that a TextBlock's width is automatically set based on its content. To verify this just set the Background property to another color. Setting the HorizontalAlignment to Stretch doesn't help.
Fortunately, most of my properties are set in code (MVPVM) and my TextBlocks are contained in a Panel and I was able to set the TextBlock's width property to its Parent width. i.e tb.Width = tb.Parent.Width, then Right alignment worked.