В WPF очень приятно то, что все элементы управления выглядят очень неприглядно. Из-за этого мы можем использовать TextBox , который имеет свойство LineCount (почему это не DependencyProperty или почему TextBlock также не имеет его, я не знаю). С помощью TextBox мы можем просто изменить его шаблон, чтобы он вел себя и выглядел больше как TextBlock. В нашем пользовательском стиле / шаблоне мы собираемся установить для IsEnabled значение False и просто создать базовый повторный шаблон элемента управления, чтобы отключенный вид больше не присутствовал. Мы также можем связать любые свойства, которые мы хотим сохранить, например, Background, используя TemplateBindings.
<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, но как мы можем получить количество строк?
Что ж, если мы хотим получить к нему доступ непосредственно в коде, то мы можем зарегистрироваться в событии SizeChanged TextBox.
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 для обработки поиска и настройка LineCount. Мы собираемся использовать AttachedProperties, чтобы сделать то же самое, но теперь мы сможем использовать его с любым TextBox в любом месте и привязать к нему через этот TextBox вместо DataContext окна.
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>