How to give dynamic width to gridview items (text block)? - c#

I am developing a Windows 10 UWP, and a Windows Phone 8.1 project. In the project, there are tags like in Instagram, which can be seen in the picture. These tags are printed on the screen using a GridView. The problem is that I couldn't make the GridView items' width dynamic by their content. It takes the first item's width and gives all the other items the same width. For the shorter words, it's not a problem but some letters of the longer words are not visible.
Here is a screen shot of the problem.
The code I wrote;
<GridView SelectionMode="None" ItemsSource="{Binding TagsArray}" ScrollViewer.IsHorizontalRailEnabled="True">
<GridView.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock x:Name="tagButton" Tapped="tagButton_Tapped" FontWeight="Medium" Text="{Binding}" Foreground="#063076" FontSize="11" HorizontalAlignment="Left"/>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemContainerStyle>
<Style TargetType="GridViewItem">
<Setter Property="Margin" Value="0,-15,0,-15"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</GridView.ItemContainerStyle>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid Orientation="Horizontal"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>

I would recommend you to use WrapPanel from UWP Community Toolkit for this.
You can use it inside the GridView by replacing the ItemsPanel:
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel Orientation="Horizontal" AllowDrop="True">
</toolkit:WrapPanel>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
Here's a full example:
<GridView x:Name="ItemGrid" Width="450" HorizontalAlignment="Left" VerticalAlignment="Bottom">
<GridView.ItemTemplate>
<DataTemplate >
<TextBlock Text="{Binding}"/>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<controls:WrapPanel Orientation="Horizontal" AllowDrop="True">
</controls:WrapPanel>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
When used with the following collection:
var ite = new ObservableCollection<string>();
ite.Add("#tag1");
ite.Add("#a");
ite.Add("#tag3");
ite.Add("#differ");
ite.Add("#tag5");
ite.Add("#longertag");
ite.Add("#verylongtag");
ite.Add("#tag1");
this.ItemGrid.ItemsSource = ite;
Provides the following output:

Related

How to STOP ListView from expanding to content

I'm trying to make my xaml squeeze list items (images) to fit the initial window size, but they cant be fixed size because i want to scale them up as i increase the size of the window. Something like a ViewBox would do.
I load images from 2 folders (software and hardware). Number and size of the images will wary in the runtime so i want to make items be the same size regardless of images size or number of items.
That's why i used uniform grid as a items panel template.
But, this is the result I'm getting...
The ListView loads images in their full size and expands itself to fit them in, cutting of some of the items in the process.
This is my xaml:
<Window x:Class="WPF_UI_Testing.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPF_UI_Testing"
mc:Ignorable="d"
Title="MainWindow" Height="460" Width="640">
<Grid>
<ListView x:Name="listview1">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}"
FontWeight="Bold" FontSize="18" />
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"></StackPanel>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid ></UniformGrid>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="{Binding problemImage}"/>
<TextBlock Text="{Binding ImageName}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Window>
Is there a way to prevent ListView from expanding beyond window borders when populated with content?
EDIT:
I packed the entire solution with some dummy data and images if anyone wants to have a go at this...
https://drive.google.com/file/d/1IWqxSR3kpsVdCm5Qcgn6QZbZhbYz52n2/view?usp=sharing
The UniformGrid does basically what you want. The idea to use it as item panel is also correct. The only problem that arises, aside from using item groups, is the fact that the ListView wraps its panel into a ScrollViewer, which results in items or the UniformGrid to resize differently as the ScrollViewer gives the panel no size restrictions. UniformGrid needs to be hosted in a fixed size container in order to be able to calculate its children's max sizes.
You should either use the ItemsControl
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type viewModels:DetailItem}">
<StackPanel>
<Image Source="{Binding problemImage}"/>
<TextBlock Text="{Binding ImageName}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
or override the ControlTemplate of ListView and remove the ScrollViewer (if you need its additional features of ListView):
<ListView>
<ListView.Template>
<ControlTemplate TargetType="ListView">
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}">
<ItemsPresenter />
</Border>
</ControlTemplate>
</ListView.Template>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type viewModels:DetailItem}">
<StackPanel>
<Image Source="{Binding problemImage}"/>
<TextBlock Text="{Binding ImageName}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The limitations of this approach is when using groups to display the items. UniformGrid should handle GroupItem (nested items) not as expected.
As said in my previous comment, if you want to group items, you need to extend a panel of your choice to manually arrange GroupItem and it's children (nested ItemsPresenter). As you think about how to calculate sizes you may realizes that it is more complicated to calculate group item sizes with dynamic grouped item sizes.
I recommend to let go the grouping and use one of the above solutions or use grouping and embrace the ScrollViewer.

