1

I need textblock, that could toggle between full text and trimmed text, something like this in a picture:

enter image description here

And could not find anything similar.

Expander is something like that, but it would show trimmed and full version by default (trimmed in header, full in content).

I thought to write something with attached properties, but I need to paint button, check if it needs button at all (text is not trimmed), do the collapse and expand logic - in short I'm feeling like inventing wheel here, someone should have done this before, just probably I can't find it?

Giedrius
  • 8,430
  • 6
  • 50
  • 91

3 Answers3

5

I'd make a usercontrol to handle the case. Should be easy... I've crafted this quickly on a ContentControl but could be easily expanded to suit your needs:

<ControlTemplate x:Key="MyControl" TargetType="ContentControl">
  <StackPanel>
    <Expander x:Name="ExpanderControl" IsExpanded="True"/>
    <TextBlock x:Name="MyTextBlock" TextTrimming="CharacterEllipsis" TextWrapping="Wrap" Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ex est, hendrerit a ante nec, molestie aliquet nunc. Mauris est mi, aliquam nec nulla id, placerat vulputate est. Nulla tristique elit eu sapien ultrices fringilla. Nulla ac dictum lorem, ac mattis elit. Sed augue arcu, bibendum nec mauris nec, volutpat pulvinar lacus. Mauris et volutpat nibh, eu luctus leo. Pellentesque non dapibus nisi.
Quisque in nulla eu purus sagittis bibendum. Pellentesque orci ipsum, porttitor vitae risus at, faucibus pellentesque justo. Aenean ut nibh pharetra orci euismod vehicula. Aenean commodo vestibulum placerat. Nam condimentum dictum purus nec suscipit. Duis semper ligula massa, in pretium diam scelerisque tincidunt. Vivamus placerat porttitor orci et finibus.
Aenean ut purus eu mi venenatis dapibus id vel urna. Donec enim odio, molestie sed pharetra vel, blandit non purus. Sed in leo eget felis suscipit consectetur. Suspendisse sagittis, sapien ac iaculis venenatis, velit purus viverra turpis, vitae suscipit mi odio pellentesque velit. Proin pulvinar sem consequat nunc varius semper. Maecenas vitae nisl quis risus auctor suscipit. Aenean libero tortor, placerat non ex et, gravida efficitur sem. Aliquam volutpat mauris fermentum, rhoncus arcu et, finibus tortor.
Praesent sit amet pretium risus. Quisque bibendum nibh vel risus rhoncus eleifend. Nunc in eros sit amet neque euismod laoreet. Aliquam dictum ac magna quis interdum. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis varius diam dolor, mollis tristique nisl malesuada ac. Nulla sed vehicula sapien. Vestibulum venenatis lobortis sodales. Aliquam rhoncus metus at velit accumsan, at fringilla sapien condimentum. Aliquam erat volutpat. Aenean iaculis mi augue, quis bibendum nibh suscipit ut. Nam commodo, arcu et gravida accumsan, magna diam rhoncus ante, quis posuere nibh tortor nec ligula. Quisque id metus lacus. Quisque ac magna vel ligula commodo tempor. Nam scelerisque fringilla commodo. Curabitur volutpat libero ac metus aliquam, in egestas ipsum luctus.
Ut fringilla lacinia efficitur. Nulla odio tortor, eleifend sed porttitor ut, accumsan sed nunc. Morbi malesuada nisl in lobortis auctor. Nam suscipit neque ac neque blandit dictum. Duis pulvinar commodo enim eget laoreet. In sodales arcu nisl, sit amet posuere orci blandit nec. Nullam a dapibus est, lacinia maximus ante. Mauris rutrum ex nunc, non vehicula orci volutpat vehicula. Praesent mattis tortor non odio molestie, sit amet congue urna sollicitudin. Sed lobortis est et mauris suscipit mattis. Maecenas porttitor elit nec nulla pulvinar, nec porttitor odio placerat. Vivamus maximus lobortis erat a fringilla. Sed vitae hendrerit tortor." />
  </StackPanel>
  <ControlTemplate.Triggers>
    <Trigger SourceName="ExpanderControl" Property="IsExpanded" Value="False">
      <Trigger.Setters>
        <Setter TargetName="MyTextBlock" Property="Height" Value="100"/>
      </Trigger.Setters>
    </Trigger>
  </ControlTemplate.Triggers>
</ControlTemplate>

And use it like:

<ContentControl Template="{StaticResource MyControl}"/>

Result, expanded:

enter image description here

And contracted:

enter image description here

Of course, you'd need to style it to your needs. Could (and should) make a property to have the text defined in the UserControl and not in the template, the desired contracted height, etc.

Checking if it actually needs a button requires some codebehind (checking if Height is ActualHeight comes to mind, but for a better way you'd ideally create a FormattedText object with the TextBlock's typeface, apply the same text and check if it's height is the ActualHeight of the control... I'd put this on an attached dependency property).

