Wpf 文本块的可见行计数
如果将TextWrapping设置为“Wrap”,则WPF TextBlock可以有几行文本。Wpf 文本块的可见行计数,wpf,wpf-controls,textblock,Wpf,Wpf Controls,Textblock,如果将TextWrapping设置为“Wrap”,则WPF TextBlock可以有几行文本。 有没有一种“干净”的方法来获取文本行数?我考虑看一下所需的高度,然后除以每条线的估计高度。然而,这似乎相当肮脏。有更好的方法吗?关于WPF,有一点非常好,那就是所有的控件都非常不好看。因此,我们可以利用,它有一个LineCount属性(为什么它不是DependencyProperty,或者为什么TextBlock也没有它,我不知道)。有了TextBox,我们可以简单地重新设置它的模板,使其行为和外观更
有没有一种“干净”的方法来获取文本行数?我考虑看一下所需的高度,然后除以每条线的估计高度。然而,这似乎相当肮脏。有更好的方法吗?关于WPF,有一点非常好,那就是所有的控件都非常不好看。因此,我们可以利用,它有一个LineCount属性(为什么它不是DependencyProperty,或者为什么TextBlock也没有它,我不知道)。有了TextBox,我们可以简单地重新设置它的模板,使其行为和外观更像一个TextBlock。在我们的自定义样式/模板中,我们将IsEnabled设置为False,只需创建控件的基本重新模板,这样禁用的外观就不再存在。我们还可以通过使用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>
但是,由于我倾向于在当前窗口之外的其他位置使用类似的属性,并且/或者我正在使用MVVM,并且不想采用这种方法,因此我们可以创建一些AttachedProperties来处理LineCount的检索和设置。我们将使用AttachedProperties来做同样的事情,但是现在我们可以将它用于任何地方的文本框,并通过该文本框而不是窗口的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>
简单的方法是使用LineCount属性。还有一个名为GetLastVisibleLineIndex的方法,可以让您知道文本框可以显示多少行(没有滚动条) 如果您想知道何时添加一行,您可以在TextChanged事件中听到,并询问LineCount属性(您需要将las LineCount保留在一个变量中以进行比较)。
//这似乎可以完成这项工作
///
///我们需要在输入时限制多行文本框
///
///
///发送者。
///
///
///e。
///
private void DescriptionTextBox_PreviewKeyDown(对象发送方,System.Windows.Input.KeyEventArgs e)
{
TextBox thisttextbox=发送者作为TextBox;
如果(thisTextBox!=null)
{
//只检查我们是否通过了MaxLines
如果(thisTextBox.LineCount>thisTextBox.MaxLines)
{
//我们将丢弃最后输入的字符
int numChars=thisttextbox.Text.Length;
//强行解决问题
thisTextBox.Text=thisTextBox.Text.Substring(0,numChars-1);
//将光标设置回最后一个允许的字符
thisTextBox.SelectionStart=numChars-1;
//不允许传入密钥
e、 已处理=正确;
}
}
}
我发现这个问题已经存在了7年,但我刚刚提出了一个解决方案:
TextBlock有一个名为LineCount的私有属性。我创建了一个扩展方法来读取此值:
public static class TextBlockExtension
{
public static int GetLineCount(this TextBlock tb)
{
var propertyInfo = GetPrivatePropertyInfo(typeof(TextBlock), "LineCount");
var result = (int)propertyInfo.GetValue(tb);
return result;
}
private static PropertyInfo GetPrivatePropertyInfo(Type type, string propertyName)
{
var props = type.GetProperties(BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.NonPublic);
return props.FirstOrDefault(propInfo => propInfo.Name == propertyName);
}
}
这太棒了。但是,TextBox比TextBlock更受限制,因为它们具有统一的字体系列/字体大小。因此,很容易计算行数。另一方面,文本块可以具有不同高度的不同内联线。这使事情变得有些困难。TextBlock没有LineCount属性。这仅仅是TextBox的领域。问题是关于TextBlock而不是TextBox。
// this seems to do the job
<TextBox x:Name="DescriptionTextBox"
Grid.Row="03"
Grid.RowSpan="3"
Grid.Column="01"
Width="100"
AcceptsReturn="True"
MaxLength="100"
MaxLines="3"
PreviewKeyDown="DescriptionTextBox_PreviewKeyDown"
Text="{Binding Path=Description,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
TextWrapping="Wrap" />
/// <summary>
/// we need to limit a multi line textbox at entry time
/// </summary>
/// <param name="sender">
/// The sender.
/// </param>
/// <param name="e">
/// The e.
/// </param>
private void DescriptionTextBox_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
TextBox thisTextBox = sender as TextBox;
if (thisTextBox != null)
{
// only check if we have passed the MaxLines
if (thisTextBox.LineCount > thisTextBox.MaxLines)
{
// we are going to discard the last entered character
int numChars = thisTextBox.Text.Length;
// force the issue
thisTextBox.Text = thisTextBox.Text.Substring(0, numChars - 1);
// set the cursor back to the last allowable character
thisTextBox.SelectionStart = numChars - 1;
// disallow the key being passed in
e.Handled = true;
}
}
}
public static class TextBlockExtension
{
public static int GetLineCount(this TextBlock tb)
{
var propertyInfo = GetPrivatePropertyInfo(typeof(TextBlock), "LineCount");
var result = (int)propertyInfo.GetValue(tb);
return result;
}
private static PropertyInfo GetPrivatePropertyInfo(Type type, string propertyName)
{
var props = type.GetProperties(BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.NonPublic);
return props.FirstOrDefault(propInfo => propInfo.Name == propertyName);
}
}