UniformGrid Grid.Row Grid.Column not being adhered to

Following on from this question (DataTemplateSelector for Uniform Grid binding?), I still am not getting my Buttons/Textblocks to adhere to the row and the column that they are bound to in the view model. I will only post the XAML as I know the row and column bindings are correct (Live Visual Tree tells me Row is 1 and Column is 1 for example, but on the grid it shows otherwise...).
If you need anymore code let me know. This is the small bit of XAML.
<Grid DockPanel.Dock="Left" Background="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinWidth="800" Height="400">
<ItemsControl ItemsSource="{Binding ObjCompositeCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid DockPanel.Dock="Top" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Name="objGrid" Grid.Row="1"
Rows="{Binding RowCount}"
Columns="{Binding ColumnCount}"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding Row}"/>
<Setter Property="Grid.Column" Value="{Binding Column}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type engine:ObjA}">
<Button Content="{Binding Id}" />
</DataTemplate>
<DataTemplate DataType="{x:Type engine:GridLabeller}">
<TextBlock Text="{Binding HeaderName}"/>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
</Grid>
A UniformGrid in WPF doesn't care about the Grid.Row and Grid.Column attached properties. You need to create a Grid with RowDefinitions and ColumnsDefinitions for setting these properties to have any effect.
You may create a Grid programmatically in the view based on the values of the RowCount and ColumnCount source properties.

How can i make a ListView's items take up all the available height?

I am trying to make a column chart using WPF. For the Y axis I have a list of values which I would like to display on the control.
I have a ListView bound to the collection. How can I get the items to spread over the entire length of the list view rather then squish together at the top? And is the ListViewthe correct control to use for this?
Here is a sample of my Xaml:
<ListView Grid.Row="1" Grid.Column="1" Grid.RowSpan="2"
ItemsSource="{Binding YAxisValues}"
Background="Gray" VerticalContentAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Path=Value}"
Foreground="White"
HorizontalAlignment="Left"
FontSize="12" Width="50"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
On the left is the list view as i currently have it.
On the right is my desired result.
How can I change my ListView to get there? Or do I need to use a different control entirely?
with UniformGrid used as ItemsPanel items get equal height
<ListView Name="Axis" Grid.Row="1" Grid.Column="1" Grid.RowSpan="2"
ItemsSource="{Binding YAxisValues}"
Background="Gray" VerticalContentAlignment="Stretch"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="1"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<!--no changes-->
</ListView.ItemTemplate>
</ListView>
there can be a issue when ListView doesn't have enough height for all elements. In that case I would put ListView into ViewBox
Set HorizontalContentAlignment to Stretch for items.
<ListView>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>

WP Horizontal Scrolling Grid Selection