Jcl
  • 27,696
  • 5
  • 61
  • 92
  • cool, I have two answers in several minutes and both gave very good thoughts, not sure which one mark as an answer. I liked the idea to use empty expander as a button. – Giedrius Jan 08 '15 at 08:49
  • I like my idea better than the other answer since it will allow you to have different "expanded" and "contracted" heights (the other answer will be "one-line" vs "multiline"), and doesn't require an external property or command. However, neither mine nor the other are complete to your needs (they won't hide the button if not needed, etc.), they are only giving ideas, so mark any that suit your needs better... or wait for other more complete answers if you want. – Jcl Jan 08 '15 at 08:55
  • I find this solution better, a custom UserControl is much easier to use for this case. As long as you use what Sinatr suggested in the comment for my solution ( http://stackoverflow.com/questions/1041820/how-can-i-determine-if-my-textblock-text-is-being-trimmed ) to indicate whether you need a button, you should be good. – Moti Azu Jan 08 '15 at 09:06
  • Notice that if the textblock container won't be tall enough to hold the whole text, it'll be trimmed also when expanded. That solution "by itself" won't work, and there's more to be checked for this particular case. Also, checking against `ActualHeight` will not work if the expander is contracted by default (it'll be trimmed even if the whole text would be visible when expanded). It should have to be checked against the "contracted height", ant not against `ActualHeight`. – Jcl Jan 08 '15 at 09:12
3

You can use a TextBlock and just add a button that will toggle it's TextWrapping property.

<TextBlock Text="{Binding Text}" TextWrapping="{Binding WrapMode}" />
<Button Command="{Binding ToggleWrapMode}" />

Make the button's command toggle between TextWrapping.NoWrap and TextWrapping.Wrap. Also, make sure you set the height of the textblock (or it's container) so the wrapping will happen at the size you want.

Update: In order to find out whether you need the button to be visible, you can initialize WrapMode to NoWrap and check if the ActualHeight of the TextBlock is higher than the threshold you defined. If it is higher - you put the limitation on the Height of the TextBlock, otherwise you just hide the button.

I do find this method quite messy (and might cause a flicker on the screen), I'll try to think of something smarter.

Update 2: As Sinatr suggest in the command, this is a much nicer way to know if your text needs wrapping.

Community
  • 1
  • 1
Moti Azu
  • 5,392
  • 1
  • 23
  • 32
  • How do you hide the button when the text does not exceed the collapsed state's size? – Peter Duniho Jan 08 '15 at 08:24
  • You don't have to hide it, according to the picture you attached the button stays and just switches it's visual content. – Moti Azu Jan 08 '15 at 08:25
  • The picture only shows the two states when the text is _not_ short enough to fit the collapsed state. Note from the OP: "check if it needs button at all". I.e. the button will not be displayed if the text fits the collapsed state (a condition not illustrated in the graphics provided in the question). – Peter Duniho Jan 08 '15 at 08:26
  • 1
    @PeterDuniho, button visibility can be controlled when `Text` [get changed](http://stackoverflow.com/q/703167/1997232), if [text is trimmed](http://stackoverflow.com/q/1041820/1997232). – Sinatr Jan 08 '15 at 08:58
0

I've combined ideas from two given answers and got quite pleasant result, hope someone will find it useful. Basic idea is, that I'm using empty Expander to toggle TextWrapping for a TextBlock. It needed C# code in single place only - to know if TextBlock is currently trimming text, I've borrowed code from https://stackoverflow.com/a/25436070/212121

<ControlTemplate x:Key="ExpandableTextBlock" TargetType="ContentControl">
        <Grid Focusable="False" Background="Transparent" Margin="4" VerticalAlignment="Center">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition x:Name="ExpandButtonColumnDefinition">
                    <ColumnDefinition.Style>
                        <Style TargetType="{x:Type ColumnDefinition}">
                            <Setter Property="Width" Value="50" />
                            <Style.Triggers>
                                <MultiDataTrigger>
                                    <MultiDataTrigger.Conditions>
                                        <Condition Binding="{Binding ElementName=TextBlock, Path=(wpfApplication8:TextBlockService.IsTextTrimmed)}" Value="False" />
                                        <Condition Binding="{Binding ElementName=TextBlock, Path=TextWrapping}" Value="NoWrap" />
                                    </MultiDataTrigger.Conditions>
                                    <MultiDataTrigger.Setters>
                                        <Setter Property="Width" Value="0" />
                                    </MultiDataTrigger.Setters>
                                </MultiDataTrigger>                                 
                            </Style.Triggers>
                        </Style>
                    </ColumnDefinition.Style>
                </ColumnDefinition>
            </Grid.ColumnDefinitions>
            <StackPanel x:Name="ViewPort" Orientation="Vertical" Margin="1" VerticalAlignment="Center" Grid.Column="0">
                <TextBlock x:Name="TextBlock" TextTrimming="CharacterEllipsis" Text="{TemplateBinding Content}" />                  
            </StackPanel>
            <Expander Grid.Column="1" x:Name="TextExpander" />                  
        </Grid>
        <ControlTemplate.Triggers>
            <Trigger SourceName="TextExpander" Property="IsExpanded" Value="True">
                <Setter TargetName="TextBlock" Property="TextWrapping" Value="Wrap" />
            </Trigger>
            <Trigger SourceName="TextExpander" Property="IsExpanded" Value="False">
                <Setter TargetName="TextBlock" Property="TextWrapping" Value="NoWrap" />
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>
Community
  • 1
  • 1
Giedrius
  • 8,430
  • 6
  • 50
  • 91