Search code examples
wpfxamltextblock

Display only two lines for a long text in a column


I have column in DataGrid to display a text message. Unfortunately it is too long. So I custom the cell column template in a textblock use TextWrapping = "Wrap"

It displays multiple lines. I don't want it. I just want to display the first two lines, at the end adding an ellipsis(...)

Is there a way to do that?


Solution

  • To achieve that you need to define a custom Behavior, first make sure to add the System.Windows.Interactivity namespace ( which is part of Expression.Blend.Sdk, use NuGet to install it: Install-Package Expression.Blend.Sdk), here a basic implementation (credit goes to @Itzalive):

    public class NumLinesBehaviour : Behavior<TextBlock>
        {
            public static readonly DependencyProperty MaxLinesProperty =
                DependencyProperty.RegisterAttached(
                    "MaxLines",
                    typeof(int),
                    typeof(NumLinesBehaviour),
                    new PropertyMetadata(default(int), OnMaxLinesPropertyChangedCallback));
    
            public static void SetMaxLines(DependencyObject element, int value)
            {
                element.SetValue(MaxLinesProperty, value);
            }
    
            public static int GetMaxLines(DependencyObject element)
            {
                return (int)element.GetValue(MaxLinesProperty);
            }
    
            private static void OnMaxLinesPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                if (d is TextBlock element) element.MaxHeight = GetLineHeight(element) * GetMaxLines(element);
            }
    
            public static readonly DependencyProperty MinLinesProperty =
                DependencyProperty.RegisterAttached(
                    "MinLines",
                    typeof(int),
                    typeof(NumLinesBehaviour),
                    new PropertyMetadata(default(int), OnMinLinesPropertyChangedCallback));
    
            public static void SetMinLines(DependencyObject element, int value)
            {
                element.SetValue(MinLinesProperty, value);
            }
    
            public static int GetMinLines(DependencyObject element)
            {
                return (int)element.GetValue(MinLinesProperty);
            }
    
            private static void OnMinLinesPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                if (d is TextBlock element) element.MinHeight = GetLineHeight(element) * GetMinLines(element);
            }
    
            private static double GetLineHeight(TextBlock textBlock)
            {
                double lineHeight = textBlock.LineHeight;
                if (double.IsNaN(lineHeight))
                    lineHeight = Math.Ceiling(textBlock.FontSize * textBlock.FontFamily.LineSpacing);
                return lineHeight;
            }
        }
    

    Now let's say you have a DataGrid Bound to an ObservableCollection of TestClass with the "Name" property, a basic use of the NumLinesBehaviour Behavior is as follow:

    <Window ...   
        xmlns:local="clr-namespace:YourNameSpace"       
        Title="MainWindow" Height="350" Width="525" DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Window.Resources>
        <DataTemplate x:Key="CellTemplate">
            <StackPanel Orientation="Horizontal">
                <TextBlock 
                    Width="200"
                    TextWrapping="Wrap" 
                    local:NumLinesBehaviour.MaxLines="2"
                    TextTrimming="WordEllipsis" 
                    LineStackingStrategy="BlockLineHeight"
                    Text="{Binding Name}"/>
    
            </StackPanel>
        </DataTemplate>
    </Window.Resources>
    
    <Grid>
        <DataGrid ItemsSource="{Binding DgCollection}" AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTemplateColumn Header="Name" CellTemplate="{StaticResource CellTemplate}" />
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
    

    Make sure to set the TextTrimming of the TextBlock to "WordEllipsis".

    Update

    The output looks something like that:

    output