From comments, a test by comparing text lenght with TextBox client width (I tested with renderScope.ActualWidth (Reflection) which seems accurate) =>
<TextBox x:Name="textBox1" HorizontalAlignment="Left" Height="27" Margin="79,133,0,0" VerticalAlignment="Top" Width="225" PreviewKeyDown="textBox1_PreviewKeyDown"/>
Main code :
public MainWindow()
{
InitializeComponent();
DataObject.AddPastingHandler(textBox1, OnPaste);
}
private KeyConverter kc = new KeyConverter();
private void textBox1_PreviewKeyDown(object sender, KeyEventArgs e)
{
TextBox tb = (TextBox)sender;
string sText = tb.Text;
string sKey = kc.ConvertToString(e.Key);
bool bCapsLock = Keyboard.IsKeyToggled(Key.CapsLock);
bool bShift = (Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift;
bool bCharacter = false;
if ((!bCapsLock && !bShift) || (bCapsLock && bShift))
{
sKey = sKey.ToLower();
}
if (sKey != null && sKey.Length == 1)
{
if (char.IsLetterOrDigit(sKey[0]))
{
sText += sKey[0];
bCharacter = true;
}
}
// System.Windows.Controls.TextBoxView
var renderScope = tb.GetFieldValue<FrameworkElement>("_renderScope") as IServiceProvider;
var nActualWidthView = (double)renderScope.InvokeMethod("get_ActualWidth");
var sz = MeasureText(tb, sText);
if (bCharacter && sz.Width >= nActualWidthView)
{
e.Handled = true;
Console.Beep(1000, 10);
}
}
private void OnPaste(object sender, DataObjectPastingEventArgs e)
{
e.Handled = true;
e.CancelCommand();
}
// Adapted from MSAGL
public Size MeasureText(TextBox ctrl, string text)
{
FormattedText formattedText = new FormattedText(
text,
System.Globalization.CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
new Typeface(ctrl.FontFamily, ctrl.FontStyle, ctrl.FontWeight, ctrl.FontStretch),
ctrl.FontSize,
new SolidColorBrush(Colors.Black),
VisualTreeHelper.GetDpi(this).PixelsPerDip);
return new Size(formattedText.Width, formattedText.Height);
}
Utility class for Reflection :
// https://sudonull.com/post/12432-Bug-when-working-TextBoxGetLineText-in-NET-WPF
public static class ReflectionExtensions
{
public static T GetFieldValue<T>(this object obj, string name)
{
var bindingFlags = System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance;
var field = obj.GetType().GetField(name, bindingFlags);
if (field == null)
field = obj.GetType().BaseType.GetField(name, bindingFlags);
return (T)field?.GetValue(obj);
}
public static object InvokeMethod(this object obj, string methodName, params object[] methodParams)
{
var methodParamTypes = methodParams?.Select(p => p.GetType()).ToArray() ?? new Type[] { };
var bindingFlags = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Static;
System.Reflection.MethodInfo method = null;
var type = obj.GetType();
while (method == null && type != null)
{
method = type.GetMethod(methodName, bindingFlags, Type.DefaultBinder, methodParamTypes, null);
var intfs = type.GetInterfaces();
if (method != null)
break;
foreach (var intf in intfs)
{
method = intf.GetMethod(methodName, bindingFlags, Type.DefaultBinder, methodParamTypes, null);
if (method != null)
break;
}
type = type.BaseType;
}
return method?.Invoke(obj, methodParams);
}
}