wpf 自定义mask datepicker
注意如果设置了系统dateformat格式。在变化系统culture的时候需要设置dateformat
CultureInfo info = new CultureInfo(this.LanguageItems[value].Name); info.DateTimeFormat.ShortDatePattern= "dd/MM/yyyy"; info.DateTimeFormat.LongDatePattern = "dd/MM/yyyy"; Thread.CurrentThread.CurrentCulture = info; Thread.CurrentThread.CurrentUICulture = info;
在textbox中设置
InputMethod.IsInputMethodEnabled=“false”;可以禁止textbox禁用IME开关,(做到不允许中文字符的输入)
// 若要将光标置于 TextBox 控件的内容的开头,应调用 Select 方法,并指定选择内容的起始位置为 0,选择长度为 0。
//1 textBox1.Select(0, 0);
//若要将光标置于 TextBox 控件的内容的末尾,应调用 Select 方法,并指定选择内容的起始位置等于文本内容的长度,选择长度为 0。
//1 textBox1.Select(textBox1.Text.Length, 0);
//若要将光标置于 TextBox 控件的内容的当前位置,应调用 Select 方法,并指定选择内容的起始位置等于光标的当前位置,选择长度为 0。
//1 textBox1.Select(textBox1.SelectionStart, 0);
//原因是通过鼠标让TextBox获得输入焦点时,TextBox触发的事件顺序是:MouseDown->GotFocus->MouseUp,
// 也就是说TextBox在鼠标按下的那一刻已经获得了输入焦点,此时可以对Select(0, 0)设置焦点位置。
// 但郁闷的是,MouseUp却会取消TextBox状态...也就是说文本焦点其实曾经被设置了,但立即又被取消(-_-#)
textbox 用鼠标获得焦点的顺序是 mouseDown GotFocus mouseUP. mouseup会把gotFocus设置的select取消
View Code
private void MyTextBox_GotFocus(object sender, RoutedEventArgs e) { this.MyTextBox.Tag = true; if (this.SelectedDate != null) { this.MyTextBox.Text = string.Format("{0:dd/MM/yyyy}", this.SelectedDate.GetValueOrDefault()); } else { UpdateInputMask(); // this.MyTextBox.Text = "__/__/____"; this.MyTextBox.Select(0, 0); } }
所有在mouseup的时候要设置
View Code
private void MyTextBox_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { if (this.SelectedDate==null) { if (e.ChangedButton == MouseButton.Left && (bool)this.MyTextBox.Tag == true) { this.MyTextBox.Select(0, 0); } this.MyTextBox.Tag = false; } }
使用tag来标记。。是否是左击鼠标获得焦点。。获得之后就不在设置焦点位置
样式
:
View Code
<!--Control colors.-->
<Color x:Key="WindowColor">#FFE8EDF9</Color>
<Color x:Key="ContentAreaColorLight">#FFC5CBF9</Color>
<Color x:Key="ContentAreaColorDark">#FF7381F9</Color>
<Color x:Key="DisabledControlLightColor">#FFE8EDF9</Color>
<Color x:Key="DisabledControlDarkColor">#FFC5CBF9</Color>
<Color x:Key="DisabledForegroundColor">#FF888888</Color>
<Color x:Key="SelectedBackgroundColor">#FFC5CBF9</Color>
<Color x:Key="SelectedUnfocusedColor">#FFDDDDDD</Color>
<Color x:Key="ControlLightColor">White</Color>
<Color x:Key="ControlMediumColor">#FF7381F9</Color>
<Color x:Key="ControlDarkColor">#FF211AA9</Color>
<Color x:Key="ControlMouseOverColor">#FF3843C4</Color>
<Color x:Key="ControlPressedColor">#FF211AA9</Color>
<Color x:Key="GlyphColor">#FF444444</Color>
<Color x:Key="GlyphMouseOver">sc#1, 0.004391443, 0.002428215, 0.242281124</Color>
<!--Border colors-->
<Color x:Key="BorderLightColor">#FFCCCCCC</Color>
<Color x:Key="BorderMediumColor">#FF888888</Color>
<Color x:Key="BorderDarkColor">#FF444444</Color>
<Color x:Key="PressedBorderLightColor">#FF888888</Color>
<Color x:Key="PressedBorderDarkColor">#FF444444</Color>
<Color x:Key="DisabledBorderLightColor">#FFAAAAAA</Color>
<Color x:Key="DisabledBorderDarkColor">#FF888888</Color>
<Color x:Key="DefaultBorderBrushDarkColor">Black</Color>
<!--Control-specific resources.-->
<Color x:Key="HeaderTopColor">#FFC5CBF9</Color>
<Color x:Key="DatagridCurrentCellBorderColor">Black</Color>
<Color x:Key="SliderTrackDarkColor">#FFC5CBF9</Color>
<Color x:Key="NavButtonFrameColor">#FF3843C4</Color>
<LinearGradientBrush x:Key="MenuPopupBrush"
EndPoint="0.5,1"
StartPoint="0.5,0">
<GradientStop Color="{DynamicResource ControlLightColor}"
Offset="0" />
<GradientStop Color="{DynamicResource ControlMediumColor}"
Offset="0.5" />
<GradientStop Color="{DynamicResource ControlLightColor}"
Offset="1" />
</LinearGradientBrush>
<LinearGradientBrush x:Key="ProgressBarIndicatorAnimatedFill"
StartPoint="0,0"
EndPoint="1,0">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#000000FF"
Offset="0" />
<GradientStop Color="#600000FF"
Offset="0.4" />
<GradientStop Color="#600000FF"
Offset="0.6" />
<GradientStop Color="#000000FF"
Offset="1" />
</GradientStopCollection>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<Style x:Key="DatePickerCalendarStyle"
TargetType="{x:Type Calendar}"
BasedOn="{StaticResource {x:Type Calendar}}" />
<!--The template for the button that displays the calendar.-->
<Style x:Key="DropDownButtonStyle"
TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0" />
<VisualTransition GeneratedDuration="0:0:0.1"
To="MouseOver" />
<VisualTransition GeneratedDuration="0:0:0.1"
To="Pressed" />
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<!--<Storyboard>
<ColorAnimationUsingKeyFrames BeginTime="0"
Duration="00:00:00.001"
Storyboard.TargetName="BackgroundGradient"
Storyboard.TargetProperty="(Border.Background). (GradientBrush.GradientStops)[1].(GradientStop.Color)">
<SplineColorKeyFrame KeyTime="0"
Value="#F2FFFFFF" />
</ColorAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames BeginTime="0"
Duration="00:00:00.001"
Storyboard.TargetName="BackgroundGradient"
Storyboard.TargetProperty="(Border.Background). (GradientBrush.GradientStops)[2].(GradientStop.Color)">
<SplineColorKeyFrame KeyTime="0"
Value="#CCFFFFFF" />
</ColorAnimationUsingKeyFrames>
<ColorAnimation Duration="0"
To="#FF448DCA"
Storyboard.TargetProperty="(Border.Background). (SolidColorBrush.Color)"
Storyboard.TargetName="Background" />
<ColorAnimationUsingKeyFrames BeginTime="0"
Duration="00:00:00.001"
Storyboard.TargetName="BackgroundGradient"
Storyboard.TargetProperty="(Border.Background). (GradientBrush.GradientStops)[3].(GradientStop.Color)">
<SplineColorKeyFrame KeyTime="0"
Value="#7FFFFFFF" />
</ColorAnimationUsingKeyFrames>
</Storyboard>-->
</VisualState>
<VisualState x:Name="Pressed">
<!--<Storyboard>
<ColorAnimationUsingKeyFrames BeginTime="0"
Duration="00:00:00.001"
Storyboard.TargetName="Background"
Storyboard.TargetProperty="(Border.Background). (SolidColorBrush.Color)">
<SplineColorKeyFrame KeyTime="0"
Value="#FF448DCA" />
</ColorAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="0"
Duration="00:00:00.001"
Storyboard.TargetProperty="(UIElement.Opacity)"
Storyboard.TargetName="Highlight">
<SplineDoubleKeyFrame KeyTime="0"
Value="1" />
</DoubleAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames BeginTime="0"
Duration="00:00:00.001"
Storyboard.TargetName="BackgroundGradient"
Storyboard.TargetProperty="(Border.Background). (GradientBrush.GradientStops)[0].(GradientStop.Color)">
<SplineColorKeyFrame KeyTime="0"
Value="#F4FFFFFF" />
</ColorAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames BeginTime="0"
Duration="00:00:00.001"
Storyboard.TargetName="BackgroundGradient"
Storyboard.TargetProperty="(Border.Background). (GradientBrush.GradientStops)[1].(GradientStop.Color)">
<SplineColorKeyFrame KeyTime="0"
Value="#EAFFFFFF" />
</ColorAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames BeginTime="0"
Duration="00:00:00.001"
Storyboard.TargetName="BackgroundGradient"
Storyboard.TargetProperty="(Border.Background). (GradientBrush.GradientStops)[2].(GradientStop.Color)">
<SplineColorKeyFrame KeyTime="0"
Value="#C6FFFFFF" />
</ColorAnimationUsingKeyFrames>
<ColorAnimationUsingKeyFrames BeginTime="0"
Duration="00:00:00.001"
Storyboard.TargetName="BackgroundGradient"
Storyboard.TargetProperty="(Border.Background). (GradientBrush.GradientStops)[3].(GradientStop.Color)">
<SplineColorKeyFrame KeyTime="0"
Value="#6BFFFFFF" />
</ColorAnimationUsingKeyFrames>
</Storyboard>-->
</VisualState>
<VisualState x:Name="Disabled" />
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates"/>
<VisualStateGroup x:Name="ValidationStates"/>
</VisualStateManager.VisualStateGroups>
<Grid Background="#11FFFFFF"
FlowDirection="LeftToRight"
HorizontalAlignment="Center"
Height="18"
Margin="0"
VerticalAlignment="Center"
Width="19">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20*" />
<ColumnDefinition Width="20*" />
<ColumnDefinition Width="20*" />
<ColumnDefinition Width="20*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="23*" />
<RowDefinition Height="19*" />
<RowDefinition Height="19*" />
<RowDefinition Height="19*" />
</Grid.RowDefinitions>
<Image Grid.ColumnSpan="4" Margin="0" Grid.RowSpan="4" Source="Image\icon_datepicker.png" Stretch="none"/>
<!--<Rectangle Grid.ColumnSpan="4"
Grid.RowSpan="1"
StrokeThickness="1">
<Rectangle.Fill>
<LinearGradientBrush EndPoint="0,1"
StartPoint="0,0">
<GradientStop Color="{DynamicResource HeaderTopColor}" />
<GradientStop Color="{DynamicResource ControlMediumColor}"
Offset="1" />
</LinearGradientBrush>
</Rectangle.Fill>
<Rectangle.Stroke>
<LinearGradientBrush EndPoint="0.48,-1"
StartPoint="0.48,1.25">
<GradientStop Color="#FF494949" />
<GradientStop Color="#FF9F9F9F"
Offset="1" />
</LinearGradientBrush>
</Rectangle.Stroke>
</Rectangle>-->
<!--<Ellipse Grid.ColumnSpan="4"
Fill="#FFFFFFFF"
HorizontalAlignment="Center"
Height="3"
StrokeThickness="0"
VerticalAlignment="Center"
Width="3" d:IsHidden="True" />-->
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="MyDatePicker" TargetType="{x:Type DatePicker}">
<Setter Property="Foreground"
Value="#FF333333" />
<Setter Property="IsTodayHighlighted"
Value="True" />
<Setter Property="SelectedDateFormat"
Value="Short" />
<Setter Property="Padding"
Value="2" />
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="HorizontalContentAlignment"
Value="Stretch" />
<!--Set CalendarStyle to DatePickerCalendarStyle.-->
<Setter Property="CalendarStyle"
Value="{DynamicResource DatePickerCalendarStyle}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DatePicker}">
<Border BorderThickness="1"
Padding="{TemplateBinding Padding}" BorderBrush="#5d7fad" Background="White" CornerRadius="5" SnapsToDevicePixels="True" Height="28" x:Name="DatePickerBorder">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Duration="0"
To="1"
Storyboard.TargetProperty="Opacity"
Storyboard.TargetName="PART_DisabledVisual" />
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="ValidationStates"/>
</VisualStateManager.VisualStateGroups>
<Grid x:Name="PART_Root"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button x:Name="PART_Button"
Grid.Column="1"
Foreground="#0c223a"
Focusable="False"
HorizontalAlignment="Left"
Margin="5,0,5,0"
Grid.Row="0"
Style="{StaticResource DropDownButtonStyle}"
VerticalAlignment="Top" />
<TextBox x:Name="PART_MyTextBox"
Grid.Column="0" BorderThickness="0"
Foreground="#0c223a"
Focusable="{TemplateBinding Focusable}"
HorizontalContentAlignment="Stretch" InputMethod.IsInputMethodEnabled="False"
Grid.Row="0"
VerticalContentAlignment="Stretch" FontFamily="Arial" FontSize="12" VerticalAlignment="Center"
>
</TextBox>
<DatePickerTextBox x:Name="PART_TextBox"
Visibility="Hidden"
Grid.Column="0"
Foreground="#0c223a"
Focusable="{TemplateBinding Focusable}"
HorizontalContentAlignment="Stretch"
Grid.Row="0"
VerticalContentAlignment="Stretch" FontFamily="Arial" FontSize="12" VerticalAlignment="Center" Style="{DynamicResource DatePickerTextBoxStyle1}" >
<DatePickerTextBox.Resources>
<Style x:Key="DatePickerTextBoxStyle1" TargetType="{x:Type DatePickerTextBox}">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"/>
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DatePickerTextBox}">
<Grid>
<Grid.Resources>
<SolidColorBrush x:Key="WatermarkBrush" Color="#FFAAAAAA"/>
</Grid.Resources>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0"/>
<VisualTransition GeneratedDuration="0:0:0.1" To="MouseOver"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Duration="0" To="#FF99C1E2" Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="ContentElement"/>
<ColorAnimation Duration="0" To="#FF99C1E2" Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" Storyboard.TargetName="watermark_decorator"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="WatermarkStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Unwatermarked"/>
<VisualState x:Name="Watermarked">
<Storyboard>
<DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="ContentElement"/>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PART_Watermark"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Unfocused"/>
<VisualState x:Name="Focused">
<Storyboard>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="FocusVisual"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="ValidationStates"/>
</VisualStateManager.VisualStateGroups>
<Border x:Name="Border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="1" Opacity="1" Padding="{TemplateBinding Padding}">
<Grid x:Name="WatermarkContent" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<Border x:Name="ContentElement" BorderBrush="#FFFFFFFF" BorderThickness="0"/>
<Border x:Name="watermark_decorator" BorderBrush="#FFFFFFFF" BorderThickness="0">
<ContentControl x:Name="PART_Watermark" Focusable="False" IsHitTestVisible="False" Opacity="0" Padding="2" Style="{DynamicResource ContentControlStyle1}">
<ContentControl.Resources>
<Style x:Key="ContentControlStyle1" TargetType="{x:Type ContentControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContentControl}">
<ContentPresenter Visibility="Collapsed"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ContentControl.Resources>
</ContentControl>
</Border>
<ScrollViewer x:Name="PART_ContentHost" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="0" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" Template="{DynamicResource ScrollViewerControlTemplate1}">
<ScrollViewer.Resources>
<ControlTemplate x:Key="ScrollViewerControlTemplate1" TargetType="{x:Type ScrollViewer}">
<Grid x:Name="Grid" Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Rectangle x:Name="Corner" Grid.Column="1" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" Grid.Row="1"/>
<ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" CanVerticallyScroll="False" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Grid.Column="0" Margin="{TemplateBinding Padding}" Grid.Row="0"/>
<ScrollBar x:Name="PART_VerticalScrollBar" AutomationProperties.AutomationId="VerticalScrollBar" Cursor="Arrow" Grid.Column="1" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Grid.Row="0" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}"/>
<ScrollBar x:Name="PART_HorizontalScrollBar" AutomationProperties.AutomationId="HorizontalScrollBar" Cursor="Arrow" Grid.Column="0" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Orientation="Horizontal" Grid.Row="1" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}"/>
</Grid>
</ControlTemplate>
</ScrollViewer.Resources>
</ScrollViewer>
<Border x:Name="FocusVisual" BorderBrush="#FF45D6FA" CornerRadius="1" IsHitTestVisible="False" Opacity="0"/>
</Grid>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DatePickerTextBox.Resources>
</DatePickerTextBox>
<Grid x:Name="PART_DisabledVisual"
Grid.ColumnSpan="2"
Grid.Column="0"
IsHitTestVisible="False"
Opacity="0"
Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0"
Fill="#A5FFFFFF"
RadiusY="3"
Grid.Row="0"
RadiusX="3" />
<Rectangle Grid.Column="1"
Fill="#A5FFFFFF"
Height="18"
Margin="5,0,5,0"
RadiusY="3"
Grid.Row="0"
RadiusX="3"
Width="19" />
<Popup x:Name="PART_Popup"
AllowsTransparency="True"
Placement="Bottom"
PlacementTarget="{Binding ElementName=PART_TextBox}"
StaysOpen="False" />
</Grid>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter TargetName="DatePickerBorder" Property="BorderBrush" Value="Red"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
cs.
View Code
using MCE.Gems.Common.Common; using MCE.Gems.Common.Events; using Microsoft.Practices.Prism.Events; using Microsoft.Practices.ServiceLocation; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Input; namespace MCE.Gems.Common.Controls.CustomControls { public class CustomDatePicker : DatePicker { #region Field // private static IEventAggregator eventAggregator = ServiceLocator.Current.GetInstance<IEventAggregator>(); private List<InputMaskChar> _maskChars; private static string currentWaterMark; private bool isTextChanged; private int _caretIndex; #endregion #region Property /// <summary> /// Gets the text box in charge of the editable portion of the MyDatePicker. /// </summary> protected TextBox MyTextBox { get { return base.GetTemplateChild("PART_MyTextBox") as TextBox; } } public string WaterMark { get { return (string)GetValue(WaterMarkProperty); } set { SetValue(WaterMarkProperty, value); } } // Using a DependencyProperty as the backing store for WaterMark. This enables animation, styling, binding, etc... public static readonly DependencyProperty WaterMarkProperty = DependencyProperty.Register("WaterMark", typeof(string), typeof(CustomDatePicker), new PropertyMetadata(null,new PropertyChangedCallback(OnWaterMarkChanged))); public DateTime DefaultCalendarDisplayDate { get { return (DateTime)GetValue(DefaultCalendarDisplayDateProperty); } set { SetValue(DefaultCalendarDisplayDateProperty, value); } } // Using a DependencyProperty as the backing store for DefaultCalendarDisplayDate. This enables animation, styling, binding, etc... public static readonly DependencyProperty DefaultCalendarDisplayDateProperty = DependencyProperty.Register("DefaultCalendarDisplayDate", typeof(DateTime), typeof(CustomDatePicker), new PropertyMetadata(null)); /// <summary> /// Invoked when the WaterMark dependency property reports a change. /// </summary> /// <param name="obj"></param> /// <param name="e"></param> private static void OnWaterMarkChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { CustomDatePicker cdp = obj as CustomDatePicker; if (cdp.SelectedDate==null&&cdp.MyTextBox!=null) { cdp.MyTextBox.Text = e.NewValue.ToString(); } currentWaterMark = e.NewValue.ToString(); } #endregion static CustomDatePicker() { DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomDatePicker), new FrameworkPropertyMetadata(typeof(CustomDatePicker))); } public CustomDatePicker() { // eventAggregator.GetEvent<UpdateLanguageEvent>().Subscribe(UpdateMaskLanguage); this._maskChars = new List<InputMaskChar>(); } public override void OnApplyTemplate() { base.OnApplyTemplate(); //load the text box control if (this.MyTextBox != null) { this.MyTextBox.GotFocus += new RoutedEventHandler(MyTextBox_GotFocus); this.MyTextBox.LostFocus += new RoutedEventHandler(MyTextBox_LostFocus); this.MyTextBox.TextChanged += new TextChangedEventHandler(MyTextBox_TextChanged); this.MyTextBox.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(MyTextBox_PreviewMouseLeftButtonUp); this.MyTextBox.PreviewKeyDown += MyTextBox_PreviewKeyDown; this.MyTextBox.PreviewTextInput += MyTextBox_PreviewTextInput; this.MyTextBox.SelectionChanged += MyTextBox_SelectionChanged; //default this.MyTextBox.Text = currentWaterMark; this.SelectedDateChanged += CustomDatePicker_SelectedDateChanged; this.Loaded += CustomDatePicker_Loaded; DataObject.AddPastingHandler(this, new DataObjectPastingEventHandler(MyTextBox_Paste)); //UpdateInputMask(); } } void CustomDatePicker_SelectedDateChanged(object sender, SelectionChangedEventArgs e) { //if (this.IsLoaded) //{ if (this.SelectedDate != null) { this.MyTextBox.Text = string.Format("{0:dd/MM/yyyy}", this.SelectedDate.GetValueOrDefault()); UpdateInputMask(); this._caretIndex = 10; this.MyTextBox.CaretIndex = 10; } else { this.MyTextBox.Text = WaterMark; } //} // this.DisplayDate = DateTime.Today; } void CustomDatePicker_Loaded(object sender, RoutedEventArgs e) { if (this.SelectedDate != null) { this.MyTextBox.Text = string.Format("{0:dd/MM/yyyy}", this.SelectedDate.GetValueOrDefault()); } else { // this.MyTextBox.Text = WaterMark; } } void MyTextBox_SelectionChanged(object sender, RoutedEventArgs e) { if (!isTextChanged) { TextBox t = (TextBox)sender; this._caretIndex = t.CaretIndex; } isTextChanged = false; } /// <summary> /// Invokes when a paste event is raised. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void MyTextBox_Paste(object sender, DataObjectPastingEventArgs e) { //TODO: play nicely here? // if (e.DataObject.GetDataPresent(typeof(string))) { string value = e.DataObject.GetData(typeof(string)).ToString(); string displayText; if (this.ValidateTextInternal(value, out displayText)) { this.MyTextBox.Text = displayText; } } e.CancelCommand(); } private void MyTextBox_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { if (this.SelectedDate == null) { if (e.ChangedButton == MouseButton.Left && (bool)this.MyTextBox.Tag == true) { this.MyTextBox.Select(0, 0); } this.MyTextBox.Tag = false; } } private void MyTextBox_TextChanged(object sender, TextChangedEventArgs e) { isTextChanged = true; if (string.IsNullOrWhiteSpace(this.MyTextBox.Text)) { this.SelectedDate = null; this.MyTextBox.Text = WaterMark; } } private void MyTextBox_LostFocus(object sender, RoutedEventArgs e) { if (this.MyTextBox.Text != null) { try { this.SelectedDate = DateTime.Parse(this.MyTextBox.Text); //this.DisplayDate = DateTime.Today; } catch (Exception) { this.SelectedDate = null; this.MyTextBox.Text = WaterMark; // this.MyTextBox.Text = null; } } if (this.DefaultCalendarDisplayDate != null) { this.DisplayDate = this.DefaultCalendarDisplayDate; } //if (this.SelectedDate != null) //{ // this.MyTextBox.Text = string.Format("{0:dd/MM/yyyy}", this.SelectedDate.GetValueOrDefault()); //} //else //{ // //this.MyTextBox.Text = WaterMark; //} } private void MyTextBox_GotFocus(object sender, RoutedEventArgs e) { this.MyTextBox.Tag = true; if (this.SelectedDate != null) { this.MyTextBox.Text = string.Format("{0:dd/MM/yyyy}", this.SelectedDate.GetValueOrDefault()); } else { UpdateInputMask(); // this.MyTextBox.Text = "__/__/____"; this.MyTextBox.Select(0, 0); } } void MyTextBox_PreviewKeyDown(object sender, KeyEventArgs e) { //no mask specified, just function as a normal textbox if (this._maskChars.Count == 0) return; if (e.Key == Key.Delete) { //delete key pressed: delete all text this.MyTextBox.Text = this.GetDefaultText(); this._caretIndex = this.MyTextBox.CaretIndex = 0; e.Handled = true; } else { //backspace key pressed if (e.Key == Key.Back) { if (this._caretIndex > 0 || this.MyTextBox.SelectionLength > 0) { if (this.MyTextBox.SelectionLength > 0) { //if one or more characters selected, delete them this.DeleteSelectedText(); } else { //if no characters selected, shift the caret back to the previous non-literal char and delete it this.MoveBack(); char[] characters = this.MyTextBox.Text.ToCharArray(); characters[this._caretIndex] = this._maskChars[this._caretIndex].GetDefaultChar(); this.MyTextBox.Text = new string(characters); } //update the base class caret index, and swallow the event this.MyTextBox.CaretIndex = this._caretIndex; e.Handled = true; } } else if (e.Key == Key.Left) { //move back to the previous non-literal character this.MoveBack(); e.Handled = true; } else if (e.Key == Key.Right || e.Key == Key.Space) { //move forwards to the next non-literal character this.MoveForward(); e.Handled = true; } } } void MyTextBox_PreviewTextInput(object sender, TextCompositionEventArgs e) { if (this._maskChars.Count == 0) return; this._caretIndex = this.MyTextBox.CaretIndex = this.MyTextBox.SelectionStart; if (this._caretIndex == this._maskChars.Count) { //at the end of the character count defined by the input mask- no more characters allowed e.Handled = true; } else { //validate the character against its validation scheme bool isValid = this.ValidateInputChar(char.Parse(e.Text), this._maskChars[this._caretIndex].ValidationFlags); if (isValid) { //delete any selected text if (this.MyTextBox.SelectionLength > 0) { this.DeleteSelectedText(); } //insert the new character char[] characters = this.MyTextBox.Text.ToCharArray(); characters[this._caretIndex] = char.Parse(e.Text); this.MyTextBox.Text = new string(characters); //move the caret on this.MoveForward(); } e.Handled = true; } } #region validateMethod protected virtual bool IsPlaceholderChar(char character, out InputMaskValidationFlags validationFlags) { validationFlags = InputMaskValidationFlags.None; switch (character.ToString().ToUpper()) { case "I": validationFlags = InputMaskValidationFlags.AllowInteger; break; case "D": validationFlags = InputMaskValidationFlags.AllowDecimal; break; case "A": validationFlags = InputMaskValidationFlags.AllowAlphabet; break; case "W": validationFlags = (InputMaskValidationFlags.AllowAlphanumeric); break; } return (validationFlags != InputMaskValidationFlags.None); } /// <summary> /// Returns a value indicating if the current text value is valid. /// </summary> /// <returns></returns> public virtual bool ValidateTextInternal(string text, out string displayText) { if (this._maskChars.Count == 0) { displayText = text; return true; } StringBuilder displayTextBuilder = new StringBuilder(this.GetDefaultText()); bool valid = (!string.IsNullOrEmpty(text) && text.Length <= this._maskChars.Count); if (valid) { for (int i = 0; i < text.Length; i++) { if (!this._maskChars[i].IsLiteral()) { if (this.ValidateInputChar(text[i], this._maskChars[i].ValidationFlags)) { displayTextBuilder[i] = text[i]; } else { valid = false; } } } } displayText = displayTextBuilder.ToString(); return valid; } /// <summary> /// Validates the specified character against all selected validation schemes. /// </summary> /// <param name="input"></param> /// <param name="validationFlags"></param> /// <returns></returns> protected virtual bool ValidateInputChar(char input, InputMaskValidationFlags validationFlags) { bool valid = (validationFlags == InputMaskValidationFlags.None); if (!valid) { Array values = Enum.GetValues(typeof(InputMaskValidationFlags)); //iterate through the validation schemes foreach (object o in values) { InputMaskValidationFlags instance = (InputMaskValidationFlags)(int)o; if ((instance & validationFlags) != 0) { if (this.ValidateCharInternal(input, instance)) { valid = true; break; } } } } return valid; } /// <summary> /// Validates the specified character against its input mask validation scheme. /// </summary> /// <param name="input"></param> /// <param name="validationType"></param> /// <returns></returns> private bool ValidateCharInternal(char input, InputMaskValidationFlags validationType) { bool valid = false; switch (validationType) { case InputMaskValidationFlags.AllowInteger: case InputMaskValidationFlags.AllowDecimal: int i; if (validationType == InputMaskValidationFlags.AllowDecimal && input == '.' && !this.MyTextBox.Text.Contains('.')) { valid = true; } else { valid = int.TryParse(input.ToString(), out i); } break; case InputMaskValidationFlags.AllowAlphabet: valid = char.IsLetter(input); break; case InputMaskValidationFlags.AllowAlphanumeric: valid = (char.IsLetter(input) || char.IsNumber(input)); break; } return valid; } #endregion #region Method public void UpdateInputMask() { string text = this.MyTextBox.Text; this._maskChars.Clear(); // this.MyTextBox.Text = string.Empty; string mask = "ii/ii/iiii"; //if (string.IsNullOrEmpty(mask)) // return; this._maskChars.Add(new InputMaskChar() { Literal = (char)0, DefaultChar = 'd', ValidationFlags = InputMaskValidationFlags.AllowInteger }); this._maskChars.Add(new InputMaskChar() { Literal = (char)0, DefaultChar = 'd', ValidationFlags = InputMaskValidationFlags.AllowInteger }); this._maskChars.Add(new InputMaskChar() { Literal = (char)1, DefaultChar = '/', ValidationFlags = InputMaskValidationFlags.None }); this._maskChars.Add(new InputMaskChar() { Literal = (char)0, DefaultChar = 'M', ValidationFlags = InputMaskValidationFlags.AllowInteger }); this._maskChars.Add(new InputMaskChar() { Literal = (char)0, DefaultChar = 'M', ValidationFlags = InputMaskValidationFlags.AllowInteger }); this._maskChars.Add(new InputMaskChar() { Literal = (char)1, DefaultChar = '/', ValidationFlags = InputMaskValidationFlags.None }); this._maskChars.Add(new InputMaskChar() { Literal = (char)0, DefaultChar = 'y', ValidationFlags = InputMaskValidationFlags.AllowInteger }); this._maskChars.Add(new InputMaskChar() { Literal = (char)0, DefaultChar = 'y', ValidationFlags = InputMaskValidationFlags.AllowInteger }); this._maskChars.Add(new InputMaskChar() { Literal = (char)0, DefaultChar = 'y', ValidationFlags = InputMaskValidationFlags.AllowInteger }); this._maskChars.Add(new InputMaskChar() { Literal = (char)0, DefaultChar = 'y', ValidationFlags = InputMaskValidationFlags.AllowInteger }); //InputMaskValidationFlags validationFlags = InputMaskValidationFlags.None; //for (int i = 0; i < mask.Length; i++) //{ // bool isPlaceholder = this.IsPlaceholderChar(mask[i], out validationFlags); // if (isPlaceholder) // { // this._maskChars.Add(new InputMaskChar(validationFlags)); // } // else // { // this._maskChars.Add(new InputMaskChar(mask[i])); // } //} string displayText; if (text.Length > 0 && this.ValidateTextInternal(text, out displayText)) { this.MyTextBox.Text = displayText; } else { this.MyTextBox.Text = this.GetDefaultText(); } } private void UpdateMaskLanguage(string obj) { if (this.SelectedDate == null) { //this.MyTextBox.Text = WaterMark; } } public string GetDefaultText() { StringBuilder text = new StringBuilder(); foreach (InputMaskChar maskChar in this._maskChars) { text.Append(maskChar.GetDefaultChar()); } return text.ToString(); } /// <summary> /// Moves the caret forward to the next non-literal position. /// </summary> private void MoveForward() { int pos = this._caretIndex; while (pos < this._maskChars.Count) { if (++pos == this._maskChars.Count || !this._maskChars[pos].IsLiteral()) { this._caretIndex = this.MyTextBox.CaretIndex = pos; break; } } } /// <summary> /// Moves the caret backward to the previous non-literal position. /// </summary> private void MoveBack() { int pos = this._caretIndex; while (pos > 0) { if (--pos == 0 || !this._maskChars[pos].IsLiteral()) { this._caretIndex = this.MyTextBox.CaretIndex = pos; break; } } } /// <summary> /// Deletes the currently selected text. /// </summary> protected virtual void DeleteSelectedText() { StringBuilder text = new StringBuilder(this.MyTextBox.Text); string defaultText = this.GetDefaultText(); int selectionStart = this.MyTextBox.SelectionStart; int selectionLength = this.MyTextBox.SelectionLength; text.Remove(selectionStart, selectionLength); text.Insert(selectionStart, defaultText.Substring(selectionStart, selectionLength)); this.MyTextBox.Text = text.ToString(); //reset the caret position this.MyTextBox.CaretIndex = this._caretIndex = selectionStart; } #endregion [Flags] protected enum InputMaskValidationFlags { None = 0, AllowInteger = 1, AllowDecimal = 2, AllowAlphabet = 4, AllowAlphanumeric = 8 } private class InputMaskChar { private InputMaskValidationFlags _validationFlags; private char _literal; public char DefaultChar { get; set; } public InputMaskChar(InputMaskValidationFlags validationFlags) { this._validationFlags = validationFlags; this._literal = (char)0; } public InputMaskChar(char literal) { this._literal = literal; } public InputMaskChar() { } public InputMaskValidationFlags ValidationFlags { get { return this._validationFlags; } set { this._validationFlags = value; } } public char Literal { get { return this._literal; } set { this._literal = value; } } public bool IsLiteral() { return (this._literal != (char)0); } public char GetDefaultChar() { return DefaultChar; } } } }

浙公网安备 33010602011771号