Making a portion of a TreeViewItem wrap - c#

I have a TreeViewItem that has multiple parts in it -- an icon, a header, and a piece of data. I'm trying to get the last part to wrap without making everything wrap, and I'm having no luck. Here's an illustration of the problem:
I've tried a few things that I've found on Stack Overflow, with no luck. There were three suggestions I found:
Put the item in a grid with a column with bound to the TreeView's actual width. This is implemented in the XAML below, but doesn't seem to help.
Disable horizontal scrolling on the TreeView with ScrollViewer.HorizontalScrollBarVisibility = "Disabled", which I also implemented in the XAML below.
Wrap what I need in a WrapPanel. I tried this, but got poor results. When I wrapped the whole TreeViewItem in one, I got wrapping that was hard to control (the whole item wraps, so all parts wrapped and it looked awful). When I wrapped just the desired TextBlock in one, I got no results at all. So I took it out.
Here's the template I have for the TreeViewItem:
<DataTemplate x:Key="BasicPropertyTemplate" DataType="{x:Type json:JProperty}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding RelativeSource={RelativeSource AncestorType=TreeView}, Path=ActualWidth}" />
</Grid.ColumnDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="16" />
<ColumnDefinition Width="3" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="3" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="3" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Border
Width="12"
Height="12"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="{Binding Converter={StaticResource TypeToColorConverter}, ConverterParameter='Interior'}"
BorderBrush="{Binding Converter={StaticResource TypeToColorConverter}}"
BorderThickness="1" />
<TextBlock Grid.Column="2" Text="{Binding Name, Mode=OneWay}" />
<TextBlock Grid.Column="4" Text=": " />
<TextBlock
Grid.Column="6"
Text="{Binding Value, Converter={StaticResource StringFormatConverter}, Mode=OneWay}"
TextWrapping="Wrap" />
</Grid>
</Grid>
</DataTemplate>
Here's an illustration of the layout, so you can see how the TreeViewItem is composed:
Essentially, the gray part is the icon, the green parts are just spacers between items, the blue parts are header and a colon (essentially irrelevant to what this question is about), and the red part is the actual content.
It's the red part I want to wrap.
Here's the TreeView definition:
<TreeView
x:Name="JsonRoot"
Grid.Column="0"
ItemTemplateSelector="{StaticResource TreeViewTemplateSelector}"
Loaded="JsonRoot_Loaded"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
VirtualizingStackPanel.IsVirtualizing="True" />
I feel like I'm doing something simple wrong, but I'm not sure what. Is there a way to accomplish this without just locking the TextBlock width to something arbitrary, like 200?

My investigation showed that TextBlock Wrap doesn't work with <ColumnDefinition Width="Auto"/>. So I changed last <ColumnDefinition Width="Auto"/> in DataTemplate to <ColumnDefinition Width="*"/>.
Unfortunately TreeViewItem (which is a default container for TreeView items) has <ColumnDefinition Width="Auto"/> in its Template and places our items there. I have tried to make a custom template with <ColumnDefinition Width="*"/> and it worked but code is really long. So I decided to compensate the effect by binding Grid width:
<DataTemplate x:Key="BasicPropertyTemplate" DataType="{x:Type json:JProperty}">
<Grid Width="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}, Path=ActualWidth}" >
<Grid Margin="0,0,20,0"> <!--right margin to compensate excessive width-->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="16" />
<ColumnDefinition Width="3" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="3" />
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="3" />
<ColumnDefinition Width="*"/> <!--was Auto-->
</Grid.ColumnDefinitions>
<Border
Width="12"
Height="12"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="{Binding Converter={StaticResource TypeToColorConverter}, ConverterParameter='Interior'}"
BorderBrush="{Binding Converter={StaticResource TypeToColorConverter}}"
BorderThickness="1" />
<TextBlock Grid.Column="2" Text="{Binding Name, Mode=OneWay}" />
<TextBlock Grid.Column="4" Text=": " />
<TextBlock
Grid.Column="6"
Text="{Binding Value, Converter={StaticResource StringFormatConverter}, Mode=OneWay}"
TextWrapping="Wrap" />
</Grid>
</Grid>
</DataTemplate>
ScrollViewer.HorizontalScrollBarVisibility="Disabled" is important element and should be set.

