Silverlight ItemsControl formatting - silverlight

I have a ItemsControl in Silverlight to display a list of objects. These objects contain the strings Name, Value and Unit such as "Load", "100" and "MW". The control is within a grid column which can vary in size depending on the size of the browser window. I am trying to format the DataTemplate to allow the Name string to be on the left with the Value and Unit to be on the right. eg...
|Load 100 MW |
|Load2 50 MW |
|Unit1 20 X |
|Unit2 130 YXZ|
After a lot of trial and error I have managed to get this to work by using a grid with two columns. The left containing the name and the right containing a stack panel containing both the value and unit. This seems to work but if there are any units which differ in length the alignment of the text doesn't work. eg...
|Load 100 MW|
|Load2 50 MW|
|Unit1 20 X|
|Unit2 130 YXZ|
I'm running out of ideas on how to format this. Can anyone suggest anything? The main point is that I do not know in advance the length of the name, value or unit strings and, when the main column changes size, the name must stay on the left with the value and unit on the right.
Thanks in advance, Cap
(here is the code so far)
<ItemsControl Name="DataTypesGrid" ItemsSource="{Binding}" Margin="0,8,0,0" BorderBrush="{x:Null}" Foreground="White" Background="{x:Null}" IsEnabled="True">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" Margin="0,2,0,0" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Width="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" HorizontalAlignment="Left" Grid.Column="0"/>
<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
<TextBlock Text="{Binding Value}" HorizontalAlignment="Right" Margin="0,0,4,0"/>
<TextBlock Text="{Binding Unit}" HorizontalAlignment="Right"/>
</StackPanel>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

At the moment you basically start again with each new row, calculating grid column widths based on a single data entry.
The problem is you want width behaviour that spans rows "just like a datagrid column". Sounds like you actually want to customise a datagrid instead and strip out any headings you don't want instead.
Some alternatives:
Set a minimum width on your units box so that smaller units, at least, align.
Calculate the actual width of the widest unit and apply that value to a width binding (used on all "units" textblocks)

Related

How to use Textwrap property without defining the width in windows phone development?

I am using a grid with single row. I have placed a single LongListSelector within that row. The ItemTemplate is as follows,
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<!--when we dont mention any of the height and width properties then control tries to first occupy the minimum height/width required and then it streches to the extent it is possible-->
<phone:LongListSelector Grid.Row="0" x:Name="CLASS1">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="25,20" Background="#FF616464" Width="Auto">
<TextBlock Text="{Binding title, Mode=TwoWay}" FontSize="40" Margin="20,20" Foreground="White" TextAlignment="Left" TextWrapping="Wrap" FontStyle="Normal" FontFamily="Segoe UI"/>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</Grid>
Now I know that if we don't mention the width,MaxWidth values in the XAML file or in the properties, the Textblock above will stretch itself to the width it really needs to fit in the text. If there still some more width remaining it will expand till that extent. However I want to wrap the text of the Textblock. At the same time I want to utilize all the width available for stretching the TextBlock. So basically I want that the Textblock to stretch itself using the maximum width available and if the Text within it is still longer then I want to wrap it. Is there any solution to achieve this. I can use text wrap by setting constant value for width. As I want to deploy this application on different models then can I make it generic? Is there anyway to use parent's width?
All you have to do is to replace StackPanel with Grid.
<DataTemplate>
<Grid Margin="25,20" Background="#FF616464" Width="Auto">
<TextBlock Text="{Binding title, Mode=TwoWay}" FontSize="40" Margin="20,20" Foreground="White" TextAlignment="Left" TextWrapping="Wrap" FontStyle="Normal" FontFamily="Segoe UI"/>
</Grid>
</DataTemplate>
Result:
try this property
HorizontalContentAlignment="Stretch"

WPF/XAML Layout of two Labels containing different length text whilst maintaining equal size Font Scaling

