原文:
WPF自定义控件(三)の扩展控件
扩展控件,顾名思义就是对已有的控件进行扩展,一般继承于已有的原生控件,不排除继承于自定义的控件,不过这样做意义不大,因为既然都自定义了,为什么不一步到位呢,有些不同的需求也可以通过此来完成,不过类似于类继承了。扩展控件本质也是类的继承。下面我们通过两个例子说明
一、自定义MButton
- 控件外观控制的属性,如圆角、鼠标悬浮前景色背景色、是否开启动画(鼠标悬停时小图标转一圈,移开又转回去)、鼠标按下颜色等;
- 字体图标相关属性,如字符值、字体图标大小、字体图标间距等。
- 首先重写要修改的属性和添加要扩展的功能(在.cs中)
-
public partial class MButton : Button { public static readonly DependencyProperty PressedBackgroundProperty = DependencyProperty.Register("PressedBackground", typeof(Brush), typeof(MButton), new PropertyMetadata(Brushes.DarkBlue)); /// <summary> /// 鼠标按下背景样式 /// </summary> public Brush PressedBackground { get { return (Brush)GetValue(PressedBackgroundProperty); } set { SetValue(PressedBackgroundProperty, value); } } public static readonly DependencyProperty PressedForegroundProperty = DependencyProperty.Register("PressedForeground", typeof(Brush), typeof(MButton), new PropertyMetadata(Brushes.White)); /// <summary> /// 鼠标按下前景样式(图标、文字) /// </summary> public Brush PressedForeground { get { return (Brush)GetValue(PressedForegroundProperty); } set { SetValue(PressedForegroundProperty, value); } } public static readonly DependencyProperty MouseOverBackgroundProperty = DependencyProperty.Register("MouseOverBackground", typeof(Brush), typeof(MButton), new PropertyMetadata(Brushes.RoyalBlue)); /// <summary> /// 鼠标进入背景样式 /// </summary> public Brush MouseOverBackground { get { return (Brush)GetValue(MouseOverBackgroundProperty); } set { SetValue(MouseOverBackgroundProperty, value); } } public static readonly DependencyProperty MouseOverForegroundProperty = DependencyProperty.Register("MouseOverForeground", typeof(Brush), typeof(MButton), new PropertyMetadata(Brushes.White)); /// <summary> /// 鼠标进入前景样式 /// </summary> public Brush MouseOverForeground { get { return (Brush)GetValue(MouseOverForegroundProperty); } set { SetValue(MouseOverForegroundProperty, value); } } public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(MButton), new PropertyMetadata(new CornerRadius(2))); /// <summary> /// 按钮圆角大小,左上,右上,右下,左下 /// </summary> public CornerRadius CornerRadius { get { return (CornerRadius)GetValue(CornerRadiusProperty); } set { SetValue(CornerRadiusProperty, value); } } public static readonly DependencyProperty ContentDecorationsProperty = DependencyProperty.Register( "ContentDecorations", typeof(TextDecorationCollection), typeof(MButton), new PropertyMetadata(null)); public TextDecorationCollection ContentDecorations { get { return (TextDecorationCollection)GetValue(ContentDecorationsProperty); } set { SetValue(ContentDecorationsProperty, value); } } static FButton() { DefaultStyleKeyProperty.OverrideMetadata(typeof(FButton), new FrameworkPropertyMetadata(typeof(FButton))); } }
下面是模板:
<!--MButton模板--> <ControlTemplate x:Key="FButton_Template" TargetType="{x:Type local:MButton}"> <Border x:Name="border" Background="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path= Background}" Height="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Height}" CornerRadius="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=CornerRadius}" Width="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Width}"> Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path= FIcon}" FontSize="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path= FIconSize}" Foreground="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path= Foreground}"> <TextBlock.RenderTransform> <RotateTransform x:Name="transIcon" Angle="0"/> </TextBlock.RenderTransform> </TextBlock> <TextBlock VerticalAlignment="Center" x:Name="txt" TextDecorations="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=ContentDecorations}" Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Content}" FontSize="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=FontSize}" Foreground="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Foreground}"></TextBlock> </StackPanel> </Border> <!--触发器--> <ControlTemplate.Triggers> <!--设置鼠标进入时的背景、前景样式--> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=MouseOverBackground}" TargetName="border" /> <Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=MouseOverForeground}" TargetName="icon"/> <Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=MouseOverForeground}" TargetName="txt"/> </Trigger> <!--鼠标按下时的前景、背景样式--> <Trigger Property="IsPressed" Value="True"> <Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=PressedBackground}" TargetName="border" /> <Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=PressedForeground}" TargetName="icon"/> <Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=PressedForeground}" TargetName="txt"/> </Trigger> <Trigger Property="IsEnabled" Value="false"> <Setter Property="Opacity" Value="0.5" TargetName="border"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate>
下面是样式:
<!--默认样式--> <Style TargetType="{x:Type local:MButton}"> <Setter Property="Background" Value="{StaticResource ButtonBackground}" /> <Setter Property="Foreground" Value="{StaticResource ButtonForeground}" /> <Setter Property="MouseOverBackground" Value="{StaticResource ButtonMouseOverBackground}" /> <Setter Property="MouseOverForeground" Value="{StaticResource ButtonMouseOverForeground}" /> <Setter Property="PressedBackground" Value="{StaticResource ButtonPressedBackground}" /> <Setter Property="PressedForeground" Value="{StaticResource ButtonPressedForeground}" /> <Setter Property="HorizontalContentAlignment" Value="Center" /> <Setter Property="Width" Value="100" /> <Setter Property="Height" Value="30" /> <Setter Property="FontSize" Value="13" /> <Setter Property="CornerRadius" Value="0" /> <Setter Property="Template" Value="{StaticResource FButton_Template}"/> <Setter Property="Padding" Value="3,1,3,1" /> <Setter Property="Content" Value="{x:Null}" /> <Setter Property="AllowsAnimation" Value="False" /> </Style>
这样我们就万行了扩展Button
二、自定义TextBox
这个TextBox可设置水印,可设置必填和正则表达式验证。
就是在输入完成后,控件一旦失去焦点就会自动验证!会根据我开放出来的“是否可以为空”属性进行验证,一旦为空,则控件变为警告样式。
但这还不是最特别的,为了各种手机号啊,邮箱啊的验证,我还开放了一个正则表达式的属性,在这个属性中填上正则表达式,同上, 一旦失去焦点就会自动验证输入的内容能否匹配正则表达式,如果不能匹配,则控件变为警告样式。
之后,代码还可以通过我开放的另一个属性来判断当前输入框的输入是否有误。
1 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 2 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 3 xmlns:ctrl="clr-namespace:KAN.WPF.XCtrl.Controls"> 4 <Style TargetType="{x:Type ctrl:XTextBox}"> 5 <!--StyleFocusVisual--> 6 <Style.Resources> 7 <ResourceDictionary Source="/KAN.WPF.Xctrl;component/Themes/CommonStyle.xaml"/> 8 </Style.Resources> 9 <Setter Property="FocusVisualStyle" Value="{StaticResource StyleFocusVisual}"/> 10 <Setter Property="BorderBrush" Value="Silver"/> 11 <Setter Property="BorderThickness" Value="1"/> 12 <Setter Property="Template"> 13 <Setter.Value> 14 <ControlTemplate TargetType="{x:Type ctrl:XTextBox}"> 15 <Border Name="brdText" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" 16 BorderBrush="{TemplateBinding BorderBrush}" SnapsToDevicePixels="true" Padding="2"> 17 <Grid> 18 <ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/> 19 <StackPanel Orientation="Horizontal" Visibility="Collapsed" Name="stpWatermark"> 20 <TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" 21 FontSize="{TemplateBinding FontSize}" FontFamily="{TemplateBinding FontFamily}" 22 Foreground="{Binding XWmkForeground, RelativeSource={RelativeSource TemplatedParent}}" 23 Text="{Binding XWmkText, RelativeSource={RelativeSource TemplatedParent}}" Cursor="IBeam" /> 24 </StackPanel> 25 <ContentPresenter></ContentPresenter> 26 </Grid> 27 </Border> 28 <ControlTemplate.Triggers> 29 <!--当失去焦点并且没有输入任何内容时--> 30 <MultiTrigger> 31 <MultiTrigger.Conditions> 32 <Condition Property="Text" Value=""/> 33 <Condition Property="IsFocused" Value="False"/> 34 </MultiTrigger.Conditions> 35 <MultiTrigger.Setters> 36 <Setter Property="Visibility" TargetName="stpWatermark" Value="Visible"/> 37 </MultiTrigger.Setters> 38 </MultiTrigger> 39 <!--当验证失败时--> 40 <Trigger Property="XIsError" Value="true"> 41 <Setter TargetName="brdText" Property="BorderBrush" Value="Red" /> 42 <Setter TargetName="brdText" Property="Background" Value="Beige" /> 43 </Trigger> 44 </ControlTemplate.Triggers> 45 </ControlTemplate> 46 </Setter.Value> 47 </Setter> 48 </Style> 49 </ResourceDictionary>
再来看看CS:
1 using System; 2 using System.Windows; 3 using System.Windows.Controls; 4 using System.Windows.Media; 5 using System.Windows.Input; 6 using System.Text.RegularExpressions; 7 8 namespace KAN.WPF.XCtrl.Controls 9 { 10 /// <summary> 11 /// 扩展输入框:可设置水印,可设置必填,可设置正则表达式验证 12 /// </summary> 13 public class XTextBox:TextBox 14 { 15 #region 依赖属性 16 public static readonly DependencyProperty XWmkTextProperty;//水印文字 17 public static readonly DependencyProperty XWmkForegroundProperty;//水印着色 18 public static readonly DependencyProperty XIsErrorProperty;//是否字段有误 19 public static readonly DependencyProperty XAllowNullProperty;//是否允许为空 20 public static readonly DependencyProperty XRegExpProperty;//正则表达式 21 #endregion 22 23 #region 内部方法 24 /// <summary> 25 /// 注册事件 26 /// </summary> 27 public XTextBox() 28 { 29 this.LostFocus += new RoutedEventHandler(XTextBox_LostFocus); 30 this.GotFocus += new RoutedEventHandler(XTextBox_GotFocus); 31 this.PreviewMouseDown += new MouseButtonEventHandler(XTextBox_PreviewMouseDown); 32 } 33 34 /// <summary> 35 /// 静态构造函数 36 /// </summary> 37 static XTextBox() 38 { 39 //注册依赖属性 40 XTextBox.XWmkTextProperty = DependencyProperty.Register("XWmkText", typeof(String), typeof(XTextBox), new PropertyMetadata(null)); 41 XTextBox.XAllowNullProperty = DependencyProperty.Register("XAllowNull", typeof(bool), typeof(XTextBox), new PropertyMetadata(true)); 42 XTextBox.XIsErrorProperty = DependencyProperty.Register("XIsError", typeof(bool), typeof(XTextBox), new PropertyMetadata(false)); 43 XTextBox.XRegExpProperty = DependencyProperty.Register("XRegExp", typeof(string), typeof(XTextBox), new PropertyMetadata("")); 44 XTextBox.XWmkForegroundProperty = DependencyProperty.Register("XWmkForeground", typeof(Brush), 45 typeof(XTextBox), new PropertyMetadata(Brushes.Silver)); 46 FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(typeof(XTextBox), new FrameworkPropertyMetadata(typeof(XTextBox))); 47 } 48 49 /// <summary> 50 /// 失去焦点时检查输入 51 /// </summary> 52 /// <param name="sender"></param> 53 /// <param name="e"></param> 54 void XTextBox_LostFocus(object sender, RoutedEventArgs e) 55 { 56 this.XIsError = false; 57 if (XAllowNull == false && this.Text.Trim() == "") 58 { 59 this.XIsError = true; 60 } 61 if (Regex.IsMatch(this.Text.Trim(), XRegExp) == false) 62 { 63 this.XIsError = true; 64 } 65 } 66 67 /// <summary> 68 /// 获得焦点时选中文字 69 /// </summary> 70 /// <param name="sender"></param> 71 /// <param name="e"></param> 72 void XTextBox_GotFocus(object sender, RoutedEventArgs e) 73 { 74 this.SelectAll(); 75 } 76 77 /// <summary> 78 /// 鼠标点击时选中文字 79 /// </summary> 80 /// <param name="sender"></param> 81 /// <param name="e"></param> 82 void XTextBox_PreviewMouseDown(object sender, MouseButtonEventArgs e) 83 { 84 if (this.IsFocused == false) 85 { 86 TextBox textBox = e.Source as TextBox; 87 textBox.Focus(); 88 e.Handled = true; 89 } 90 } 91 #endregion 92 93 #region 公布属性 94 /// <summary> 95 /// 公布属性XWmkText(水印文字) 96 /// </summary> 97 public String XWmkText 98 { 99 get 100 { 101 return base.GetValue(XTextBox.XWmkTextProperty) as String; 102 } 103 set 104 { 105 base.SetValue(XTextBox.XWmkTextProperty, value); 106 } 107 } 108 109 /// <summary> 110 /// 公布属性XWmkForeground(水印着色) 111 /// </summary> 112 public Brush XWmkForeground 113 { 114 get 115 { 116 return base.GetValue(XTextBox.XWmkForegroundProperty) as Brush; 117 } 118 set 119 { 120 base.SetValue(XTextBox.XWmkForegroundProperty, value); 121 } 122 } 123 124 /// <summary> 125 /// 公布属性XIsError(是否字段有误) 126 /// </summary> 127 public bool XIsError 128 { 129 get 130 { 131 return (bool)base.GetValue(XTextBox.XIsErrorProperty); 132 } 133 set 134 { 135 base.SetValue(XTextBox.XIsErrorProperty, value); 136 } 137 } 138 139 /// <summary> 140 /// 公布属性XAllowNull(是否允许为空) 141 /// </summary> 142 public bool XAllowNull 143 { 144 get 145 { 146 return (bool)base.GetValue(XTextBox.XAllowNullProperty); 147 } 148 set 149 { 150 base.SetValue(XTextBox.XAllowNullProperty, value); 151 } 152 } 153 154 /// <summary> 155 /// 公布属性XRegExp(正则表达式) 156 /// </summary> 157 public string XRegExp 158 { 159 get 160 { 161 return base.GetValue(XTextBox.XRegExpProperty) as string; 162 } 163 set 164 { 165 base.SetValue(XTextBox.XRegExpProperty, value); 166 } 167 } 168 #endregion 169 } 170 }
好了,扩展控件就介绍完了
自定义控件系列博文链接:
WPF自定义控件(一)の控件分类
WPF自定义控件(二)の重写原生控件样式模板
WPF自定义控件(三)の扩展控件
WPF自定义控件(四)の自定义控件
WPF自定义控件(五)の用户控件