具有自定义样式的 WPF Inherited Datepicker 不允许将子项标记为
WPF Inherited Datepicker with custom style won't allow children to be tabbed to
我想创建一个自定义 DatePicker,我用 MaskedTextBox(来自 WPFToolkit)代替了 DatePickerTextBox。无论出于何种原因,我都无法切换到控件中的 MaskedTextBox。相反,当项目被选中时 "highlights/focuses" 整个控件和再次被选中时将转到下一个可用控件。
我希望能够位于自定义控件之前的某个控件处,当切换到该控件时会将焦点放在自定义控件内的 MaskedTextBox 上。
此控件取决于存在的 MahApps 和 WPFToolkit。
Public Partial Class CustomMaskedDatePicker
Inherits DatePicker
Public Shared ReadOnly MaskedSelectedDateProperty As DependencyProperty = DependencyProperty.Register("MaskedSelectedDate", GetType(String), GetType(CustomMaskedDatePicker), New FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, AddressOf OnMaskedSelectedDateChanged))
Shared Sub New()
DefaultStyleKeyProperty.OverrideMetadata(GetType(CustomMaskedDatePicker), New FrameworkPropertyMetadata(GetType(CustomMaskedDatePicker)))
End Sub
Private Shared Sub OnMaskedSelectedDateChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
Dim tempDate As Date
Dim selectedDate As Date? = DirectCast(d, CustomMaskedDatePicker).SelectedDate
Dim tempDateString As String = e.NewValue
If tempDateString = "" AndAlso IsNothing(DirectCast(d, CustomMaskedDatePicker).SelectedDate) Then Exit Sub
If tempDateString = "" Then
DirectCast(d, CustomMaskedDatePicker).SelectedDate = Nothing
Exit Sub
End If
If tempDateString.Contains("_") Then
DirectCast(d, CustomMaskedDatePicker).MaskedSelectedDate = If(IsNothing(selectedDate), "", String.Format("{0}{1}{2}", CDate(selectedDate).Month.ToString.PadLeft(2, "0"), CDate(selectedDate).Day.ToString.PadLeft(2, "0"), CDate(selectedDate).Year.ToString.PadLeft(4, "0")))
Exit Sub
End If
If Not tempDateString.Contains("/") Then tempDateString = String.Format("{0}/{1}/{2}", tempDateString.Substring(0, 2), tempDateString.Substring(2, 2), tempDateString.Substring(4, 4))
If Not Date.TryParse(tempDateString, tempDate) Then
DirectCast(d, CustomMaskedDatePicker).MaskedSelectedDate = If(IsNothing(selectedDate), "", String.Format("{0}{1}{2}", CDate(selectedDate).Month.ToString.PadLeft(2, "0"), CDate(selectedDate).Day.ToString.PadLeft(2, "0"), CDate(selectedDate).Year.ToString.PadLeft(4, "0")))
Exit Sub
End If
If IsNothing(selectedDate) OrElse selectedDate <> tempDate Then DirectCast(d, CustomMaskedDatePicker).SelectedDate = tempDate
End Sub
Public Property MaskedSelectedDate As String
Get
Return GetValue(MaskedSelectedDateProperty).ToString
End Get
Set
SetValue(MaskedSelectedDateProperty, value.Replace("/", ""))
End Set
End Property
Friend Const ElementMaskedTextBox As String = "PART_MaskedTextBox"
Protected Overrides Sub OnSelectedDateChanged(e As SelectionChangedEventArgs)
MyBase.OnSelectedDateChanged(e)
Dim dt As Date = CDate(e.AddedItems(0)).ToShortDateString()
MaskedSelectedDate = String.Format("{0}{1}{2}", dt.Month.ToString.PadLeft(2, "0"), dt.Day.ToString.PadLeft(2, "0"), dt.Year.ToString.PadLeft(4, "0"))
End Sub
End Class
XAML:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:App"
xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style TargetType="{x:Type local:CustomMaskedDatePicker}">
<Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
<Setter Property="Background" Value="{DynamicResource ControlBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource TextBoxBorderBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CalendarStyle" Value="{DynamicResource MetroCalendar}" />
<Setter Property="controls:ControlsHelper.FocusBorderBrush" Value="{DynamicResource TextBoxFocusBorderBrush}" />
<Setter Property="controls:ControlsHelper.MouseOverBorderBrush" Value="{DynamicResource TextBoxMouseOverBorderBrush}" />
<Setter Property="controls:TextBoxHelper.IsMonitoring" Value="True" />
<Setter Property="FontFamily" Value="{DynamicResource ContentFontFamily}" />
<Setter Property="FontSize" Value="{DynamicResource ContentFontSize}" />
<Setter Property="IsTodayHighlighted" Value="True" />
<Setter Property="MinHeight" Value="26" />
<Setter Property="Padding" Value="0" />
<Setter Property="SelectedDateFormat" Value="Short" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Validation.ErrorTemplate" Value="{DynamicResource ValidationErrorTemplate}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomMaskedDatePicker}">
<Grid x:Name="PART_Root">
<Border x:Name="Base"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<Grid x:Name="PART_InnerGrid" Margin="-5,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button x:Name="PART_Button"
Grid.Column="1"
Height="Auto"
Style="{DynamicResource ChromelessButtonStyle}"
Foreground="{TemplateBinding Foreground}"
IsTabStop="False"
HorizontalAlignment="Right"
Margin="0,2,2,2">
<ContentControl Style="{DynamicResource {x:Type ContentControl}}"
Content="M34,52H31V38.5C29.66,39.9 28.16,40.78 26.34,41.45V37.76C27.3,37.45 28.34,36.86 29.46,36C30.59,35.15 31.36,34.15 31.78,33H34V52M45,52V48H37V45L45,33H48V45H50V48H48V52H45M45,45V38.26L40.26,45H45M18,57V23H23V20A2,2 0 0,1 25,18H29C30.11,18 31,18.9 31,20V23H45V20A2,2 0 0,1 47,18H51C52.11,18 53,18.9 53,20V23H58V57H18M21,54H55V31H21V54M48.5,20A1.5,1.5 0 0,0 47,21.5V24.5A1.5,1.5 0 0,0 48.5,26H49.5C50.34,26 51,25.33 51,24.5V21.5A1.5,1.5 0 0,0 49.5,20H48.5M26.5,20A1.5,1.5 0 0,0 25,21.5V24.5A1.5,1.5 0 0,0 26.5,26H27.5A1.5,1.5 0 0,0 29,24.5V21.5A1.5,1.5 0 0,0 27.5,20H26.5Z"
Padding="0"
Width="21"
Height="16">
<ContentControl.Template>
<ControlTemplate TargetType="{x:Type ContentControl}">
<Viewbox Margin="{TemplateBinding Padding}">
<Path Fill="{TemplateBinding Foreground}"
Stretch="Uniform"
Data="{Binding Content, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay, Converter={local:xConNullToUnsetValueConverter}}"
SnapsToDevicePixels="True"
UseLayoutRounding="False" />
</Viewbox>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
</Button>
<DatePickerTextBox Grid.Column="0" x:Name="PART_TextBox" Visibility="Hidden" IsTabStop="False"/>
<xctk:MaskedTextBox x:Name="PART_MaskedTextBox" Style="{DynamicResource {x:Type TextBox}}"
Grid.Column="0"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Foreground="{TemplateBinding Foreground}"
Background="Transparent"
FocusVisualStyle ="{x:Null}"
FontFamily ="{DynamicResource ContentFontFamily}"
FontSize="{DynamicResource ContentFontSize}"
ScrollViewer.PanningMode="VerticalFirst"
Stylus.IsFlicksEnabled="False"
CaretBrush="{DynamicResource BlackBrush}"
ContextMenu="{DynamicResource TextBoxMetroContextMenu}"
Focusable="{TemplateBinding Focusable}"
Mask="00/00/0000"
Text="{Binding MaskedSelectedDate, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
BorderThickness="0"
IsTabStop="True"/>
</Grid>
<Popup x:Name="PART_Popup"
AllowsTransparency="True"
Placement="Bottom"
PlacementTarget="{Binding ElementName=PART_Root}"
StaysOpen="False"/>
<Border x:Name="DisabledVisualElement"
Background="{DynamicResource ControlsDisabledBrush}"
BorderBrush="{DynamicResource ControlsDisabledBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Opacity="0"
IsHitTestVisible="False"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Base" Property="BorderBrush" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(controls:ControlsHelper.MouseOverBorderBrush)}" />
</Trigger>
<Trigger Property="IsFocused" Value="True" SourceName="PART_MaskedTextBox">
<Setter TargetName="Base" Property="BorderBrush" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(controls:ControlsHelper.FocusBorderBrush)}" />
</Trigger>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter TargetName="Base" Property="BorderBrush" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(controls:ControlsHelper.FocusBorderBrush)}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="DisabledVisualElement" Property="Opacity" Value="0.6" />
</Trigger>
<Trigger SourceName="PART_Button" Property="IsMouseOver" Value="True">
<Setter TargetName="PART_Button" Property="Background" Value="{DynamicResource GrayBrush8}" />
<Setter TargetName="PART_Button" Property="Foreground" Value="{DynamicResource AccentColorBrush}" />
</Trigger>
<Trigger SourceName="PART_Button" Property="IsPressed" Value="True">
<Setter TargetName="PART_Button" Property="Background" Value="{DynamicResource BlackBrush}" />
<Setter TargetName="PART_Button" Property="Foreground" Value="{DynamicResource WhiteBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
通过覆盖代码中的 OnGotFocus 和 OnGotKeyboardFocus 解决了这个问题。
Public Partial Class CustomMaskedDatePicker
Inherits DatePicker
Public Shared ReadOnly MaskedSelectedDateProperty As DependencyProperty = DependencyProperty.Register("MaskedSelectedDate", GetType(String), GetType(CustomMaskedDatePicker), New FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, AddressOf OnMaskedSelectedDateChanged))
Friend Const ElementMaskedTextBox As String = "PART_MaskedTextBox"
Public Property MaskedSelectedDate As String
Get
Return GetValue(MaskedSelectedDateProperty).ToString
End Get
Set
SetValue(MaskedSelectedDateProperty, value.Replace("/", ""))
End Set
End Property
Shared Sub New()
DefaultStyleKeyProperty.OverrideMetadata(GetType(CustomMaskedDatePicker), New FrameworkPropertyMetadata(GetType(CustomMaskedDatePicker)))
End Sub
Protected Overrides Sub OnGotFocus(e As RoutedEventArgs)
Dim mskTxtBx As Object = Template.FindName(ElementMaskedTextBox, Me)
If IsNothing(mskTxtBx) Then
MyBase.OnGotFocus(e)
Else
DirectCast(mskTxtBx, MaskedTextBox).Focus
End If
End Sub
Protected Overrides Sub OnGotKeyboardFocus(e As KeyboardFocusChangedEventArgs)
Dim mskTxtBx As Object = Template.FindName(ElementMaskedTextBox, Me)
If IsNothing(mskTxtBx) Then
MyBase.OnGotFocus(e)
Else
DirectCast(mskTxtBx, MaskedTextBox).Focus
End If
End Sub
Private Shared Sub OnMaskedSelectedDateChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
Dim tempDate As Date
Dim selectedDate As Date? = DirectCast(d, CustomMaskedDatePicker).SelectedDate
Dim tempDateString As String = e.NewValue
If tempDateString = "" AndAlso IsNothing(DirectCast(d, CustomMaskedDatePicker).SelectedDate) Then Exit Sub
If tempDateString = "" Then
DirectCast(d, CustomMaskedDatePicker).SelectedDate = Nothing
Exit Sub
End If
If tempDateString.Contains("_") Then
DirectCast(d, CustomMaskedDatePicker).MaskedSelectedDate = If(IsNothing(selectedDate), "", String.Format("{0}{1}{2}", CDate(selectedDate).Month.ToString.PadLeft(2, "0"), CDate(selectedDate).Day.ToString.PadLeft(2, "0"), CDate(selectedDate).Year.ToString.PadLeft(4, "0")))
Exit Sub
End If
If Not tempDateString.Contains("/") Then tempDateString = String.Format("{0}/{1}/{2}", tempDateString.Substring(0, 2), tempDateString.Substring(2, 2), tempDateString.Substring(4, 4))
If Not Date.TryParse(tempDateString, tempDate) Then
DirectCast(d, CustomMaskedDatePicker).MaskedSelectedDate = If(IsNothing(selectedDate), "", String.Format("{0}{1}{2}", CDate(selectedDate).Month.ToString.PadLeft(2, "0"), CDate(selectedDate).Day.ToString.PadLeft(2, "0"), CDate(selectedDate).Year.ToString.PadLeft(4, "0")))
Exit Sub
End If
If IsNothing(selectedDate) OrElse selectedDate <> tempDate Then DirectCast(d, CustomMaskedDatePicker).SelectedDate = tempDate
End Sub
Protected Overrides Sub OnSelectedDateChanged(e As SelectionChangedEventArgs)
MyBase.OnSelectedDateChanged(e)
Dim dt As Date = CDate(e.AddedItems(0)).ToShortDateString()
MaskedSelectedDate = String.Format("{0}{1}{2}", dt.Month.ToString.PadLeft(2, "0"), dt.Day.ToString.PadLeft(2, "0"), dt.Year.ToString.PadLeft(4, "0"))
End Sub
End Class
我想创建一个自定义 DatePicker,我用 MaskedTextBox(来自 WPFToolkit)代替了 DatePickerTextBox。无论出于何种原因,我都无法切换到控件中的 MaskedTextBox。相反,当项目被选中时 "highlights/focuses" 整个控件和再次被选中时将转到下一个可用控件。
我希望能够位于自定义控件之前的某个控件处,当切换到该控件时会将焦点放在自定义控件内的 MaskedTextBox 上。
此控件取决于存在的 MahApps 和 WPFToolkit。
Public Partial Class CustomMaskedDatePicker
Inherits DatePicker
Public Shared ReadOnly MaskedSelectedDateProperty As DependencyProperty = DependencyProperty.Register("MaskedSelectedDate", GetType(String), GetType(CustomMaskedDatePicker), New FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, AddressOf OnMaskedSelectedDateChanged))
Shared Sub New()
DefaultStyleKeyProperty.OverrideMetadata(GetType(CustomMaskedDatePicker), New FrameworkPropertyMetadata(GetType(CustomMaskedDatePicker)))
End Sub
Private Shared Sub OnMaskedSelectedDateChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
Dim tempDate As Date
Dim selectedDate As Date? = DirectCast(d, CustomMaskedDatePicker).SelectedDate
Dim tempDateString As String = e.NewValue
If tempDateString = "" AndAlso IsNothing(DirectCast(d, CustomMaskedDatePicker).SelectedDate) Then Exit Sub
If tempDateString = "" Then
DirectCast(d, CustomMaskedDatePicker).SelectedDate = Nothing
Exit Sub
End If
If tempDateString.Contains("_") Then
DirectCast(d, CustomMaskedDatePicker).MaskedSelectedDate = If(IsNothing(selectedDate), "", String.Format("{0}{1}{2}", CDate(selectedDate).Month.ToString.PadLeft(2, "0"), CDate(selectedDate).Day.ToString.PadLeft(2, "0"), CDate(selectedDate).Year.ToString.PadLeft(4, "0")))
Exit Sub
End If
If Not tempDateString.Contains("/") Then tempDateString = String.Format("{0}/{1}/{2}", tempDateString.Substring(0, 2), tempDateString.Substring(2, 2), tempDateString.Substring(4, 4))
If Not Date.TryParse(tempDateString, tempDate) Then
DirectCast(d, CustomMaskedDatePicker).MaskedSelectedDate = If(IsNothing(selectedDate), "", String.Format("{0}{1}{2}", CDate(selectedDate).Month.ToString.PadLeft(2, "0"), CDate(selectedDate).Day.ToString.PadLeft(2, "0"), CDate(selectedDate).Year.ToString.PadLeft(4, "0")))
Exit Sub
End If
If IsNothing(selectedDate) OrElse selectedDate <> tempDate Then DirectCast(d, CustomMaskedDatePicker).SelectedDate = tempDate
End Sub
Public Property MaskedSelectedDate As String
Get
Return GetValue(MaskedSelectedDateProperty).ToString
End Get
Set
SetValue(MaskedSelectedDateProperty, value.Replace("/", ""))
End Set
End Property
Friend Const ElementMaskedTextBox As String = "PART_MaskedTextBox"
Protected Overrides Sub OnSelectedDateChanged(e As SelectionChangedEventArgs)
MyBase.OnSelectedDateChanged(e)
Dim dt As Date = CDate(e.AddedItems(0)).ToShortDateString()
MaskedSelectedDate = String.Format("{0}{1}{2}", dt.Month.ToString.PadLeft(2, "0"), dt.Day.ToString.PadLeft(2, "0"), dt.Year.ToString.PadLeft(4, "0"))
End Sub
End Class
XAML:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:App"
xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style TargetType="{x:Type local:CustomMaskedDatePicker}">
<Setter Property="Foreground" Value="{DynamicResource TextBrush}" />
<Setter Property="Background" Value="{DynamicResource ControlBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource TextBoxBorderBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CalendarStyle" Value="{DynamicResource MetroCalendar}" />
<Setter Property="controls:ControlsHelper.FocusBorderBrush" Value="{DynamicResource TextBoxFocusBorderBrush}" />
<Setter Property="controls:ControlsHelper.MouseOverBorderBrush" Value="{DynamicResource TextBoxMouseOverBorderBrush}" />
<Setter Property="controls:TextBoxHelper.IsMonitoring" Value="True" />
<Setter Property="FontFamily" Value="{DynamicResource ContentFontFamily}" />
<Setter Property="FontSize" Value="{DynamicResource ContentFontSize}" />
<Setter Property="IsTodayHighlighted" Value="True" />
<Setter Property="MinHeight" Value="26" />
<Setter Property="Padding" Value="0" />
<Setter Property="SelectedDateFormat" Value="Short" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Validation.ErrorTemplate" Value="{DynamicResource ValidationErrorTemplate}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomMaskedDatePicker}">
<Grid x:Name="PART_Root">
<Border x:Name="Base"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<Grid x:Name="PART_InnerGrid" Margin="-5,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button x:Name="PART_Button"
Grid.Column="1"
Height="Auto"
Style="{DynamicResource ChromelessButtonStyle}"
Foreground="{TemplateBinding Foreground}"
IsTabStop="False"
HorizontalAlignment="Right"
Margin="0,2,2,2">
<ContentControl Style="{DynamicResource {x:Type ContentControl}}"
Content="M34,52H31V38.5C29.66,39.9 28.16,40.78 26.34,41.45V37.76C27.3,37.45 28.34,36.86 29.46,36C30.59,35.15 31.36,34.15 31.78,33H34V52M45,52V48H37V45L45,33H48V45H50V48H48V52H45M45,45V38.26L40.26,45H45M18,57V23H23V20A2,2 0 0,1 25,18H29C30.11,18 31,18.9 31,20V23H45V20A2,2 0 0,1 47,18H51C52.11,18 53,18.9 53,20V23H58V57H18M21,54H55V31H21V54M48.5,20A1.5,1.5 0 0,0 47,21.5V24.5A1.5,1.5 0 0,0 48.5,26H49.5C50.34,26 51,25.33 51,24.5V21.5A1.5,1.5 0 0,0 49.5,20H48.5M26.5,20A1.5,1.5 0 0,0 25,21.5V24.5A1.5,1.5 0 0,0 26.5,26H27.5A1.5,1.5 0 0,0 29,24.5V21.5A1.5,1.5 0 0,0 27.5,20H26.5Z"
Padding="0"
Width="21"
Height="16">
<ContentControl.Template>
<ControlTemplate TargetType="{x:Type ContentControl}">
<Viewbox Margin="{TemplateBinding Padding}">
<Path Fill="{TemplateBinding Foreground}"
Stretch="Uniform"
Data="{Binding Content, RelativeSource={RelativeSource TemplatedParent}, Mode=OneWay, Converter={local:xConNullToUnsetValueConverter}}"
SnapsToDevicePixels="True"
UseLayoutRounding="False" />
</Viewbox>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
</Button>
<DatePickerTextBox Grid.Column="0" x:Name="PART_TextBox" Visibility="Hidden" IsTabStop="False"/>
<xctk:MaskedTextBox x:Name="PART_MaskedTextBox" Style="{DynamicResource {x:Type TextBox}}"
Grid.Column="0"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Foreground="{TemplateBinding Foreground}"
Background="Transparent"
FocusVisualStyle ="{x:Null}"
FontFamily ="{DynamicResource ContentFontFamily}"
FontSize="{DynamicResource ContentFontSize}"
ScrollViewer.PanningMode="VerticalFirst"
Stylus.IsFlicksEnabled="False"
CaretBrush="{DynamicResource BlackBrush}"
ContextMenu="{DynamicResource TextBoxMetroContextMenu}"
Focusable="{TemplateBinding Focusable}"
Mask="00/00/0000"
Text="{Binding MaskedSelectedDate, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
BorderThickness="0"
IsTabStop="True"/>
</Grid>
<Popup x:Name="PART_Popup"
AllowsTransparency="True"
Placement="Bottom"
PlacementTarget="{Binding ElementName=PART_Root}"
StaysOpen="False"/>
<Border x:Name="DisabledVisualElement"
Background="{DynamicResource ControlsDisabledBrush}"
BorderBrush="{DynamicResource ControlsDisabledBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Opacity="0"
IsHitTestVisible="False"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Base" Property="BorderBrush" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(controls:ControlsHelper.MouseOverBorderBrush)}" />
</Trigger>
<Trigger Property="IsFocused" Value="True" SourceName="PART_MaskedTextBox">
<Setter TargetName="Base" Property="BorderBrush" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(controls:ControlsHelper.FocusBorderBrush)}" />
</Trigger>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter TargetName="Base" Property="BorderBrush" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(controls:ControlsHelper.FocusBorderBrush)}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="DisabledVisualElement" Property="Opacity" Value="0.6" />
</Trigger>
<Trigger SourceName="PART_Button" Property="IsMouseOver" Value="True">
<Setter TargetName="PART_Button" Property="Background" Value="{DynamicResource GrayBrush8}" />
<Setter TargetName="PART_Button" Property="Foreground" Value="{DynamicResource AccentColorBrush}" />
</Trigger>
<Trigger SourceName="PART_Button" Property="IsPressed" Value="True">
<Setter TargetName="PART_Button" Property="Background" Value="{DynamicResource BlackBrush}" />
<Setter TargetName="PART_Button" Property="Foreground" Value="{DynamicResource WhiteBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
通过覆盖代码中的 OnGotFocus 和 OnGotKeyboardFocus 解决了这个问题。
Public Partial Class CustomMaskedDatePicker
Inherits DatePicker
Public Shared ReadOnly MaskedSelectedDateProperty As DependencyProperty = DependencyProperty.Register("MaskedSelectedDate", GetType(String), GetType(CustomMaskedDatePicker), New FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, AddressOf OnMaskedSelectedDateChanged))
Friend Const ElementMaskedTextBox As String = "PART_MaskedTextBox"
Public Property MaskedSelectedDate As String
Get
Return GetValue(MaskedSelectedDateProperty).ToString
End Get
Set
SetValue(MaskedSelectedDateProperty, value.Replace("/", ""))
End Set
End Property
Shared Sub New()
DefaultStyleKeyProperty.OverrideMetadata(GetType(CustomMaskedDatePicker), New FrameworkPropertyMetadata(GetType(CustomMaskedDatePicker)))
End Sub
Protected Overrides Sub OnGotFocus(e As RoutedEventArgs)
Dim mskTxtBx As Object = Template.FindName(ElementMaskedTextBox, Me)
If IsNothing(mskTxtBx) Then
MyBase.OnGotFocus(e)
Else
DirectCast(mskTxtBx, MaskedTextBox).Focus
End If
End Sub
Protected Overrides Sub OnGotKeyboardFocus(e As KeyboardFocusChangedEventArgs)
Dim mskTxtBx As Object = Template.FindName(ElementMaskedTextBox, Me)
If IsNothing(mskTxtBx) Then
MyBase.OnGotFocus(e)
Else
DirectCast(mskTxtBx, MaskedTextBox).Focus
End If
End Sub
Private Shared Sub OnMaskedSelectedDateChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
Dim tempDate As Date
Dim selectedDate As Date? = DirectCast(d, CustomMaskedDatePicker).SelectedDate
Dim tempDateString As String = e.NewValue
If tempDateString = "" AndAlso IsNothing(DirectCast(d, CustomMaskedDatePicker).SelectedDate) Then Exit Sub
If tempDateString = "" Then
DirectCast(d, CustomMaskedDatePicker).SelectedDate = Nothing
Exit Sub
End If
If tempDateString.Contains("_") Then
DirectCast(d, CustomMaskedDatePicker).MaskedSelectedDate = If(IsNothing(selectedDate), "", String.Format("{0}{1}{2}", CDate(selectedDate).Month.ToString.PadLeft(2, "0"), CDate(selectedDate).Day.ToString.PadLeft(2, "0"), CDate(selectedDate).Year.ToString.PadLeft(4, "0")))
Exit Sub
End If
If Not tempDateString.Contains("/") Then tempDateString = String.Format("{0}/{1}/{2}", tempDateString.Substring(0, 2), tempDateString.Substring(2, 2), tempDateString.Substring(4, 4))
If Not Date.TryParse(tempDateString, tempDate) Then
DirectCast(d, CustomMaskedDatePicker).MaskedSelectedDate = If(IsNothing(selectedDate), "", String.Format("{0}{1}{2}", CDate(selectedDate).Month.ToString.PadLeft(2, "0"), CDate(selectedDate).Day.ToString.PadLeft(2, "0"), CDate(selectedDate).Year.ToString.PadLeft(4, "0")))
Exit Sub
End If
If IsNothing(selectedDate) OrElse selectedDate <> tempDate Then DirectCast(d, CustomMaskedDatePicker).SelectedDate = tempDate
End Sub
Protected Overrides Sub OnSelectedDateChanged(e As SelectionChangedEventArgs)
MyBase.OnSelectedDateChanged(e)
Dim dt As Date = CDate(e.AddedItems(0)).ToShortDateString()
MaskedSelectedDate = String.Format("{0}{1}{2}", dt.Month.ToString.PadLeft(2, "0"), dt.Day.ToString.PadLeft(2, "0"), dt.Year.ToString.PadLeft(4, "0"))
End Sub
End Class