I'm trying position two labels, one right next to the other (with a small amount of spacing).
e.g.
Label 1 Label 2
the content of each label will always be different, most often Label 2 will have more characters but I want the content to scale as the window is resized and I want the font sizes in Label 1 and Label 2 to remain consistent.
I don't care if the contents of Label 2 are cropped if too long.
I've tried using a grid with two columns and placed the Label inside a ViewBox:
<Viewbox Margin="0,0,0,0" VerticalAlignment="Top" HorizontalAlignment="Left">
<Label Margin="0,0,0,0" Content="{Binding Path=Suburb}"/>
</Viewbox>
Problem with this is, the positioning is never right and the font sizes don't remain consistent.
What is the best layout method to achieve this? Can I synchronise the font sizes through binding? I've tried binding Label 2's FontSize property to Label 1's FontSize but that doesn't work as it just returns 12 every time no matter how big/small the font really is (I'm presuming the actual FontSize isn't being calculated because the Label is inside a ViewBox).
Any suggestions?
Thanks
You can use the following approach:
<Viewbox VerticalAlignment="Top">
<DockPanel>
<TextBlock Text="Second" DockPanel.Dock="Right" />
<TextBlock Text="First" />
</DockPanel>
</Viewbox>
Note that the second label would never get cropped since the Viewbox would allow the content to render to any size and then scale it. You can specify MaxWidth on the second TextBlock which would limit its size in pixels.
Instead of a DockPanel you can use a Grid with different ColumnDefinitions to achieve the same effect.
The important thing is that they're all in the same Viewbox (which, consequently, doesn't synchronize the FontSize but performs a visual scaling of everything it contains).
The following markup seems to be ok:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Viewbox>
<Label Content="Label 1" Width="{Binding ActualWidth, ElementName=label2}"/>
</Viewbox>
<Viewbox Grid.Column="1">
<Label Name="label2" Content="Label 2 Label 2 Label 2"/>
</Viewbox>
</Grid>
Im not 100% sure of what 'content scale as the window resizes' and 'font sizes remain consistent' actuall means, but I guess you want each label to trim depending on available space.
So why not use TextBlock as below
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=Suburb}" TextWrapping="NoWrap" TextTrimming="CharacterEllipsis" Margin="0,0,5,0" />
<TextBlock Text="{Binding Path=Area}" TextWrapping="NoWrap" TextTrimming="CharacterEllipsis" Grid.Column="1"/>
</Grid>
I realize this question is old, but one of the more correct ways to do this is to use Runs.
Runs are sections of text within a TextBlock.
<TextBlock Margin="0,0,0,0" VerticalAlignment="Top" HorizontalAlignment="Left">
<Run Text="{Binding Suburb}" />
<Run Text="{Binding Area}" />
</TextBlock>
Keep in mind that everything within a TextBlock is whitespace-sensitive, so if you wanted to have two Runs back-to-back without any space in between, you would do something like this:
<TextBlock Margin="0,0,0,0" VerticalAlignment="Top" HorizontalAlignment="Left">
<Run Text="Foo" /><Run Text="bar" />
<!-- Prints 'Foobar' -->
</TextBlock>
It's similar to how HTML is rendered, if there is whitespace between the tags, a single space is rendered on the page between those elements. This rule only applies to items within a TextBlock, not the entire XAML page.

WPF ListView TextBlock TextWrapping

