Column in UWP DataTemplate not stretching - c#

I'm writing a UWP app to track TV shows watched/purchased/streamed etc and
am going absolutely crazy trying to get grid columns inside a DataTempate to stretch their width as it seems there is a bug in XAML which ignores the * width definition. I need the first column in the ListView (the show Title) to take up the remaining space (hence the column definition = "*") and while it will do that in the HeaderTemplate it absolutely refuses to do it inside the DataTemplate so the whole grid just ends up being all wonky and out of alignment as the Title column only uses the space it needs on each line.
My XAML is below - in the ItemTemplate DataTemplate template I am binding to an instance of an object called TVShow which is in an observable collection in my main view model. (I have not included the ViewModel or TVShow class definition here as I know this is a purely XAML issue).
The only thing that worked so far is having an extra property in my TVShow class that stores the correct width of the column (by subtracting the widths of the other three columns from the grid size (fetched in the view code behind) but this causes the whole list to reformat itself after initally displaying which looks ugly, not to mention awful programming.
So I'm looking for ideas on how to solve this - I could move the property for the correct column width in the main viewmodel but then how do I bind to that in the template given I am binding to "TVShow"? Or do I have to take the content out of the DataTemplate and put in a UserControl? I have wasted so much time on something that is so ridiculously simple - this bug seems to have been around since WPF so why haven't MS ever fixed this - very frustrating.
<HubSection Name="hsShows" Width="{Binding HubSectionWidth}" MinWidth="430" MaxWidth="640"
VerticalAlignment="Top" HorizontalAlignment="Stretch" Background="{StaticResource Dark}" >
<HubSection.Header>
<TextBlock Text="Shows" TextLineBounds="TrimToBaseline" OpticalMarginAlignment="TrimSideBearings"
FontSize="24" Foreground="{StaticResource Light}"/>
</HubSection.Header>
<DataTemplate x:DataType="local:MainPage">
<ListView Name="lvwShows"
Width="{Binding HubSectionGridWidth}"
Grid.Row="0"
Foreground="{StaticResource Light}"
Background="{StaticResource Dark}"
Margin="-14,20,0,0"
Loaded="lvwShows_Loaded"
ItemsSource="{Binding AllShows}"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsSwipeEnabled="True"
IsItemClickEnabled="True"
SelectedItem="{Binding SelectedTVShow, Mode=TwoWay}"
SelectionMode="Single"
ScrollViewer.VerticalScrollMode="Enabled"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.HeaderTemplate>
<DataTemplate>
<Grid Width="{Binding HubSectionGridWidth}" Height="Auto" Background="DarkGreen" Margin="15,5,5,5" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="80"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Title" FontSize="16" FontWeight="Bold" Foreground="{StaticResource Bright}"
VerticalAlignment="Bottom" HorizontalAlignment="Left"
Tag="TITLE,ASC" Tapped="ShowsGridHeading_Tapped"/>
<TextBlock Grid.Column="1" Text="Seasons" FontSize="16" FontWeight="Bold" Foreground="{StaticResource Bright}"
VerticalAlignment="Bottom" HorizontalAlignment="Center"
Tag="SEASONS,ASC" Tapped="ShowsGridHeading_Tapped"/>
<TextBlock Grid.Column="2" Text="Last Watched" FontSize="16" FontWeight="Bold" Foreground="{StaticResource Bright}"
VerticalAlignment="Bottom" HorizontalAlignment="Center" TextAlignment="Center" TextWrapping="Wrap"
Tag="WATCHED,ASC" Tapped="ShowsGridHeading_Tapped"/>
<TextBlock Grid.Column="3" Text="Last Episode" FontSize="16" FontWeight="Bold" Foreground="{StaticResource Bright}"
VerticalAlignment="Bottom" HorizontalAlignment="Center" TextAlignment="Center" TextWrapping="Wrap"
Tag="EPISODE,ASC" Tapped="ShowsGridHeading_Tapped"/>
</Grid>
</DataTemplate>
</ListView.HeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate x:DataType="model:TVShow">
<Grid Height="Auto" MinWidth="410" MaxWidth="640" Background="Blue" HorizontalAlignment="Stretch" RightTapped="ShowsList_RightTapped">
<FlyoutBase.AttachedFlyout>
<MenuFlyout Placement="Bottom">
<MenuFlyoutItem x:Name="UpdateButton" Text="Update from TVMaze" Click="FlyoutUpdateButton_Click"/>
<MenuFlyoutItem x:Name="RefreshButton" Text="Refresh" Click="FlyoutRefreshButton_Click"/>
<MenuFlyoutItem x:Name="DeleteButton" Text="Delete Show" Click="FlyoutDeleteButton_Click"/>
</MenuFlyout>
</FlyoutBase.AttachedFlyout>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="80"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="{x:Bind Title}" Foreground="{StaticResource Light}"
VerticalAlignment="Center" HorizontalAlignment="Stretch" TextWrapping="Wrap" />
<TextBlock Grid.Row="0" Grid.Column="1" Text="{x:Bind Seasons}" Foreground="{StaticResource Light}"
VerticalAlignment="Center" HorizontalAlignment="Center"/>
<TextBlock Grid.Row="0" Grid.Column="2" Foreground="{StaticResource Light}"
Text="{x:Bind LastWatchedDate, Mode=OneWay, Converter={StaticResource DateTimeFormatConverter}, ConverterParameter='{}{0:dd/MM/yyy HH\\\\:mm}'}"
VerticalAlignment="Center" HorizontalAlignment="Center"/>
<TextBlock Grid.Row="0" Grid.Column="3" Text="{Binding LastWatchedEpisodeRef}" Foreground="{StaticResource Light}"
VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</HubSection>

Ok, so I ended up adding this XAML into my ListViews (though I know I could have done what Grace suggested but I just find Blend horrific to use) - it was the HorizontalContentAlignment that actually did the trick!
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>

If you have Correct width in viewmodel you can Bind it like this
Width={Binding ElementName = ListViewname,Path=DataContext.width}

This problem is caused by the default template of the ListViewItem, to make the Grid stretch inside of the items, you can open the Document Outline label => find your ListView control and right click on it, then choose Edit Additional Templates => select Edit Generated Item Container (ItemContainerStyle), and at last Edit a Copy.
Then you will find this template in your Page resources, please change the code:
<Setter Property="HorizontalContentAlignment" Value="Left" />
To:
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
Then your problem can be solved.
I saw you've more then one ListView, if you want this style target all the ListView in this page, you can remove the x:Key attribute of this template and remove the ItemContainerStyle with the StaticResource which is generated by the action upper.
Don't be frustrating, I think you have developed WPF before, it's easy to learn UWP. Editing the template or the styles of the controls can solve many layout problem, here is some default templates and styles of different controls, you may take a look next time you have such problem.
If you have questions about how to develop an UWP app, you can refer to Develop UWP apps, and if you have some problems with the APIs, you may refer to Reference for Universal Windows apps.
If you need help or suggestion, you may ask question here, people here are glad to help.

Related

How to add a separator to the Expander component in UWP app?

I'm looking for a way of implementing this kind of separator to the expander component in UWP app:
Any ideas how to do that?
I've managed to create the similar look of the expander, but I don't know how to implement separators?
Also, I would like to learn how to make expanders kind of "push" other components down when I expand them, so they don't overlap with other components, but simply move them.
Here's the XAML code for this expander:
<muxc:Expander x:Name="FontExpander"
x:Uid="FontExpander"
Width="1155"
IsExpanded="True"
Margin="22,323,0,0"
VerticalAlignment="Top"
Grid.Row="2"
Height="380"
HorizontalContentAlignment="Left"
ExpandDirection="Down">
<muxc:Expander.Header>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<FontIcon Margin="0,0,12,0"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
FontSize="16"
Glyph=""/>
<StackPanel Grid.Column="1"
Margin="0,12"
Orientation="Vertical">
<TextBlock x:Uid="SettingsFontTitle" Style="{StaticResource BodyTextBlockStyle}"/>
<TextBlock x:Uid="SettingsFontDescription"
Style="{StaticResource CaptionTextBlockStyle}"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
TextWrapping="WrapWholeWords"/>
</StackPanel>
</Grid>
</muxc:Expander.Header>
<muxc:Expander.Content>
<Grid Margin="-18,-170,-17,-170">
<TextBlock x:Name="SettingsFamily" x:Uid="SettingsFamily" Text="Family" Margin="50,51,1000,286"/>
<ComboBox x:Name="ComboBoxFamily" ItemsSource="{x:Bind Fonts}" Width="200" Margin="950,41,0,0" SelectionChanged="ComboBoxFamily_SelectionChanged" />
<TextBlock x:Name="SettingsStyle" x:Uid="SettingsStyle" Text="Style" Margin="50,106,995,218" Loaded="SettingsStyle_Loaded"/>
<ComboBox x:Name="ComboBoxStyle" Width="200" Margin="950,101,0,0">
<x:String>Regular</x:String>
</ComboBox>
<TextBlock x:Name="SettingsSize" x:Uid="SettingsSize" Text="Size" Margin="50,166,1002,158"/>
<ComboBox x:Name="ComboBoxSize" ItemsSource="{x:Bind FontSizes}" Width="200" Margin="950,161,0,0"/>
<TextBlock x:Name="Text" x:Uid="SettingsText" Text="Hello World! I am so excited to be here!" Margin="62,224,38,126" TextWrapping="Wrap" FontSize="{x:Bind (x:Double)ComboBoxSize.SelectedValue, Mode=OneWay}" TextAlignment="Center"/>
</Grid>
</muxc:Expander.Content>
</muxc:Expander>
Any help would be much appreciated!
A separate can for example be implemented using a Border or a Grid:
<Grid Background="DarkGray" Height="1" />
As a side note, you shouldn't use margins to position your elements. Use an appropriate layout Panel. Using a Grid, you should add ColumnDefinitions and RowDefinitions to it as examplified here.

How to navigate to specifically position on ListBox Windows Phone 8.1

i have a listbox with a lot of items, and each items when clicked go to a new page, what i want is when i return from the second page, stay at the same position of the item is clicked!
Thats my list!
<ListBox x:Name="list" Loaded="ListView_Loaded" SelectedItem="true" SelectionChanged="searchResultsList_SelectionChanged" ItemsSource="{Binding}" Background="{x:Null}">
<!--<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Margin" Value="0,0,0,15" />
</Style>
</ListView.ItemContainerStyle>-->
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border Width="80" Height="80">
<Image Source="{Binding Caminho}" />
</Border>
<StackPanel Margin="0,16,0,0" Grid.Column="2">
<TextBlock Foreground="White" Text="{Binding NomeCurso}" TextWrapping="Wrap" FontSize="{StaticResource TextStyleExtraLargeFontSize}" />
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
thanks!
If you are using WinRT you can write the following code in your page constructor to cache it, this way the positions will be intact after you go back to this page:
this.NavigationCacheMode = NavigationCacheMode.Required;
And from your sample I cant really see if you are using ListBox or ListView, I will presume ListView which is better as the ListBox is somewhat not needed anymore.
One thing I also noticed is that you use a simple {Binding} for your ItemsSource so maybe it is reset every time you go back because of this (if you are doing something that does this, as this is not visible from your sample code). I always have an additional property of type ObservableCollection and bind to it for example ItemsSource={Binding MyItems}. This way the list is reset only when I reset the property MyItems.

UserControl in DataTemplate does not bind correctly

I've been trying to create a custom menu. For this reason I wanted to use an ItemsControl in order to make it flexible. After hours of headache I figured out how to make it - kinda.
I have my custom ItemsControl "LiftMenu" (which is not yet much custom but standard) and an UserControl called "LiftItem". Last but not least I got the Model-class "LiftMenuItem".
By adding a new LiftMenuItem to the LiftMenu, it should display a new LiftItem-control as corresponding item. So far so good, I managed to get this working.
In that LiftItem-control I bind like I would in a normal DataTemplate: plain bindings with a path, nothing more. Normally this would work just fine because the DataTemplate already has it's context set to the model-type.
But now I just get an empty control that does nothing and shows nothing, because the bindings don't work.
I implemented it this way:
<menu:LiftMenu HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinHeight="200" Background="#80A8A8A8" Margin="5,0,0,0">
<menu:LiftMenu.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</menu:LiftMenu.ItemsPanel>
<menu:LiftMenu.ItemTemplate>
<DataTemplate DataType="{x:Type menu:LiftMenuItem}">
<menu:LiftItem />
</DataTemplate>
</menu:LiftMenu.ItemTemplate>
<menu:LiftMenuItem Header="Test1"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Border x:Name="border" BorderBrush="{Binding LabelColor}" BorderThickness="1" HorizontalAlignment="Left" Height="Auto" Margin="0"
VerticalAlignment="Stretch" Width="4" Background="{Binding BorderBrush, ElementName=border}"/>
<TextBlock Text="{Binding Header}" TextTrimming="CharacterEllipsis" Margin="5,0,5,0" HorizontalAlignment="Left" VerticalAlignment="Center"
Foreground="White" />
<controls:ProgressRing x:Name="ring" Grid.Column="2" HorizontalAlignment="Right" Stroke="#ffff8000" VerticalAlignment="Center"
Minimum="0" Maximum="100" Value="{Binding ProcessValue}" IsIndeterminate="{Binding ProcessIndeterminate}" Visibility="{Binding ProcessVisibility}"
Width="20" Height="20" Radius="10" Margin="2,0,2,0" />
</Grid>
In the end there is no text, no border. Just the ProgressRing is visible.
How can I fix this? This ListItem-control should become similiar to a button, thus I need to do some styling (animation, ...). I can't do this within a normal DataTemplate, but I don't want to miss the binding features of WPF on that. This would make it relatively unflexible.
What's the problem? I probably just miss some DataContext or so, but I don't know what it would be.

Get dynamic bind listbox item

I have the following listbox in my windows phone 8.1 silverlight application
<ListBox x:Name="ScenarioControl" FontFamily="Segoe WP">
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<Border Name="myborder" Margin="0,0,0,0" BorderThickness="0,0,0,3" BorderBrush="Black">
<Grid HorizontalAlignment="Stretch" Margin="0,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Tap="TextBlock_Tap" HorizontalAlignment="Left" VerticalAlignment="Center" Text="{Binding Path=Title, Mode=OneTime}" FontFamily="Segoe WP Semibold" Grid.Column="0" FontSize="23"/>
<Image Tap="Image_Tap" Source="/Images/blogger_small.png" HorizontalAlignment="Right" VerticalAlignment="Center" Grid.Column="1" OpacityMask="Black"/>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
As you can see TextBlock bound dynamically. I need to change some properties for the last TextBlock element but I cannot find method to get exact element inside the code.
I did search and found that there are solutions with ItemContainerGenerator method but that does not work for me, the following line of code always returns null.
ListBoxItem item = this.ScenarioControl.ItemContainerGenerator.ContainerFromIndex(ScenarioControl.Items.Count - 1) as ListBoxItem;
The most common cause of this problem is that the item in question isn't visible when you make the call to ItemContainerGenerator. Most listboxes, etc. virtualize their contents, meaning that they don't generate UI for elements that would not be visible to the user, so when you go to get the container, you just get null.
This Question + Answer(s) sums this up, and offers a solution to the problem.
Using a converter can help. It's not all declarative, but was what I thought initially.
Change style of last item in ListBox

AvalonDock 2.2 - Full width TitleTemplate (fill parent container)

Hy everyone!
I created a title template in AvalonDock 2.2 (WPF Toolkit). The problem is that the context menu of the LayoutAnchorable is only triggered when I right-click on the part of the title that contains something (and not the entire width of the anchorable).
Here is the relevant code segment I'm using now:
<ad:DockingManager x:Class="Pdn.Gui.Docking.Control.DockingSystem" ...
AnchorablesSource="{Binding Path=Panels}">
<ad:DockingManager.Resources>
<DataTemplate x:Key="DockingWindowTitleDataTemplate" DataType="{x:Type ad:LayoutContent}">
<StackPanel ToolTip="{Binding Path=Content.ToolTip}" Orientation="Horizontal" HorizontalAlignment="Stretch">
<Image MaxHeight="16" MaxWidth="16" VerticalAlignment="Center"
Source="{Binding Path=Content.IconSource, Converter={StaticResource IconToImageSourceConverter}}" />
<TextBlock Text="{Binding Path=Content.Name}" Margin="5,0,0,0" VerticalAlignment="Center"/>
<TextBlock Text="*" Visibility="{Binding Path=Content.DirtySignVisibility}" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="DockingWindowTitleGridDataTemplate" DataType="{x:Type ad:LayoutContent}">
<Grid ToolTip="{Binding Path=Content.ToolTip}" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" MaxHeight="16" MaxWidth="16" VerticalAlignment="Center"
Source="{Binding Path=Content.IconSource, Converter={StaticResource IconToImageSourceConverter}}" />
<TextBlock Grid.Column="1" Text="{Binding Path=Content.Name}" Margin="5,0,0,0" VerticalAlignment="Center"/>
<TextBlock Grid.Column="2" Text="*" Visibility="{Binding Path=Content.DirtySignVisibility}" VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="DefaultPanelTitle">
<TextBlock Text="{Binding Path=Content.Name}" TextTrimming="CharacterEllipsis" />
</DataTemplate>
...
</ad:DockingManager.Resources>
<ad:DockingManager.AnchorableTitleTemplate>
<StaticResource ResourceKey="DockingWindowTitleDataTemplate" />
</ad:DockingManager.AnchorableTitleTemplate>
...
</ad:DockingManager>
When I use the DefaultPanelTitle template (which is the default template of the theme) everything is fine, the context menu is triggered on the full width of the title part.
However when I use the other two templates (Image-Name-IsDirty elements), the context menu is triggered only on the beginning of the title area (and not right to the asterix).
I'm guessing I should tell the container to fill its parent container, but I can't figure out how. I used StackPanel, Grid, DockPanel (LastChildFill = "True") with HorizontalAlignment set to Stretch. What kind of container should I use?
What am I missing?
P.S.: I can only respond to your answers for another 12 hours, then I'm gone for a while (week). But I'm not abandoning this question until it's answered :) Thanks for your patience.
Well, the solution was quite simple. I wrapped the StackPanel in a Label. Now the context menu can be triggered on every pixel in the title part. The template now looks like this:
<ad:DockingManager x:Class="Pdn.Gui.Docking.Control.DockingSystem" ...
AnchorablesSource="{Binding Path=Panels}">
<ad:DockingManager.Resources>
<DataTemplate x:Key="DockingWindowTitleDataTemplate" DataType="{x:Type ad:LayoutContent}">
<Label>
<StackPanel ToolTip="{Binding Path=Content.ToolTip}" Orientation="Horizontal" HorizontalAlignment="Stretch">
<Image MaxHeight="16" MaxWidth="16" VerticalAlignment="Center"
Source="{Binding Path=Content.IconSource, Converter={StaticResource IconToImageSourceConverter}}" />
<TextBlock Text="{Binding Path=Content.Name}" Margin="5,0,0,0" VerticalAlignment="Center"/>
<TextBlock Text="*" Visibility="{Binding Path=Content.DirtySignVisibility}" VerticalAlignment="Center"/>
</StackPanel>
</Label>
</DataTemplate>
...
</ad:DockingManager.Resources>
<ad:DockingManager.AnchorableTitleTemplate>
<StaticResource ResourceKey="DockingWindowTitleDataTemplate" />
</ad:DockingManager.AnchorableTitleTemplate>
...
</ad:DockingManager>
I love simple solutions.

Categories