I'm trying to create a scrollable gird, in my app i have a vertical scrolling listbox with text items as shown below.
<ListBox x:Name="selectionList" Margin="49,0,11,0" Padding="20,20,0,0" SelectionChanged="AlbumList_SelectionChanged" ItemsSource="{Binding ''}" Background="{x:Null}" Height="606" VerticalAlignment="Top" Width="420">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding SlectionTitle}" FontSize="22" Margin="0,0,0,10" FontFamily="{StaticResource hevel}" Foreground="#FF99FFFF"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
So I Changed The TextBlock to this
<Border x:Name="Selection_List_Image" BorderBrush="#FFC4C3C3" BorderThickness="8" HorizontalAlignment="Left" Height="198" Margin="18,24,0,5" VerticalAlignment="Center" Width="199" CornerRadius="12" RenderTransformOrigin="0.5,0.5" Padding="0">
<Border.Background>
<ImageBrush Stretch="Fill" ImageSource="{Binding SelectionArt}"/>
</Border.Background>
</Border>
This is fine for a single vertical scrolling list. I'm trying to get a grid that is for example permanently 4 images high and automatically wide, so if there are 4 images it shows a column of 4 if there are 8 images it shows 2 columns of 4 and so on
i have tried this example
WP7 - issues with Horizontal scrolling Listbox
but it just kept the list vertical and scrolled horizontal
any suggestions, thanks
// Solved
Thank you, i ended up having to use the example link aswel
<ListBox x:Name="AlbumList" Margin="49,0,11,0" ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Disabled" SelectionChanged="AlbumList_SelectionChanged" ItemsSource="{Binding ''}" Background="{x:Null}" Height="748" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel Orientation="Vertical" ></toolkit:WrapPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Border x:Name="Album_List_Image" BorderBrush="#FFC4C3C3" BorderThickness="8" HorizontalAlignment="Left" Height="152" Margin="18,24,0,5" VerticalAlignment="Center" CornerRadius="12" RenderTransformOrigin="0.5,0.5" Padding="0" Width="152">
<Border.Background>
<ImageBrush Stretch="Fill" ImageSource="{Binding AlbumArt}"/>
</Border.Background>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
If you're doing WP8.1 runtime you can easily do this by changing the <ItemsPanelTemplate> to a <WrapGrid> with Orientation set to Vertical and MaximumRowsOrColumns set to 4. Like so,
See MSDN WrapGrid (they actually do an example of what you want.. but in another Orientation)
<ListBox>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid Orientation="Vertical" MaximumRowsOrColumns="4"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<!-- your data template -->
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
If you're doing this with WP8.0+ SL then it will be a tad bit harder. You will need the Windows Phone Toolkit and use a <WrapPanel> instead but you will need to Databind some values (or hardcode it...depending on how loose your ViewModel is)
<ListBox x:Name="myListBox" Height="412">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel Orientation="Vertical" ItemHeight="100" ItemWidth="100"></toolkit:WrapPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<!-- your data template -->
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In this example I hard coded each Item to be 100x100 and I hard coded the Height to be 412, thus making it have 4 items in the Vertical.
You can Databind the Height and ItemHeight and the ItemWidth if you choose to do so.

Access to the element of a header Template in a grouped gridView

For a windows 8 application in C#/XAML, I would like to be able to know the position (X and Y) of a button that I have in the header template of a grouped grid View:
Here is the simple Xaml Code :
<GridView x:Name="PicturesGridView" SelectionMode="None"
ItemsSource="{Binding Source={StaticResource cvs1}}" IsItemClickEnabled="True"
ItemTemplate="{StaticResource CustomTileItem}" ItemClick="ItemView_ItemClick" IsSwipeEnabled="True">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</local:MyGridView.ItemsPanel>
<local:MyGridView.GroupStyle>
<GroupStyle x:Name="MyGroupStyle">
<GroupStyle.HeaderTemplate >
<DataTemplate x:Name="MyDataTemplate">
<Button x:Name="HeaderButton" Click="Button_Click_1" Content="{Binding Key}" Foreground="Black" Background="White" FontSize="30" Margin="0,0,0,-10" ></Button>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<VariableSizedWrapGrid ItemWidth="75" ItemHeight="150" Orientation="Vertical" Margin="0,0,80,0" MaximumRowsOrColumns="3"/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
<GridView.GroupStyle>
<GridView>
I succeed in accessing to the Button inside the header template by doing this :
var template = element.FindName("PicturesGridView") as MyGridView;
var group = template.GroupStyle[0] as GroupStyle;
var buttonHeader = group.HeaderTemplate.LoadContent() as Button;
But then I can't distinguish each button of my template. I would like an array of physical button representing my data and their position.
Thank you for your help
The WinRT Tookit has what you need...Check out http://winrtxamltoolkit.codeplex.com/downloads/get/467926 and look at the VisualTreeHelperExtensions.
Using the extension method GetDescendantsOfType you can write code like the following:
var buttons = PicturesGridView.GetDescendantsOfType().ToArray();
Now, this will give you all the buttons in PicturesGridView, so if you have item templates that also contain buttons, you will get those as well. You could set the Tag property on the button in the Header template so you could easily identify them from other buttons.

Categories

Resources