I am building a ListView that needs to have five columns - the first one needs to have text that can be any length and needs to wrap whenever the window size changes (in addition to changing the row height so the wrapped text is visible) and the other four columns are a static width of 45. I've been searching for hours on this and every solution I come across either requires a static width or doesn't work.
Solutions tried:
Column widths of auto, 1*, 2*, etc. (settings ignored)
DockPanel (settings ignored)
WrapPanel (ignored)
Setting Width to RelativeSource of parent for ActualWidth (ignored)
Any ideas? It seems like a significant number of people have had this same problem, but I would highly prefer to not have to go the static width route for this column. Especially since the content just gets cut off when I do that anyway (even with height="Auto" for the row). The width of the overall window could be as small as 1024, but could also be 1600+ which is why I want dynamic sizing. That way smaller screens will have the content wrap and larger screens will just show the one line since the content fits.
Here is the XAML:
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="45" />
<ColumnDefinition Width="45" />
<ColumnDefinition Width="45" />
<ColumnDefinition Width="45" />
</Grid.ColumnDefinitions>
<!-- This is the TextBlock that needs to wrap its content (and
change the height of the row (so the full content is still
visible) to whatever the available space is, but should not
make overall ListView wider than the parent's width. -->
<TextBlock Text="{Binding Content}" Padding="20,6,6,6" />
<!-- These four blocks will have other content eventually, but only need
to be 45 wide -->
<TextBlock Text="X" Grid.Column="1" HorizontalAlignment="Center" />
<TextBlock Text="X" Grid.Column="2" HorizontalAlignment="Center" />
<TextBlock Text="X" Grid.Column="3" HorizontalAlignment="Center" />
<TextBlock Text="X" Grid.Column="4" HorizontalAlignment="Center" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
Not so easy...but it can be done.
I wrote a solution for you. In short, use Expression Blend to create a copy of the ListView Template and delete the ScrollViewer surrounding the ItemPresenter.
Here is a more indepth explanation:
How to have the TextBlock in a left column of a Grid in a ListView Template expand or shrink with text wrapping?
<ListView HorizontalContentAlignment="Stretch" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.ItemTemplate>
...
</ListView.ItemTemplate>
</ListView>
I'd add TextWrapping="Wrap" to the first TextBlock element.

Windows Phone - equal column width [duplicate]

I'd like to create a table on WP7. This is my current approach using a ListBox with a Grid as the data template.
<ListBox x:Name="ResultsList" Margin="12,0" Grid.Row="1">
<ListBox.Resources>
<DataTemplate x:Key="ResultsListItem">
<Grid d:DesignWidth="385" Height="28">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="88"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="textBlock1" Margin="0,0,24,0"/>
<TextBlock x:Name="textBlock2" Margin="0,0,24,0"
VerticalAlignment="Top" Grid.Column="1"/>
<TextBlock x:Name="textBlock3" Margin="0,0,24,0"
VerticalAlignment="Top" Grid.Column="3"/>
</Grid>
</DataTemplate>
</ListBox.Resources>
<ListBox.ItemTemplate>
<StaticResource ResourceKey="ResultsListItem"/>
</ListBox.ItemTemplate>
</ListBox>
The problem is, that the resulting table's columns are not sized equally. The Grid's column definitions are applied to each row independently of the other rows. That means, if there is a long text in textBlock1, column 0 will be larger. In the next row there could be a shorter text in textBlock1, resulting in column 0 also being shorter than the column 0 in the previous row.
How can the columns in all rows be sized equally? I don't want to use fixed width because when the orientation changes from portrait to landscape the colums would resize automatically.
There is the HeaderedItemsControl, but as I understand it it is not available for Windows Phone 7?
This is a tricky problem! In WPF there exists the concept of a SharedSizeGroup, which allows you to share column widths across multiple grids, but this is not available in silverlight.
There are a few workarounds on the web:
http://www.scottlogic.co.uk/blog/colin/2010/11/using-a-grid-as-the-panel-for-an-itemscontrol/
http://databaseconsultinggroup.com/blog/2009/05/simulating_sharedsizegroup_in.html
Although neither are simple solutions.
You might also try Mike's AutoGrid:
http://whydoidoit.com/2010/10/06/automatic-grid-layout-for-silverlight/
Here is my solution using SharedSizeGroup as suggested by ColinE.
<ListBox x:Name="ResultsList">
<ListBox.Resources>
<SharedSize:SharedSizeGroup x:Key="Col1Width" />
<SharedSize:SharedSizeGroup x:Key="Col2Width" />
<SharedSize:SharedSizeGroup x:Key="Col3Width" />
<DataTemplate x:Key="ResultsListItem">
<StackPanel d:DesignWidth="385" Orientation="Horizontal">
<SharedSize:SharedSizePanel WidthGroup="{StaticResource Col1Width}">
<TextBlock x:Name="textBlock" MaxWidth="100" Text="{Binding A}"/>
</SharedSize:SharedSizePanel>
<SharedSize:SharedSizePanel WidthGroup="{StaticResource Col2Width}">
<TextBlock x:Name="textBlock1" MaxWidth="85" Text="{Binding B}"/>
</SharedSize:SharedSizePanel>
<SharedSize:SharedSizePanel WidthGroup="{StaticResource Col3Width}">
<TextBlock x:Name="textBlock2" MaxWidth="200" Text="{Binding C}"/>
</SharedSize:SharedSizePanel>
</StackPanel>
</DataTemplate>
</ListBox.Resources>
<ListBox.ItemTemplate>
<StaticResource ResourceKey="ResultsListItem"/>
</ListBox.ItemTemplate>
</ListBox>
Even the maximum with of each column can be controlled via the TextBlock's MaxWidth property. The SharedSizeGroups ensure that the TextBlocks have the same size in each row.
You can use WrapPanel. Set the following ItemsPanel in the Datatemple, you can just have textblock.
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<control:WrapPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>

WPF Grid Column MaxWidth not enforced

This problem stems from not being able to get my TextBlock to wrap. Basically as a last-ditch attempt I am setting MaxWidth on my container grid's columns. I was surprised to find that my child label and textbox still do whatever they want (bad children, BAD) and are not limited by my grid column's MaxWidth="200".
What I'm really trying to do is let my TextBlock fill available width and wrap if necessary. So far after trying many variations of HorizontalAlignment="Stretch" on every known parent in the universe, nothing works, except setting an explicit MaxWidth="400" or whatever number on the TextBlock. This is not good because I need the TextBlock to fill available width, not be limited by some fixed number. Thanks!
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MaxWidth="200" SharedSizeGroup="A" />
<ColumnDefinition MaxWidth="200" SharedSizeGroup="B" />
</Grid.ColumnDefinitions>
<Label VerticalAlignment="Top" Margin="0 5 0 0" Grid.Column="0" Style="{StaticResource LabelStyle}" Width="Auto" Content="{Binding Value.Summary}" />
<TextBlock Grid.Column="1" Margin="5,8,5,8" FontWeight="Normal"
Background="AliceBlue"
Foreground="Black" Text="{Binding Value.Description}"
HorizontalAlignment="Stretch"
TextWrapping="Wrap" Height="Auto" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I've tried to replicate your problem by pasting everything between your Grid elements in to Kaxaml but everything wraps as you would expect it to. (I inserted regular strings where you were doing bindings and removed the Label style).
It could be that the problem is higher up the tree.
I'd suggest pasting chunks in to Kaxaml or similar to test and see which parent breaks your UI.
I provided an answer to this question, only it was using an ListView instead of an ItemsControl but the issue is likely the same. There is probably a ScrollViewer surrounding your ItemPresenter and you need to edit a copy of the ItemsControl template.
WPF ListView TextBlock TextWrapping

Resources