[英]Limit number of lines entered in WPF textbox
我試圖限制用戶可以在文本框中輸入的行數。
我一直在研究 - 我能找到的最接近的是: 限制文本框中每行的最大字符數 。
並限制文本框中每行的最大字符數 ,結果是winforms。
這不是我所追求的......還值得一提的是,有一個誤導性的maxlines屬性我發現只限制了文本框中顯示的內容。
我的要求:
這些要求用於創建WYSIWYG文本框,該文本框將用於捕獲最終將被打印的數據,並且字體需要更改 - 如果文本被切斷或對於固定大小的行太大 - 那么它將會出現印刷方式(即使它看起來不正確)。
我通過處理事件自己做了這件事 - 但是我很難做到這一點。 到目前為止,這是我的代碼。
XAML
<TextBox TextWrapping="Wrap" AcceptsReturn="True"
PreviewTextInput="UIElement_OnPreviewTextInput"
TextChanged="TextBoxBase_OnTextChanged" />
代碼背后
public int TextBoxMaxAllowedLines { get; set; }
public int TextBoxMaxAllowedCharactersPerLine { get; set; }
public MainWindow()
{
InitializeComponent();
TextBoxMaxAllowedLines = 5;
TextBoxMaxAllowedCharactersPerLine = 50;
}
private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = (TextBox)sender;
int textLineCount = textBox.LineCount;
if (textLineCount > TextBoxMaxAllowedLines)
{
StringBuilder text = new StringBuilder();
for (int i = 0; i < TextBoxMaxAllowedLines; i++)
text.Append(textBox.GetLineText(i));
textBox.Text = text.ToString();
}
}
private void UIElement_OnPreviewTextInput(object sender, TextCompositionEventArgs e)
{
TextBox textBox = (TextBox)sender;
int textLineCount = textBox.LineCount;
for (int i = 0; i < textLineCount; i++)
{
var line = textBox.GetLineText(i);
if (i == TextBoxMaxAllowedLines-1)
{
int selectStart = textBox.SelectionStart;
textBox.Text = textBox.Text.TrimEnd('\r', '\n');
textBox.SelectionStart = selectStart;
//Last line
if (line.Length > TextBoxMaxAllowedCharactersPerLine)
e.Handled = true;
}
else
{
if (line.Length > TextBoxMaxAllowedCharactersPerLine-1 && !line.EndsWith("\r\n"))
e.Handled = true;
}
}
}
這不太正常 - 我在最后一行得到了奇怪的行為,文本框中的選定位置不斷跳躍。
順便說一句,也許我走錯了軌道...我也想知道是否可以通過使用這樣的東西使用正則表達式來實現: https : //stackoverflow.com/a/1103822/685341
我對任何想法持開放態度,因為我一直在努力解決這個問題。 上面列出的要求是不可變的 - 我無法更改它們。
這是我的最終解決方案 - 我仍然想聽聽是否有人能想出更好的方法來做到這一點......
這只是處理最大行數 - 我還沒有用最大字符做任何事情 - 但它在邏輯上是我已經完成的一個簡單的擴展。
當我正在處理文本框的textChanged事件時 - 這也包括對控件的調整 - 我沒有找到一種簡潔的方法來截斷此事件中的文本(除非我單獨處理key_preview) - 所以我只是沒有通過撤消允許無效輸入。
XAML
<TextBox TextWrapping="Wrap" AcceptsReturn="True"
HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Disabled">
<i:Interaction.Behaviors>
<lineLimitingTextBoxWpfTest:LineLimitingBehavior TextBoxMaxAllowedLines="5" />
</i:Interaction.Behaviors>
</TextBox>
代碼(行為)
/// <summary> limits the number of lines the textbox will accept </summary>
public class LineLimitingBehavior : Behavior<TextBox>
{
/// <summary> The maximum number of lines the textbox will allow </summary>
public int? TextBoxMaxAllowedLines { get; set; }
/// <summary>
/// Called after the behavior is attached to an AssociatedObject.
/// </summary>
/// <remarks>
/// Override this to hook up functionality to the AssociatedObject.
/// </remarks>
protected override void OnAttached()
{
if (TextBoxMaxAllowedLines != null && TextBoxMaxAllowedLines > 0)
AssociatedObject.TextChanged += OnTextBoxTextChanged;
}
/// <summary>
/// Called when the behavior is being detached from its AssociatedObject, but before it has actually occurred.
/// </summary>
/// <remarks>
/// Override this to unhook functionality from the AssociatedObject.
/// </remarks>
protected override void OnDetaching()
{
AssociatedObject.TextChanged -= OnTextBoxTextChanged;
}
private void OnTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = (TextBox)sender;
int textLineCount = textBox.LineCount;
//Use Dispatcher to undo - http://stackoverflow.com/a/25453051/685341
if (textLineCount > TextBoxMaxAllowedLines.Value)
Dispatcher.BeginInvoke(DispatcherPriority.Input, (Action) (() => textBox.Undo()));
}
}
這需要將System.Windows.InterActivity添加到項目中並在XAML中引用:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
我一直在尋找與此類似的問題的答案,我發現的每個答案都涉及附加事件處理程序和編寫大量代碼。 這對我來說似乎並不合適,並且似乎根據我的口味將GUI與Codebehind緊密聯系起來。 此外,它似乎沒有利用WPF的力量。
限制行數實際上是更通用問題的一部分:如何在編輯文本框時限制任何內容?
答案非常簡單:將文本框綁定到自定義DependencyProperty ,然后使用CoerceCallback限制/更改/更改文本框的內容。
確保正確設置數據上下文 - 最簡單(但不是最好)的方法是將行:DataContext =“{Binding RelativeSource = {RelativeSource self}}”添加到Window或UserControl XAML代碼的頂部。
XAML
<TextBox TextWrapping="Wrap"
Text="{Binding NotesText, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
AcceptsReturn="True"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Disabled">
Codebehind(C#)
const int MaxLineCount = 10;
const int MaxLineLength = 200;
public static readonly DependencyProperty NotesTextProperty =
DependencyProperty.Register(
name: "NotesText",
propertyType: typeof( String ),
ownerType: typeof( SampleTextBoxEntryWindow ),
typeMetadata: new PropertyMetadata(
defaultValue: string.Empty,
propertyChangedCallback: OnNotesTextPropertyChanged,
coerceValueCallback: CoerceTextLineLimiter ) );
public string NotesText
{
get { return (String)GetValue( NotesTextProperty ); }
set { SetValue( NotesTextProperty, value ); }
}
private static void OnNotesTextPropertyChanged(DependencyObject source,
DependencyPropertyChangedEventArgs e)
{
// Whatever you want to do when the text changes, like
// set flags to allow buttons to light up, etc.
}
private static object CoerceTextLineLimiter(DependencyObject d, object value)
{
string result = null;
if (value != null)
{
string text = ((string)value);
string[] lines = text.Split( '\n' );
if (lines.Length <= MaxLineCount)
result = text;
else
{
StringBuilder obj = new StringBuilder();
for (int index = 0; index < MaxLineCount; index++)
if (lines[index].Length > 0)
obj.AppendLine( lines[index] > MaxLineLength ? lines[index].Substring(0, MaxLineLength) : lines[index] );
result = obj.ToString();
}
}
return result;
}
(行限制代碼很粗糙 - 但你明白了)。
很酷的是,這提供了一個簡單的框架來做其他事情,比如限制數字或alpha或特殊的東西 - 例如,這里是一個簡單的(非Regx)電話號碼強制方法:
private static object CoercePhoneNumber(DependencyObject d, object value)
{
StringBuilder result = new StringBuilder();
if (value != null)
{
string text = ((string)value).ToUpper();
foreach (char chr in text)
if ((chr >= '0' && chr <= '9') || (chr == ' ') || (chr == '-') || (chr == '(') || (chr == ')'))
result.Append( chr );
}
return result.ToString();
}
對我來說,這似乎是一個更加清潔和可維護的解決方案,可以很容易地重構 - 同時保持數據和表示盡可能分開。 Coerce方法不需要知道數據來自或將要發生的任何事情 - 它只是數據。
這是我為TextBox設置MaxLines的簡單靈魂,它工作正常,我希望它符合您的要求。
My_Defined_MaxTextLength是設置MaxLenght的屬性
My_MaxLines是一個設置最大行數的屬性
My_TextBox.TextChanged += (sender, e) =>
{
if(My_TextBox.LineCount > My_MaxLines)
{
My_TextBox.MaxLength = My_TextBox.Text.Length;
}
else
{
My_TextBox.MaxLength = My_Defined_MaxTextLength;
}
};
最好的祝福
艾哈邁德努爾
感謝Jay的回答,我能夠找到最適合我的解決方案。 它將撤消粘貼和阻止鍵入。
public class LineLimitingBehavior : Behavior<TextBox>
{
public int? TextBoxMaxAllowedLines { get; set; }
protected override void OnAttached()
{
if (TextBoxMaxAllowedLines == null || !(TextBoxMaxAllowedLines > 0)) return;
AssociatedObject.PreviewTextInput += OnTextBoxPreviewTextInput;
AssociatedObject.TextChanged += OnTextBoxTextChanged;
}
private void OnTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
var textBox = (TextBox)sender;
if (textBox.LineCount > TextBoxMaxAllowedLines.Value)
Dispatcher.BeginInvoke(DispatcherPriority.Input, (Action)(() => textBox.Undo()));
}
private void OnTextBoxPreviewTextInput(object sender, TextCompositionEventArgs e)
{
var textBox = (TextBox)sender;
var currentText = textBox.Text;
textBox.Text += e.Text;
if (textBox.LineCount > TextBoxMaxAllowedLines.Value)
e.Handled = true;
textBox.Text = currentText;
textBox.CaretIndex = textBox.Text.Length;
}
protected override void OnDetaching()
{
AssociatedObject.PreviewTextInput -= OnTextBoxPreviewTextInput;
AssociatedObject.TextChanged -= OnTextBoxTextChanged;
}
}
這應該限制行並顯示最后添加的行,而不是顯示第一行:
XAML
<TextBox TextWrapping="Wrap" AcceptsReturn="True"
PreviewTextInput="UIElement_OnPreviewTextInput"
TextChanged="TextBoxBase_OnTextChanged" />
碼
const int MaxLineCount = 10;
private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = (TextBox)sender;
int textLineCount = textBox.LineCount;
if (textLineCount > MaxLineCount)
{
StringBuilder text = new StringBuilder();
for (int i = 0; i < MaxLineCount; i++)
{
text.Append(textBox.GetLineText((textLineCount - MaxLineCount) + i - 1));
}
textBox.Text = text.ToString();
}
}
string prev_text = string.Empty;
private void textBox1_TextChanged(object sender, TextChangedEventArgs e)
{
int MaxLineCount = 5;
if (textBox1.LineCount > MaxLineCount)
{
int index = textBox1.CaretIndex;
textBox1.Text = prev_text;
textBox1.CaretIndex = index;
}
else
{
prev_text = textBox1.Text;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.