-  [Wpf] 指定した行数を超えた文字を省略するTextBlock
FC2ブログ

[Wpf] 指定した行数を超えた文字を省略するTextBlock

2019.07.05(Fri) | EDIT

前回の更新から1ヶ月たったので小ネタを投下。

折り返し可能なTextBlockを画面に表示した際、設定した文字が長すぎてレイアウトが崩れてしまった経験はありませんか?
その際に作成したBehaviorを紹介します。
このBehaviorを使用すると、指定された行数を超えた文字が省略されるのでレイアウトが崩れなくなります。

Behavior
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
public static class TextBlockWrappingBehavior
{
    /// <summary>
    /// テキストブロックに表示可能な最大行数の依存関係プロパティ
    /// </summary>
    public static DependencyProperty MaxLinesProperty =
        DependencyProperty.RegisterAttached("MaxLines",
            typeof(int),
            typeof(TextBlockWrappingBehavior),
            new FrameworkPropertyMetadata(int.MinValue, OnSetMaxLinesCallback)
        );
 
    /// <summary>
    /// テキストブロックに表示可能な最大行数を設定します(添付ビヘイビア)
    /// </summary>
    /// <param name="target">対象</param>
    /// <param name="value">テキストブロックに表示可能な最大行数</param>
    public static void SetMaxLines(DependencyObject target, object value)
    {
        target.SetValue(MaxLinesProperty, value);
    }
 
    /// <summary>
    /// テキストブロックに表示可能な最大行数を取得します(添付ビヘイビア)
    /// </summary>
    /// <param name="target">対象</param>
    public static int GetMaxLines(DependencyObject target)
    {
        return (int)target.GetValue(MaxLinesProperty);
    }
 
    /// <summary>
    /// MaxLinesプロパティが変更された際の処理
    /// </summary>
    private static void OnSetMaxLinesCallback(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        var block = target as TextBlock;
 
        if (block == null)
        {
            return;
        }
        if (block.TextWrapping == TextWrapping.NoWrap)
        {
            // 改行設定
            block.TextWrapping = TextWrapping.Wrap;
        }
        if (block.TextTrimming == TextTrimming.None)
        {
            // 省略文字設定
            block.TextTrimming = TextTrimming.CharacterEllipsis;
        }
 
        var binding = BindingOperations.GetBinding(block, TextBlock.TextProperty);
 
        if (binding == null)
        {
            return;
        }
        if (!binding.NotifyOnTargetUpdated)
        {
            // Textプロパティ変更時に通知されるようにする
            BindingOperations.SetBinding(block, TextBlock.TextProperty, new Binding()
            {
                Path = binding.Path,
                NotifyOnTargetUpdated = true
            });
        }
 
        block.TargetUpdated += (sender, args) =>
        {
            // 行の高さが設定されている場合はその値、未設定であればFormattedTextの高さを行の高さとする
            var line = double.IsInfinity(block.LineHeight) || double.IsNaN(block.LineHeight)
                ? GetFormattedTextHeight(block)
                : block.LineHeight;
 
            // 最大高さ設定
            block.MaxHeight = line * GetMaxLines(block);
        };
    }
 
    /// <summary>
    /// TextBlockの文字をFormattedText化して、その高さを取得します。
    /// </summary>
    /// <param name="block">オブジェクト</param>
    private static double GetFormattedTextHeight(TextBlock block)
    {
        var formatted = new FormattedText(block.Text ?? string.Empty,
            new CultureInfo("ja-jp"),
            FlowDirection.LeftToRight,
            new Typeface(block.FontFamily, block.FontStyle, block.FontWeight, block.FontStretch),
            block.FontSize,
            block.Foreground
        );
        return formatted.Height;
    }
}

使い方:wpf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<Window x:Class="WpfApp15.MainWindow"
        xmlns:local="clr-namespace:WpfApp15"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <StackPanel >
 
        <StackPanel.Resources>
            <Style TargetType="TextBlock">
                <Setter Property="local:TextBlockWrappingBehavior.MaxLines" Value="3" />
            </Style>
        </StackPanel.Resources>
 
        <TextBlock Text="{Binding Text1}" Foreground="Blue" />
        <TextBlock Text="{Binding Text2, NotifyOnTargetUpdated=True}" Foreground="Red" />
        <TextBlock Text="{Binding Text3, NotifyOnTargetUpdated=False}" Foreground="Purple" LineHeight="40" />
         
    </StackPanel>
</Window>

使い方:コードビハインド
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
 
        Text1 = string.Join("", Enumerable.Range(0, 100).Select(i => "W"));
        Text2 = string.Join("", Enumerable.Range(0, 200).Select(i => "W"));
        Text3 = string.Join("", Enumerable.Range(0, 300).Select(i => "W"));
 
        DataContext = this;
    }
 
    public string Text1 { get; set; }
    public string Text2 { get; set; }
    public string Text3 { get; set; }
}

このBehaviorを使う場合はTextWrapping、TextTrimming、Binding.NotifyOnTargetUpdatedを設定する必要がありますが、使ってて良く忘れるのでBehavior側で設定するようにしています。

GitHub

コメント

PageTop↑

コメントの投稿


プロフィール

FC2USER615689ZRX

Author:FC2USER615689ZRX
FC2ブログへようこそ!

最新トラックバック

カウンター

FC2無料カウンターFC2無料カウンターFC2無料カウンターFC2無料カウンターFC2無料カウンター

検索フォーム

ブロとも申請フォーム

QRコード

QR