需求
1.设定四种输入规则
a,无验证
输入框可以输入任意字符
b,大写
输入框可以输入任意字符且小写字母自动转换为大写
c,小写
输入框可以输入任意字符且大写字母自动转换为大写
d,数字
输入框只能输入数字
2.警告边框
当文本输入框文本不符合输入规则时显示警告边框
3.占位符
当文本输入框文本为空且无焦点时,显示占位符
4.其他
a.字体大小随输入框高度变化
b.增加一个按下enter键的事件
实现
1.输入规则
继承控件,利用重载事件中的逻辑处理实现
2.提示边框、占位符
利用Adorner对自定义的文本框进行装饰来实现
Tips
1.Adorner
需要理解Adorner的原理以及使用时的一些注意事项
2.路由事件
注意WPF中事件的传递顺序
3.输入法
必要时验证输入法或者禁用输入法(数字规则)
4.TextBox
需要详细理解系统文本框原理、事件流程
--------------------------------------------------------------------------------以下为代码,没有仔细整理,见谅------------------------------------------------------------------------------------------------
样式模版:
<Style TargetType="{x:Type local:XTextInput}">
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
<Setter Property="BorderBrush" Value="#FFABADB3"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="AllowDrop" Value="True"/>
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="MaxLength" Value="100"/>
<Setter Property="TextWrapping" Value="NoWrap"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<AdornerDecorator>
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<ScrollViewer x:Name="PART_ContentHost" Focusable="False" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
</Border>
</AdornerDecorator>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" TargetName="border" Value="0.56"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" TargetName="border" Value="#FF7EB4EA"/>
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="BorderBrush" TargetName="border" Value="#FF569DE5"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Adorner
#region 占位符装饰器
public class PlaceHolderAdorner : Adorner
{
private TextBlock tipTextblock;
private TextBox host;
private VisualCollection visCollec;
public PlaceHolderAdorner(TextBox element, string tipString,Brush tipColor,FontStyle tipFontStyle,FontWeight tipFontWeight)
: base(element)
{
if (element == null) return;
host = element;
visCollec = new VisualCollection(this);
tipTextblock = new TextBlock();
tipTextblock.Style = null;
tipTextblock.FontSize = element.FontSize;
tipTextblock.Height = element.Height - element.BorderThickness.Bottom - element.BorderThickness.Top;
tipTextblock.Width = element.Width - element.BorderThickness.Left - element.BorderThickness.Right;
tipTextblock.FontFamily = element.FontFamily;
tipTextblock.FontStretch = element.FontStretch;
tipTextblock.FontWeight =tipFontWeight;
tipTextblock.FontStyle = tipFontStyle;
tipTextblock.Foreground = tipColor;
tipTextblock.Text = tipString;
tipTextblock.IsHitTestVisible = false;
visCollec.Add(tipTextblock);
}
protected override int VisualChildrenCount
{
get
{
return visCollec.Count;
}
}
protected override Size ArrangeOverride(Size finalSize)
{
tipTextblock.Arrange(new Rect(new Point(host.BorderThickness.Left, host.BorderThickness.Top), finalSize));
return finalSize;
}
protected override Visual GetVisualChild(int index)
{
return visCollec[index];
}
}
#endregion
#region 特殊边框装饰器
public class BorderAdorner : Adorner
{
private VisualCollection visCollec;
private Border warnBorder;
public BorderAdorner(TextBox ele, Brush boederColor)
: base(ele)
{
if (ele == null) return;
visCollec = new VisualCollection(this);
warnBorder = new Border();
warnBorder.BorderBrush = boederColor;
warnBorder.Width = ele.Width;
warnBorder.Height = ele.Height;
warnBorder.BorderThickness = ele.BorderThickness == new Thickness(0) ? new Thickness(1, 1, 1, 1) : ele.BorderThickness;
warnBorder.IsHitTestVisible = false;
visCollec.Add(warnBorder);
}
protected override int VisualChildrenCount
{
get
{
return visCollec.Count;
}
}
protected override Size ArrangeOverride(Size finalSize)
{
warnBorder.Arrange(new Rect(new Point(0, 0), finalSize));
return finalSize;
}
protected override Visual GetVisualChild(int index)
{
return visCollec[index];
}
}
#endregion
文本输入框实现
public class XTextInput : TextBox
{
static XTextInput()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(XTextInput), new FrameworkPropertyMetadata(typeof(XTextInput)));
}
private AdornerLayer layer = null;
#region add properties
[Category("CX")]
[Description("获取文本输入是否符合输入当前规则")]
public bool IsVailedText
{
get { return IsVailedString(Text); }
}
#region Add Property:PlaceHolderText
public static readonly DependencyProperty PlaceHolderTextProperty =
DependencyProperty.Register("PlaceHolderText", typeof(string), typeof(XTextInput), new PropertyMetadata("请输入"));
[Category("CX")]
[Description("提示信息,超过20自动截取")]
public string PlaceHolderText
{
get { return GetValue(PlaceHolderTextProperty).ToString(); }
set
{
SetValue(PlaceHolderTextProperty, value.Length > 20 ? value.Substring(0, 20) : value);
SetPlaceHolder();
}
}
#endregion
#region Add Property:PlaceHolderBrush
public static readonly DependencyProperty PlaceHolderBrushProperty =
DependencyProperty.Register("PlaceHolderBrush", typeof(Brush), typeof(XTextInput), new PropertyMetadata(Brushes.DarkGray));
[Category("CX")]
[Description("提示信息文字颜色")]
public Brush PlaceHolderBrush
{
get { return (Brush)GetValue(PlaceHolderBrushProperty); }
set
{
SetValue(PlaceHolderBrushProperty, value);
SetPlaceHolder();
}
}
#endregion
#region Add Property:PlaceHolderFontStyle
public static readonly DependencyProperty PlaceHolderFontStyleProperty =
DependencyProperty.Register("PlaceHolderFontStyle", typeof(FontStyle), typeof(XTextInput), new PropertyMetadata(FontStyles.Italic));
[Category("CX")]
[Description("提示信息文字字体样式")]
public FontStyle PlaceHolderFontStyle
{
get { return (FontStyle)GetValue(PlaceHolderFontStyleProperty); }
set
{
SetValue(PlaceHolderFontStyleProperty, value);
SetPlaceHolder();
}
}
#endregion
#region Add Property:PlaceHolderFontWeight
public static readonly DependencyProperty PlaceHolderFontWeightProperty =
DependencyProperty.Register("PlaceHolderFontWeight", typeof(FontWeight), typeof(XTextInput), new PropertyMetadata(FontWeights.Normal));
[Category("CX")]
[Description("提示信息文字字体粗细")]
public FontWeight PlaceHolderFontWeight
{
get { return (FontWeight)GetValue(PlaceHolderFontWeightProperty); }
set
{
SetValue(PlaceHolderFontWeightProperty, value);
SetPlaceHolder();
}
}
#endregion
#region Add Property:WarnBrush
public static readonly DependencyProperty WarnBrushProperty =
DependencyProperty.Register("WarnBrush", typeof(Brush), typeof(XTextInput),
new PropertyMetadata(Brushes.Red));
[Category("CX")]
[Description("文本不符合规则时警告边框样式")]
public Brush WarnBrush
{
get { return (Brush)GetValue(WarnBrushProperty); }
set
{
SetValue(WarnBrushProperty, value);
SetWarnBorder();
}
}
#endregion
#region Add Property:TextRule
public static readonly DependencyProperty TextRuleProperty =
DependencyProperty.Register("TextRule", typeof(XTextRule), typeof(XTextInput),
new FrameworkPropertyMetadata(XTextRule.AllCase));
[Category("CX")]
[Description("文本规则,包括:任意字符、大写、小写、数字,默认任意字符")]
public XTextRule TextRule
{
get { return (XTextRule)GetValue(TextRuleProperty); }
set
{
SetValue(TextRuleProperty, value);
InputMethod.SetIsInputMethodEnabled(this, TextRule == XTextRule.OnlyNumber ? false : true);
SetWarnBorder();
}
}
#endregion
#region Add Event:EnterCompleted
public delegate void EnterCompletedHandle(object sender);
[Description("输入完成事件,键入Enter键触发")]
public event EnterCompletedHandle EnterCompleted;
#endregion
#endregion
#region override
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
layer = AdornerLayer.GetAdornerLayer(this);
CommandBinding pasteCommand = new CommandBinding(ApplicationCommands.Paste);
pasteCommand.PreviewCanExecute += (sender, e) =>
{
e.CanExecute = true;
};
pasteCommand.CanExecute += (sender, e) =>
{
e.CanExecute = true;
};
pasteCommand.PreviewExecuted += pasteCommand_PreviewExecuted;
CommandBindings.Add(pasteCommand);//加入粘贴命令
InputMethod.SetIsInputMethodEnabled(this, TextRule == XTextRule.OnlyNumber ? false : true);
SetWarnBorder();
}
void pasteCommand_PreviewExecuted(object sender, ExecutedRoutedEventArgs e)
{
if (!System.Windows.Clipboard.ContainsText()) return;
string s = System.Windows.Clipboard.GetText();
switch (TextRule)
{
case XTextRule.AllCase:
if (this.Text.Length - this.SelectedText.Length + s.Length <= MaxLength)
{
string newText = Text.Remove(this.SelectionStart, this.SelectedText.Length);
newText = newText.Insert(this.SelectionStart, s);
int endIndex = this.SelectionStart + s.Length;
this.Text = newText;
CaretIndex = endIndex;
}
else
{
int overFolwLength = this.Text.Length - this.SelectedText.Length + s.Length - MaxLength;
var str = s.Substring(0, s.Length - overFolwLength);
string newText = Text.Remove(this.SelectionStart, this.SelectedText.Length).Insert(this.SelectionStart, str);
int endIndex = this.SelectionStart + str.Length;
this.Text = newText;
CaretIndex = endIndex;
}
break;
case XTextRule.UpperCase:
if (this.Text.Length - this.SelectedText.Length + s.Length <= MaxLength)
{
string newText = Text.Remove(this.SelectionStart, this.SelectedText.Length);
newText = newText.Insert(this.SelectionStart, s.ToUpper());
int endIndex = this.SelectionStart + s.Length;
this.Text = newText;
CaretIndex = endIndex;
}
else
{
int overFolwLength = this.Text.Length - this.SelectedText.Length + s.Length - MaxLength;
var str = s.Substring(0, s.Length - overFolwLength);
string newText = Text.Remove(this.SelectionStart, this.SelectedText.Length).Insert(this.SelectionStart, str.ToUpper());
int endIndex = this.SelectionStart + str.Length;
this.Text = newText;
CaretIndex = endIndex;
}
break;
case XTextRule.OnlyNumber:
if (s.Any(p => !Char.IsDigit(p)))
e.Handled = true;
else
{
if (this.Text.Length - this.SelectedText.Length + s.Length <= MaxLength)
{
string newText = Text.Remove(this.SelectionStart, this.SelectedText.Length);
newText = newText.Insert(this.SelectionStart, s.ToLower());
int endIndex = this.SelectionStart + s.Length;
this.Text = newText;
CaretIndex = endIndex;
}
else
{
int overFolwLength = this.Text.Length - this.SelectedText.Length + s.Length - MaxLength;
var str = s.Substring(0, s.Length - overFolwLength);
string newText = Text.Remove(this.SelectionStart, this.SelectedText.Length).Insert(this.SelectionStart, str.ToLower());
int endIndex = this.SelectionStart + str.Length;
this.Text = newText;
CaretIndex = endIndex;
}
}
break;
case XTextRule.LowerCase:
if (this.Text.Length - this.SelectedText.Length + s.Length <= MaxLength)
{
string newText = Text.Remove(this.SelectionStart, this.SelectedText.Length);
newText = newText.Insert(this.SelectionStart, s.ToLower());
int endIndex = this.SelectionStart + s.Length;
this.Text = newText;
CaretIndex = endIndex;
}
else
{
int overFolwLength = this.Text.Length - this.SelectedText.Length + s.Length - MaxLength;
var str = s.Substring(0, s.Length - overFolwLength);
string newText = Text.Remove(this.SelectionStart, this.SelectedText.Length).Insert(this.SelectionStart, str.ToLower());
int endIndex = this.SelectionStart + str.Length;
this.Text = newText;
CaretIndex = endIndex;
}
break;
}
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
FontSize = sizeInfo.NewSize.Height * 0.6;
SetPlaceHolder();
}
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
if (e.Key == Key.Enter && e.ImeProcessedKey != Key.ImeProcessed)
if (EnterCompleted != null)
EnterCompleted(this);
if (TextRule == XTextRule.OnlyNumber)
{
if(e.Key==Key.Space)
e.Handled = true;
}
}
protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
SelectAll();
SetPlaceHolder();
}
protected override void OnLostFocus(RoutedEventArgs e)
{
base.OnLostFocus(e);
SetPlaceHolder();
SetWarnBorder();
}
protected override void OnPreviewTextInput(TextCompositionEventArgs e)
{
base.OnPreviewTextInput(e);
string str = e.Text;
int overFlow = Text.Length - this.SelectedText.Length + e.Text.Length - MaxLength;
if (overFlow > 0)
{
if (overFlow == str.Length) return;
else
str = e.Text.Substring(0, overFlow);
}
switch (TextRule)
{
case XTextRule.AllCase:
e.Handled = false;
break;
case XTextRule.UpperCase:
if (!IsVailedString(str))
{
string newString = Text.Remove(this.SelectionStart, this.SelectionLength).Insert(
this.SelectionStart, str.ToUpper());
int index = CaretIndex;
Text = newString;
CaretIndex = index + str.Length;
e.Handled = true;
}
else
e.Handled = false;
break;
case XTextRule.LowerCase:
if (!IsVailedString(str))
{
string newString = Text.Remove(this.SelectionStart, this.SelectionLength).Insert(
this.SelectionStart, str.ToLower());
int index = CaretIndex;
Text = newString;
CaretIndex = index + str.Length;
e.Handled = true;
}
else
e.Handled = false;
break;
case XTextRule.OnlyNumber:
if (e.Text.Any(p => !Char.IsDigit(p)))
e.Handled = true;
else
{
e.Handled = false;
}
break;
}
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
if (layer != null)
SetWarnBorder();
}
#endregion
#region function
/// <summary>
/// 判断是否符合当前规则
/// </summary>
/// <param name="chars"></param>
/// <returns></returns>
private bool IsVailedString(string chars)
{
switch (TextRule)
{
case XTextRule.LowerCase:
return chars.Any(p => Char.IsUpper(p)) ? false : true;
case XTextRule.OnlyNumber:
return chars.All(p => Char.IsDigit(p)) ? true : false;
case XTextRule.UpperCase:
return chars.Any(p => Char.IsLower(p)) ? false : true;
default:
return true;
}
}
/// <summary>
/// 设置占位符装饰器
/// </summary>
private void SetPlaceHolder()
{
if (Text.Length == 0 && !IsFocused)
{
var tmp = layer.GetAdorners(this);
if (tmp != null)
{
foreach (var m in tmp)
if (m is PlaceHolderAdorner) layer.Remove(m);
}
PlaceHolderAdorner p = new PlaceHolderAdorner(this, PlaceHolderText, PlaceHolderBrush, PlaceHolderFontStyle, PlaceHolderFontWeight);
layer.Add(p);
}
else
{
var tmp = layer.GetAdorners(this);
if (tmp != null)
{
foreach (var m in tmp)
if (m is PlaceHolderAdorner) layer.Remove(m);
}
}
}
/// <summary>
/// 设置边框装饰器
/// </summary>
private void SetWarnBorder()
{
if (IsVailedString(Text))
{
var tmp = layer.GetAdorners(this);
if (tmp != null)
{
foreach (var m in tmp)
if (m is BorderAdorner) layer.Remove(m);
}
}
else
{
var tmp = layer.GetAdorners(this);
if (tmp != null)
{
foreach (var m in tmp)
if (m is BorderAdorner) layer.Remove(m);
}
BorderAdorner p = new BorderAdorner(this, WarnBrush);
layer.Add(p);
}
}
#endregion
}
/// <summary>
/// XTextInput 文本输入规则
/// </summary>
public enum XTextRule
{
/// <summary>
/// 大小写混合
/// </summary>
AllCase = 0,
/// <summary>
/// 全部大写
/// </summary>
UpperCase = 1,
/// <summary>
/// 全部小写
/// </summary>
LowerCase = 2,
/// <summary>
/// 全部数字
/// </summary>
OnlyNumber = 3
}