Try changing the grid in DataTemplate with a DockPanel:
<DataTemplate>
<DockPanel>
<Border Width="12"
Height="12"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="{Binding Converter={StaticResource TypeToColorConverter}, ConverterParameter='Interior'}"
BorderBrush="{Binding Converter={StaticResource TypeToColorConverter}}"
BorderThickness="1" />
<TextBlock Text="{Binding Name, Mode=OneWay}" />
<TextBlock Text=": " />
<TextBlock Text="{Binding Value, Converter={StaticResource StringFormatConverter}, Mode=OneWay}"
TextWrapping="Wrap" />
</DockPanel>
</DataTemplate>

Related

Text in WPF label is being cut-off at the end of the line

I have an WPF usercontrol which has a grid with 4 columns and only 1 row.
The second column is a WPF label.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
// I only specify there the label, no other components, just to simplify
<Label Grid.Column="1"
VerticalAlignment="Center"
Margin="5"
Content="{Binding Path=Text}"
Foreground="{Binding Path=ForegroundColor}">
<Label.Resources>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
</Label.Resources>
</Label>
</Grid>
For some reason the text of the label is being cut-off at the end of the line instead of putting it at next line. I have tried above solution to wrap text and continue in the next line but it is not working. Any ideas?
UPDATE:
I changed the Label as below according to this:
<Label Grid.Column="1"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
HorizontalContentAlignment="Stretch"
HorizontalAlignment="Stretch"
Height="Auto"
Margin="5"
Foreground="{Binding Path=ForegroundColor}">
<TextBlock Text="{Binding Path=Text}" TextWrapping="Wrap"/>
</Label>
and now it is working like a charm!
You may use the AccessText and set the TextWrapping="Wrap":
<Label Grid.Column="1"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
HorizontalContentAlignment="Stretch"
HorizontalAlignment="Stretch"
Height="Auto"
Margin="5"
Foreground="{Binding Path=ForegroundColor}">
<Label.Content>
<AccessText TextWrapping="Wrap" Text="{Binding Path=Text}"/>
</Label.Content>
</Label>

TextTrimming property of TextBlock placed in TreeViewItem using GridSplitter

My XAML is very simple:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock FontSize="55" HorizontalAlignment="Center"
VerticalAlignment="Center" TextWrapping="Wrap">Left side</TextBlock>
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Center" />
<TreeView Grid.Column="2" ScrollViewer.HorizontalScrollBarVisibility="Disabled" >
<TreeViewItem>
<TreeViewItem.Header>
<TextBlock Text="The full string"
TextTrimming="CharacterEllipsis"/>
</TreeViewItem.Header>
<TreeViewItem>
<TreeViewItem.Header>
<TextBlock Text="The full string"/>
</TreeViewItem.Header>
</TreeViewItem>
</TreeViewItem>
</TreeView>
<Border BorderBrush="Green" BorderThickness="5" CornerRadius="5" Grid.Column="3" />
</Grid>
What I have is:
and it is not correct for me:
And what behavior I want is:
How can I use TextTrimming property of TextBlock placed in TreeViewItem?
You need to modify the style of your TreeViewItem, because the content is not stretched.
See style example and problem explanation at
https://leecampbell.com/2009/01/14/horizontal-stretch-on-treeviewitems/
Just apply the style from the link to your TreeView
<TreeView>
<TreeView.Resources>
<!--paste the style from the link here-->
</TreeView.Resources>
Sorry for not posting the style, but i don't get the formatting done.

Confused wpf grid behaviour in DataTemplate

