TextWrapping を "Wrap" に設定すると、WPF TextBlock に複数行のテキストを含めることができます。テキストの行数を取得する「クリーンな」方法はありますか? 希望の高さを見て、それを各行の推定高さで割ることを検討しました。しかし、それはかなり汚いようです。より良い方法はありますか?
4 に答える
WPF の非常に優れた点の 1 つは、すべてのコントロールが非常に見た目がよくないことです。このため、 LineCount プロパティを持つTextBoxを利用できます (なぜそれが DependencyProperty ではないのか、なぜ TextBlock にもそれがないのかはわかりません)。TextBox を使用すると、単純にテンプレートを再作成して、TextBlock に似た動作と外観にすることができます。カスタム スタイル/テンプレートでは、IsEnabled を False に設定し、コントロールの基本的な再テンプレートを作成して、無効な外観が存在しないようにします。TemplateBindings を使用して、Background など、維持したいプロパティをバインドすることもできます。
<Style x:Key="Local_TextBox"
TargetType="{x:Type TextBoxBase}">
<Setter Property="IsEnabled"
Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBoxBase}">
<Border Name="Border"
Background="{TemplateBinding Background}">
<ScrollViewer x:Name="PART_ContentHost" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
これで、TextBox の外観と動作を TextBlock のようにすることができますが、行数を取得するにはどうすればよいでしょうか。
コード ビハインドで直接アクセスしたい場合は、TextBox の SizeChanged イベントに登録できます。
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
LongText = "This is a long line that has lots of text in it. Because it is a long line, if a TextBlock's TextWrapping property is set to wrap then the text will wrap onto new lines. However, we can also use wrapping on a TextBox, that has some diffrent properties availible and then re-template it to look just like a TextBlock!";
uiTextBox.SizeChanged += new SizeChangedEventHandler(uiTextBox_SizeChanged);
this.DataContext = this;
}
void uiTextBox_SizeChanged(object sender, SizeChangedEventArgs e)
{
Lines = uiTextBox.LineCount;
}
public string LongText { get; set; }
public int Lines
{
get { return (int)GetValue(LinesProperty); }
set { SetValue(LinesProperty, value); }
}
// Using a DependencyProperty as the backing store for Lines. This enables animation, styling, binding, etc...
public static readonly DependencyProperty LinesProperty =
DependencyProperty.Register("Lines", typeof(int), typeof(MainWindow), new UIPropertyMetadata(-1));
}
ただし、現在のウィンドウ以外の場所でそのようなプロパティを使用する必要がある、および/または MVVM を使用していてそのアプローチを取りたくない傾向があるため、AttachedProperties をいくつか作成して、の取得と設定を処理できます。行数。AttachedProperties を使用して同じことを行いますが、これを任意の TextBox で使用し、ウィンドウの DataContext の代わりにその TextBox を介してバインドできるようになりました。
public class AttachedProperties
{
#region BindableLineCount AttachedProperty
public static int GetBindableLineCount(DependencyObject obj)
{
return (int)obj.GetValue(BindableLineCountProperty);
}
public static void SetBindableLineCount(DependencyObject obj, int value)
{
obj.SetValue(BindableLineCountProperty, value);
}
// Using a DependencyProperty as the backing store for BindableLineCount. This enables animation, styling, binding, etc...
public static readonly DependencyProperty BindableLineCountProperty =
DependencyProperty.RegisterAttached(
"BindableLineCount",
typeof(int),
typeof(MainWindow),
new UIPropertyMetadata(-1));
#endregion // BindableLineCount AttachedProperty
#region HasBindableLineCount AttachedProperty
public static bool GetHasBindableLineCount(DependencyObject obj)
{
return (bool)obj.GetValue(HasBindableLineCountProperty);
}
public static void SetHasBindableLineCount(DependencyObject obj, bool value)
{
obj.SetValue(HasBindableLineCountProperty, value);
}
// Using a DependencyProperty as the backing store for HasBindableLineCount. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HasBindableLineCountProperty =
DependencyProperty.RegisterAttached(
"HasBindableLineCount",
typeof(bool),
typeof(MainWindow),
new UIPropertyMetadata(
false,
new PropertyChangedCallback(OnHasBindableLineCountChanged)));
private static void OnHasBindableLineCountChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var textBox = (TextBox)o;
if ((e.NewValue as bool?) == true)
{
textBox.SetValue(BindableLineCountProperty, textBox.LineCount);
textBox.SizeChanged += new SizeChangedEventHandler(box_SizeChanged);
}
else
{
textBox.SizeChanged -= new SizeChangedEventHandler(box_SizeChanged);
}
}
static void box_SizeChanged(object sender, SizeChangedEventArgs e)
{
var textBox = (TextBox)sender;
(textBox).SetValue(BindableLineCountProperty, (textBox).LineCount);
}
#endregion // HasBindableLineCount AttachedProperty
}
これで、LineCount を簡単に見つけることができます。
<StackPanel>
<TextBox x:Name="uiTextBox"
TextWrapping="Wrap"
local:AttachedProperties.HasBindableLineCount="True"
Text="{Binding LongText}"
Style="{StaticResource Local_TextBox}" />
<TextBlock Text="{Binding Lines, StringFormat=Binding through the code behind: {0}}" />
<TextBlock Text="{Binding ElementName=uiTextBox, Path=(local:AttachedProperties.BindableLineCount), StringFormat=Binding through AttachedProperties: {0}}" />
</StackPanel>
簡単な方法は LineCount プロパティです。また、GetLastVisibleLineIndex というメソッドがあり、テキスト ボックスに (スクロール バーなしで) 表示できる行数を知らせます。
行がいつ追加されたかを知りたい場合は、TextChanged イベントで聞き、LineCount プロパティについて尋ねることができます (比較を行うには、las LineCount を変数に保持する必要があります)。