Inside my XAML/C# app for Windows 8 Store, I am trying to create a ListView where each ListItem is a horizontal Grid, so I am using the XAML below:
<ListView Name="ResultsView">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="4*" />
<ColumnDefinition Width="4*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding BestRank}" Grid.Column="0"/>
<TextBlock Text="{Binding PlayerName}" Grid.Column="1"/>
<TextBlock Text="{Binding BestScore}" Grid.Column="2"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
When I run this program, the listview contains all the items from the list it is bound to (through code behind). However in every list-item, contents of all three columns appear together without any space between them. When I create a similar grid outside the listview, it displays fine and takes up entire width of the screen and divides it between the three columns as specified in the XAML above.
What am I doing wrong?
I believe the issue is the ItemContainerStyle needs to have a HorizontalContentAlignment of Stretch. Try this adding this to the ListView:
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
Both the ListBox and the Grid should have HorizontalContentAlignment="Stretch"
Specify the width of the grid or add the style as a resource to the grid. Add this right after your </Grid.ColumnDefinitions> line.
<Grid.Resources>
<Style TargetType="TextBlock">
<Setter Property="TextAlignment" Value="Right" />
<Setter Property="Margin" Value="4" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</Grid.Resources>
This will set the properties to all of the TextBlocks in the Grid. You might only need the Margin property. Also consider the properties Width and Padding. You can override this default by setting the property on any given TextBlock to something that isn't the default from the Style.
The default ItemsPanelTemplate is a stack panel as far as I recall meaning that it won't automatically give it's children stretch behavior. You will need to change it, I will edit in a sec just on my mobile waiting for my laptop to boot up :)
Edit:
Er no ignore that I was thinking of ItemsControl, JBrooks is right, just make sure HorizontalContentAlignment is set to Stretch as the default is Left.
<ListView Name="ResultsView" HorizontalContentAlignment="Stretch">
That should work fine - any controls that use XXXXContentAlignment seem to default to Left which I think its a bit inconsistent vs other controls (such as Grid)
actually this is not a problem of Listview. Problem is where you are putting it in? Put your list view in grid. It should work
<ListView Name="ResultsView"
HorizontalContentAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Background="Yellow">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="4*" />
<ColumnDefinition Width="4*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding BestRank}" Grid.Column="0"/>
<TextBlock Text="{Binding PlayerName}" Grid.Column="1"/>
<TextBlock Text="{Binding BestScore}" Grid.Column="2"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Related
I'm a relative WPF newbie and am still trying to get my head around its basics. I have just came across a grid sizing issue which has me stumped (and it seems that is not limited to just grids alone).
The problem is that if I have a grid with relatively sized rows/columns, within a row/column of another grid, with that row/column dimensions being set to auto, the relative sizing for the child grid seems to have no effect.
What I would expect for this xaml, is that the inner grid columns are still relatively sized (with one being 3x the width of the other) even if they are inside an auto-sized column of parent grid. Is this expected behaviour, is this a bug or am I missing something basic?
Thanks!!
Try this:
<Grid Background="DarkBlue">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label BorderBrush="White"
BorderThickness="1"
Content="Cell 1"
Foreground="White" />
<Grid Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Label BorderBrush="White"
BorderThickness="1"
Content="Cell 2"
Foreground="White" />
<Label Grid.Column="1"
BorderBrush="White"
BorderThickness="1"
Content="Cell 3"
Foreground="White" />
</Grid>
</Grid>
The reason that second grid was behaving strangely is because the column of the first grid is set to Auto.
As it's auto it's trying to squash everything inside it to be as small as possible - without the labels this would be a width of 0 for each column of the second grid. Plus the width of the labels makes them about equal.
I believe it has something to do with the order in which WPF calculates widths of something in an auto column/row.
The first thing it does is notice the auto value of the second grid, which is 0. THEN, after that it adds whatever the width of the contents are to this calculated value - as both labels are about the same size, this results in two similar columns.
My solution removes the auto value and instead says the the first column of the first grid "of the total width, you get 2/3rds of the space, and the other column gets 1/3rd" - it's not auto, so the second column is not being squashed in any way.
it seems outer Grid column dictate which width inner Grid should have. Try reverse measure order by binding column width to inner grid width: <ColumnDefinition Width="{Binding Path=ActualWidth, ElementName=InnerGrid}"/>
<Grid Background="DarkBlue">
<Grid.Resources>
<Style TargetType="Label">
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderBrush" Value="White"/>
<Setter Property="BorderThickness" Value="1"/>
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="{Binding Path=ActualWidth, ElementName=InnerGrid}"/>
</Grid.ColumnDefinitions>
<Label Content="Cell 1" />
<Grid Grid.Column="1" Name="InnerGrid" Background="DarkRed">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Label Content="Cell 2.1" />
<Label Grid.Column="1" Content="Cell 2.2" />
</Grid>
</Grid>
after resize only 1st column is increasing (see screenshot)
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.
I'm trying to build a simple (in terms of design) screen which will allow users to enter multiple rows of data.
The format of the screen is 4 columns (TextBox, TextBox, ComboBox, TextBox), with new rows added dynamically.
I have tried 2 approaches initially - one using a DataGrid, and one using a ListView.
The DataGrid caused many headaches with items retaining focus when they were clicked out of, and the ListView prevented me from being able to consistently access the underlying cell, instead returning the bound data object represented by the row.
My current approach uses a custom component which represents a row, and contains the 3 TextBox objects, and one ComboBox object.
One or more of these objects are displayed in a ListBox.
This approach allows for handling events more consistently, but is visually less straightforward to get working.
Currently, when a row is displayed, the first 3 columns in the grid (which have an explicitly defined width) display fine, but the textbox in the final column does not expand to fill the available width.
The relevant code for the panel:
<DockPanel LastChildFill="True" Name="basePanel">
<Grid x:Name="baseGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60" />
<ColumnDefinition Width="60"></ColumnDefinition>
<ColumnDefinition Width="55"></ColumnDefinition>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBox Name="txtYear" Grid.Column="0" Style="{StaticResource txtComponentStyle}" Text="{Binding Path=RatingYear, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Name="txtArrears" Grid.Column="1" Style="{StaticResource txtComponentStyle}" Text="{Binding Path=ArrearsAmount, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" />
<ComboBox Name="cmbChangeCode" Grid.Column="2" ItemsSource="{Binding Path=ChangeCodes, Mode=OneTime}" SelectedItem="{Binding Path=ChangeCode}" />
<TextBox Name="txtComments" Grid.Column="3" Style="{StaticResource txtComponentStyle}" Text="{Binding Path=Comments, ValidatesOnDataErrors=True,UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch" />
</Grid>
</DockPanel>
When I select a row in the ListBox, I can see that the object itself fills the entire width, yet the text box in the final column only expands to fit the content.
Setting the width in the column definition to * should (as I understand it) cause the column to expand to fille the available space, and setting the HorizontalAlignment property on the textbox to Stretch should cause the text box to fill the column.
The code below creates the ListBox.
<UserControl.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="0.5" Margin="1">
<DockPanel LastChildFill="True" HorizontalAlignment="Stretch" Background="DarkSlateBlue">
<Controls:ArrearsListEntry />
</DockPanel>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<DockPanel LastChildFill="True" Background="Red">
<ListBox IsSynchronizedWithCurrentItem="True" Name="lstRowData" ItemsSource="{Binding Path=Rows}" />
</DockPanel>
Is there a way to bind the width of the text box to the actual width of the column, or a property which will cause the text box to automatically expand to the available width?
The ListBoxItem's Content is left aligned by default. Add a Setter for the HorizontalContentAlignment to your ListBoxItem Style:
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="ContentTemplate">
...
</Setter>
</Style>
I have been trying to get dynamic column width to work for my simple WP8.1 app. The goal is to have the first column take up half of the listbox, and the other two columns a quarter each. I hoped to do this by assigning a dynamic width, using the * indicator as described here.
This let me to the piece of xaml code at the bottom of my post.
In my MainPage I set the DataContext to an ObservableCollection, and all the data shows up in their respective columns, the columns just do not get the desired width (they are all as small as can be).
What prevents my dynamic width from working? I have toyed around with HorizontalAlignment and Width of TextBoxes too, but with no success. I tried to look around for answers, and I even used some examples that did work, but that did not bring me closer to understanding why it does not work here.
Thanks in advance.
<Grid>
<ListBox Name="transactionListBox" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="0,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Name}"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Category}"/>
<TextBlock Grid.Row="0" Grid.Column="2" Text="{Binding Amount}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
The item Grid itself is being sized-to-fit by the ListBox. You can override this behavior by setting the ItemContainerStyle to make the ListBoxItems stretch to fill horizontally:
<ListBox Name="transactionListBox" ItemsSource="{Binding}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
...
</ListBox>
I think you need a constraining width on the ListBox or Grid. With your code, the grid and ListBox will re-size to fit the content, so there is no need for the dynamic column definitions.
I have a databound listbox which is actually displaying two columns of data. It is displayed as follows:
<UserControl.Resources>
<DataTemplate x:Key="AlignedPairs">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Fruits}" Grid.Column="0" />
<TextBlock Text="->" TextAlignment="Center" Grid.Column="1" />
<TextBlock Text="{Binding Colors}" Grid.Column="3" />
</Grid>
</DataTemplate>
</UserControl.Resources>
<ListBox Name="lbStuff" Grid.ColumnSpan="2" Grid.Row="1"
ItemTemplate="{StaticResource AlignedPairs}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
Then Itemsource set in codebehind.
Based on some logic however, I would like to set either a line or a item in one of the columns, e.g. a fruit to red, or the line to bold. I have code to work out which Fruit or Color I would like to differentiate (by color/bold) in the code behind, but I can't figure out, especially given the custom listbox display, how I could go about setting a particular item to a different color/bold.
Does anyone have any ideas?
Let me know if any further code is required. Cheers.
edit: all I really want to be able to do is make the problem as easy as possible... loop through each item in the listbox and check with a string, if there is a match SOMEHOW visually distinguish this item in the listbox/listview. There is no need to make this any more complicated than it needs to be. Why you are not able to individually set an items foreground color is beyond me!
edit 2:
It looks like I almost get there with the following (but it throws exception and doesn't work)
foreach (var li in ListView.Items)
{
if (someCriteria == true)
{
((ListViewItem) li).Foreground = Brushes.Red;
//exception throw as this is the actual object stored in this location (the Fruit object for example) not the row or listviewitem I want to change the color of so it can't be cast :(
}
}
This is where the ViewModel design pattern really helps you out.
Presumably you have a class that exposes the properties Fruit and Color. Make a wrapper class that exposes both those properties and also exposes, say, FruitBackgroundBrush and ItemBackgroundBrush. Then you can just bind to those properties in your template, e.g.:
<DataTemplate x:Key="AlignedPairs">
<Grid Background={Binding ItemBackgroundBrush}>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Fruits}"
Background="{Binding FruitBackgroundBrush}"
Grid.Column="0" />
<TextBlock Text="->" TextAlignment="Center" Grid.Column="1" />
<TextBlock Text="{Binding Colors}" Grid.Column="3" />
</Grid>
</DataTemplate>
DataTriggers will take care of this. Here's a good primer: http://en.csharp-online.net/WPF_Styles_and_Control_Templates%E2%80%94Data_Triggers