I am confused about the Grid's behaviour inside a ContentTemplate:
I want to style my ListView:
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding ArticleID}"
Grid.Column="0"
/>
<TextBlock Text="{Binding ArticleName}"
Grid.Column="1"
/>
<Button Command="{Binding DataContext.RemoveComponentFromVehicle, RelativeSource={RelativeSource AncestorType=Grid}}"
Grid.Column="2"
>
<Button.Content>
<StackPanel Style="{StaticResource IconTextCombo}">
<Image Source="{StaticResource ComponentToVehicle_Delete}"/>
</StackPanel>
</Button.Content>
</Button>
</Grid>
As you can see, I want to make sure that my buttons align right on the right side because the ArticleID and Name length can vary.
The buttons aren't align as I want and expect. I have a test project:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBox Text="Foo"
Grid.Column="0"/>
<TextBox Text="Bar"
Grid.Column="1"/>
<Button Content="DELETE"
Grid.Column="2"/>
<TextBox Text="Hello World"
Grid.Column="0"
Grid.Row="1"/>
<TextBox Text="LONG TEXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXT"
Grid.Row="1"
Grid.Column="1"/>
<Button Content="DELETE"
Grid.Row="1"
Grid.Column="2"/>
</Grid>
In this 'hardcoded' version every aligns as I want... I don't know why my grid is so weird in the Template. Can someone help me?
Thanks in advance!
Here is the solution after working around:
<ListView DockPanel.Dock="Right"
ItemsSource="{Binding}"
SelectedItem="{Binding }"
Name="lvMain"
HorizontalContentAlignment="Stretch"
>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition />
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding ArticleID}"
Grid.Column="0"
/>
<TextBlock Text="{Binding ArticleName}"
Grid.Column="1"
/>
<Button Command="{Binding }"
Grid.Column="2"
>
<Button.Content>
<StackPanel Style="{StaticResource IconTextCombo}">
<Image Source="{StaticResource img}"/>
</StackPanel>
</Button.Content>
</Button>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I have tested it with ListBox and ListView... It works for both of them as I want now!
Thanks to all helpers!
you need to use the SharedSizeGroup property on your grid in your template. It works in your hardcoded example because all your elements sit inside the same grid. Once you place them inside a ListBox every item has its own grid and is unaware of the other grids.
Setting this property will make sure all these columns have the same size regardless which grid they are in.
your Grid.ColumnsDefinitions should look like this:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" SharedSizeGroup="ColumnA"/>
<ColumnDefinition Width="auto" SharedSizeGroup="ColumnB"/>
<ColumnDefinition Width="auto" SharedSizeGroup="ButtonsColumn"/>
</Grid.ColumnDefinitions>
You should also use ItemTemplate meaning:
<ListBox>
<ListBox.ItemTemplate>
*****Your Posted Code ************
</ListBox.ItemTemplate>
</ListBox>

LongListMultiSelector Data Template stretches outside screen width - windows phone 8

I've the following pivot control in my application
<phone:PivotItem Header="{Binding Path=LocalizedResources.requests_title, Source={StaticResource LocalizedStrings}}" >
<Grid Margin="0,-12,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" >
<TextBlock FontWeight="Bold"
TextDecorations="Underline"
VerticalAlignment="Top"
TextWrapping="NoWrap"
FontSize="{StaticResource PhoneFontSizeMediumLarge}"
Foreground="{StaticResource PhoneAccentBrush}"
toolkit:TurnstileFeatherEffect.FeatheringIndex="1"/>
<toolkit:PhoneTextBox AcceptsReturn="False"
TextWrapping="NoWrap"
Visibility="Collapsed"
TextChanged="SearchBox_TextChanged"
toolkit:TurnstileFeatherEffect.FeatheringIndex="1"/>
</StackPanel>
<toolkit:LongListMultiSelector Grid.Row="1"
ItemsSource="{Binding Details_OC}"
SelectionChanged="Requests_SelectionChanged"
toolkit:TurnstileFeatherEffect.FeatheringIndex="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Margin="0,0,24,0">
<toolkit:LongListMultiSelector.ItemTemplate>
<DataTemplate>
<Border BorderThickness="0.25" BorderBrush="{StaticResource PhoneAccentBrush}">
<Grid Background="{StaticResource TransparentBrush}" HorizontalAlignment="Stretch" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal"
Grid.Row="0"
Grid.Column="0"
HorizontalAlignment="Left"
VerticalAlignment="Top" >
<TextBlock Text="#" FontSize="{StaticResource PhoneFontSizeLarge}" />
<TextBlock x:Name="ID"
Text="{Binding ID}"
FontSize="{StaticResource PhoneFontSizeLarge}"
TextWrapping="NoWrap"/>
</StackPanel>
<TextBlock x:Name="date"
Text="{Binding Path=TIME, Converter={StaticResource dateConverter}}"
Grid.Row="0"
Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Top"
FontSize="{StaticResource PhoneFontSizeSmall}"
TextWrapping="NoWrap"/>
</Grid>
</Border>
</DataTemplate>
</toolkit:LongListMultiSelector.ItemTemplate>
</toolkit:LongListMultiSelector>
</Grid>
</phone:PivotItem>
The result appears as follows
You can see that the date Textblock is not appearing fully. I couldn't find the reason for this unexpected behavior. Please help.
Thank you.
The problem appears to be in the sizing of your columns. See the following, inside the <toolkit:LongListMultiSelector.ItemTemplate>:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
In this case I would give the first column a static width and then the second can fill the remaining space. Like this:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
Alternatively you may wish to take a look into the ItemInfoTemplate property of the LongListMultiSelector.
It gives you the ability to display a second column at the far right which does not get squashed over by the checkboxes when they become active.
An example can be found here.

Use placeholders in controltemplate

I have a number of grids that have almost the same layout. This is an example
<Grid x:Name="OptionChangeUserState" Grid.Row="0" Style="{StaticResource MenuItemGridStyle}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="30" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Style="{StaticResource MenuItemTextblockStyle}" Text="Status wijzigen" Margin="0"/>
<Image Grid.Column="1" Source="/UserControlSolution;component/Image/user.png" Margin="5" />
</Grid>
I want to turn this in a controltemplate like here :no template property on grid style in WPF?
But how can I change the Textblock text and Image source when I use a controltemplate ?
You can achieve this by creating the one model with Text and Source properties. Then depending on the different number of instance where you want to use this ControlTemplate you can set the model instance in the Tag of your Grid.
<local:MyModel x:Key="myModel1" Text="" Source=""/>
<ContentControl Tag="{StaticResource myModel1}" Template={StaticResource myTemplate}>
</ContentControl>
and in template
<ControlTemplate x:Key="myTemplate" TargetType="ContentControl">
<Grid x:Name="OptionChangeUserState" Grid.Row="0" Style="{StaticResource MenuItemGridStyle}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="30" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Style="{StaticResource MenuItemTextblockStyle}" Text="{Binding Tag.Text, RelativeSource={RelativeSource TemplatedParent}}" Margin="0"/>
<Image Grid.Column="1" Source="{Binding Tag.Source, RelativeSource={RelativeSource TemplatedParent}}" Margin="5" />
</Grid>
</ControlTemplate>
As far as I am aware, you can only do this with a DataTemplate and only if each of your data bound classes had properties of the same name, in this case, TextValue and ImageSource. I don't think that you can get access to the data object directly from the ControlTemplate.
<DataTemplate>
<Grid x:Name="OptionChangeUserState" Grid.Row="0" Style="{StaticResource MenuItemGridStyle}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="30" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Style="{StaticResource MenuItemTextblockStyle}" Text="{Binding TextValue}" Margin="0"/>
<Image Grid.Column="1" Source="{Binding ImageSource}" Margin="5" />
</Grid>
</DataTemplate>
You'd also need to use the ContentTemplate property instead.
<ContentControl Content="{Binding DataObject}"
ContentTemplate="{StaticResource Template}" />
This can be done with data-binding, but it would be preferable to use a property interface so that you can simply type something like:
<local:OptionControl Text="Status wijzigen" ImageSource="/UserControlSolution;component/Image/user.png" />
This way you don't need to have your UI labels and icons in a separate data layer.
This can be achieved by creating a control class which defines Dependency Properties, and using TemplateBindings to bind to them in the ControlTemplate.
It's possible that an existing control has appropriate properties that you can create a template for. This HeaderedContentControl template should work for you, although a custom control would be nicer:
<Style x:Key="HeaderedContentControlStyle" TargetType="HeaderedContentControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="HeaderedContentControl">
<Grid Style="{StaticResource MenuItemGridStyle}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="30" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Style="{StaticResource MenuItemTextblockStyle}" Text="{TemplateBinding Header}" />
<Image Grid.Column="1" Source="{TemplateBinding Content}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Usage:
<HeaderedContentControl Name="OptionChangeUserState" Grid.Row="0"
Style="{StaticResource HeaderedContentControlStyle}"
Header="Status wijzigen">
<BitmapImage>/UserControlSolution;component/Image/user.png</BitmapImage>
</HeaderedContentControl>
Note that we need to wrap the image path in BitmapImage explicitly. This is because the HeaderedContentControl.Content property is not declared as an image type, so WPF will not automatically convert it.
Use DataTemplate with some sort of DTO, DictionaryEntry will do for two items.
<DataTemplate DataType="{x:Type Collections:DictionaryEntry}" x:Key="gridTemplate">
<Grid Style="{StaticResource MenuItemGridStyle}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="30" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Style="{StaticResource MenuItemTextblockStyle}" Text="{Binding Key}" Margin="0"/>
<Image Grid.Column="1" Source="{Binding Value}" Margin="5" />
</Grid>
</DataTemplate>
Usage:
<ContentControl ContentTemplate="{StaticResource gridTemplate}">
<ContentControl.Content>
<Collections:DictionaryEntry Key="Status wijzigen" Value="/UserControlSolution;component/Image/user.png" />
</ContentControl.Content>

Categories