最近项目开发中,发现需要一个选择年月而不需要选择日的控件,于是想到基于ComboBox,并模仿DatePicker样子来实现该效果,废话不多说直接上代码:
xaml:
<Style TargetType="{x:Type ComboBox}">
<Setter Property="Height" Value="25" />
<Setter Property="BorderBrush" Value="{StaticResource Border.Static}" />
<Setter Property="Margin" Value="3" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.7*" />
<ColumnDefinition Width="0.3*" MaxWidth="30" />
</Grid.ColumnDefinitions>
<Border
Name="border"
Grid.Column="0"
Grid.ColumnSpan="2"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="1"
CornerRadius="1" />
<TextBlock
Name="contentHost"
Margin="5"
Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=DateText}" />
<TextBlock
x:Name="Message"
Margin="5"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Foreground="#FF707070"
IsHitTestVisible="False"
Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Watermark}"
TextAlignment="Center"
Visibility="Collapsed" />
<!-- ToggleButton 已数据绑定到 ComboBox 本身以切换 IsDropDownOpen -->
<ToggleButton
x:Name="ToggleButton"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="1"
ClickMode="Press"
Focusable="false"
IsChecked="{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
Style="{StaticResource ArrowToggleButton}" />
<!-- Popup 可显示 ComboBox 中的项列表。IsOpen 已数据绑定到通过 ComboBoxToggleButton 来切换的 IsDropDownOpen -->
<Popup
x:Name="Popup"
Width="160"
AllowsTransparency="True"
Focusable="False"
IsOpen="{TemplateBinding IsDropDownOpen}"
Placement="Bottom"
PlacementTarget="{Binding ElementName=border}"
PopupAnimation="Slide">
<Border
x:Name="DropDownBorder"
Background="White"
BorderBrush="#FF3D9CAE"
BorderThickness="1"
CornerRadius="3">
<Grid
x:Name="DropDown"
Width="160"
Height="130"
SnapsToDevicePixels="True">
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border BorderBrush="#FF3D9CAE" BorderThickness="0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="30" />
</Grid.ColumnDefinitions>
<DockPanel Grid.Column="0">
<Button
Name="lessBtn"
Width="30"
Height="30"
Click="LessBtn_Click">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="Height" Value="Auto" />
<Setter Property="Width" Value="Auto" />
<Setter Property="Content" Value="{x:Null}" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Border
Width="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Width}"
Height="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Height}"
Background="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Background}" />
<Path
Name="path"
Width="12"
Height="12"
Fill="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"
Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"
StrokeThickness="2">
<Path.Data>
M6,6 L12,0 L12,12 Z
</Path.Data>
</Path>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="path" Property="Stroke" Value="#FF3D9CAE" />
<Setter TargetName="path" Property="Fill" Value="#FF3D9CAE" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</DockPanel>
<Grid Grid.Column="1">
<StackPanel
HorizontalAlignment="Center"
VerticalAlignment="Center"
Orientation="Horizontal">
<Label x:Name="year" Content="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Year}" />
<Label Content="年" />
</StackPanel>
</Grid>
<DockPanel Grid.Column="2">
<Button
Name="addBtn"
Width="30"
Height="30"
Click="AddBtn_Click">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="Height" Value="Auto" />
<Setter Property="Width" Value="Auto" />
<Setter Property="Content" Value="{x:Null}" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Border
Width="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Width}"
Height="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Height}"
Background="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Background}" />
<Path
Name="path"
Width="12"
Height="12"
Fill="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"
Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"
StrokeThickness="2">
<Path.Data>
M0,0 L0,12 L6,6 Z
</Path.Data>
</Path>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="path" Property="Stroke" Value="#FF3D9CAE" />
<Setter TargetName="path" Property="Fill" Value="#FF3D9CAE" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</DockPanel>
</Grid>
</Border>
<Grid Grid.Row="1" Margin="3">
<WrapPanel
Name="buttonsHost"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<ToggleButton
Width="30"
Height="20"
Click="ToggleButton_Click"
Content="1月"
Style="{StaticResource ToggleButtonStyle.AnnualCalendar.Month}"
Tag="1">
<ToggleButton.IsChecked>
<MultiBinding Converter="{StaticResource monthCheckOrNoConverter}" Mode="OneWay">
<Binding Path="NowMonth" RelativeSource="{RelativeSource Mode=TemplatedParent}" />
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}" />
</MultiBinding>
</ToggleButton.IsChecked>
</ToggleButton>
<ToggleButton
Width="30"
Height="20"
Click="ToggleButton_Click"
Content="2月"
Style="{StaticResource ToggleButtonStyle.AnnualCalendar.Month}"
Tag="2">
<ToggleButton.IsChecked>
<MultiBinding Converter="{StaticResource monthCheckOrNoConverter}" Mode="OneWay">
<Binding Path="NowMonth" RelativeSource="{RelativeSource Mode=TemplatedParent}" />
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}" />
</MultiBinding>
</ToggleButton.IsChecked>
</ToggleButton>
<ToggleButton
Width="30"
Height="20"
Click="ToggleButton_Click"
Content="3月"
Style="{StaticResource ToggleButtonStyle.AnnualCalendar.Month}"
Tag="3">
<ToggleButton.IsChecked>
<MultiBinding Converter="{StaticResource monthCheckOrNoConverter}" Mode="OneWay">
<Binding Path="NowMonth" RelativeSource="{RelativeSource Mode=TemplatedParent}" />
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}" />
</MultiBinding>
</ToggleButton.IsChecked>
</ToggleButton>
<ToggleButton
Width="30"
Height="20"
Click="ToggleButton_Click"
Content="4月"
Style="{StaticResource ToggleButtonStyle.AnnualCalendar.Month}"
Tag="4">
<ToggleButton.IsChecked>
<MultiBinding Converter="{StaticResource monthCheckOrNoConverter}" Mode="OneWay">
<Binding Path="NowMonth" RelativeSource="{RelativeSource Mode=TemplatedParent}" />
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}" />
</MultiBinding>
</ToggleButton.IsChecked>
</ToggleButton>
<ToggleButton
Width="30"
Height="20"
Click="ToggleButton_Click"
Content="5月"
Style="{StaticResource ToggleButtonStyle.AnnualCalendar.Month}"
Tag="5">
<ToggleButton.IsChecked>
<MultiBinding Converter="{StaticResource monthCheckOrNoConverter}" Mode="OneWay">
<Binding Path="NowMonth" RelativeSource="{RelativeSource Mode=TemplatedParent}" />
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}" />
</MultiBinding>
</ToggleButton.IsChecked>
</ToggleButton>
<ToggleButton
Width="30"
Height="20"
Click="ToggleButton_Click"
Content="6月"
Style="{StaticResource ToggleButtonStyle.AnnualCalendar.Month}"
Tag="6">
<ToggleButton.IsChecked>
<MultiBinding Converter="{StaticResource monthCheckOrNoConverter}" Mode="OneWay">
<Binding Path="NowMonth" RelativeSource="{RelativeSource Mode=TemplatedParent}" />
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}" />
</MultiBinding>
</ToggleButton.IsChecked>
</ToggleButton>
<ToggleButton
Width="30"
Height="20"
Click="ToggleButton_Click"
Content="7月"
Style="{StaticResource ToggleButtonStyle.AnnualCalendar.Month}"
Tag="7">
<ToggleButton.IsChecked>
<MultiBinding Converter="{StaticResource monthCheckOrNoConverter}" Mode="OneWay">
<Binding Path="NowMonth" RelativeSource="{RelativeSource Mode=TemplatedParent}" />
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}" />
</MultiBinding>
</ToggleButton.IsChecked>
</ToggleButton>
<ToggleButton
Width="30"
Height="20"
Click="ToggleButton_Click"
Content="8月"
Style="{StaticResource ToggleButtonStyle.AnnualCalendar.Month}"
Tag="8">
<ToggleButton.IsChecked>
<MultiBinding Converter="{StaticResource monthCheckOrNoConverter}" Mode="OneWay">
<Binding Path="NowMonth" RelativeSource="{RelativeSource Mode=TemplatedParent}" />
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}" />
</MultiBinding>
</ToggleButton.IsChecked>
</ToggleButton>
<ToggleButton
Width="30"
Height="20"
Click="ToggleButton_Click"
Content="9月"
Style="{StaticResource ToggleButtonStyle.AnnualCalendar.Month}"
Tag="9">
<ToggleButton.IsChecked>
<MultiBinding Converter="{StaticResource monthCheckOrNoConverter}" Mode="OneWay">
<Binding Path="NowMonth" RelativeSource="{RelativeSource Mode=TemplatedParent}" />
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}" />
</MultiBinding>
</ToggleButton.IsChecked>
</ToggleButton>
<ToggleButton
Width="30"
Height="20"
Click="ToggleButton_Click"
Content="10月"
Style="{StaticResource ToggleButtonStyle.AnnualCalendar.Month}"
Tag="10">
<ToggleButton.IsChecked>
<MultiBinding Converter="{StaticResource monthCheckOrNoConverter}" Mode="OneWay">
<Binding Path="NowMonth" RelativeSource="{RelativeSource Mode=TemplatedParent}" />
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}" />
</MultiBinding>
</ToggleButton.IsChecked>
</ToggleButton>
<ToggleButton
Width="30"
Height="20"
Click="ToggleButton_Click"
Content="11月"
Style="{StaticResource ToggleButtonStyle.AnnualCalendar.Month}"
Tag="11">
<ToggleButton.IsChecked>
<MultiBinding Converter="{StaticResource monthCheckOrNoConverter}" Mode="OneWay">
<Binding Path="NowMonth" RelativeSource="{RelativeSource Mode=TemplatedParent}" />
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}" />
</MultiBinding>
</ToggleButton.IsChecked>
</ToggleButton>
<ToggleButton
Width="30"
Height="20"
Click="ToggleButton_Click"
Content="12月"
Style="{StaticResource ToggleButtonStyle.AnnualCalendar.Month}"
Tag="12">
<ToggleButton.IsChecked>
<MultiBinding Converter="{StaticResource monthCheckOrNoConverter}" Mode="OneWay">
<Binding Path="NowMonth" RelativeSource="{RelativeSource Mode=TemplatedParent}" />
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}" />
</MultiBinding>
</ToggleButton.IsChecked>
</ToggleButton>
</WrapPanel>
</Grid>
</Grid>
</Border>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<!-- 1.显示水印 -->
<DataTrigger Binding="{Binding ElementName=contentHost, Path=Text}" Value="">
<Setter TargetName="Message" Property="Visibility" Value="Visible" />
</DataTrigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="border" Property="BorderBrush" Value="{StaticResource Border.MouseOver}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
xaml.cs:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (NowYear == 0) { Year = DateTime.Now.Year; } else { Year = NowYear; }
if (NowYear != 0 && NowMonth != 0) { DateText = ToDateText(NowYear, NowMonth); }
}
#region dependency proeprties
public static readonly DependencyProperty NowMonthProperty = DependencyProperty.Register(
"NowMonth",
typeof(int),
typeof(AnnualCalendarTest),
new FrameworkPropertyMetadata(0)
);
public int NowMonth
{
get { return (int)GetValue(NowMonthProperty); }
set { SetValue(NowMonthProperty, value); }
}
public static readonly DependencyProperty NowYearProperty = DependencyProperty.Register(
"NowYear",
typeof(int),
typeof(AnnualCalendarTest),
new FrameworkPropertyMetadata(0)
);
public int NowYear
{
get { return (int)GetValue(NowYearProperty); }
set { SetValue(NowYearProperty, value); }
}
public static readonly DependencyProperty DateTextProperty = DependencyProperty.Register(
"DateText",
typeof(string),
typeof(AnnualCalendarTest),
new PropertyMetadata(null)
);
private static void DateTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Console.WriteLine("date text changed");
}
public string DateText
{
get { return (string)GetValue(DateTextProperty); }
set { SetValue(DateTextProperty, value); }
}
public static readonly DependencyProperty YearProperty = DependencyProperty.Register(
"Year",
typeof(int),
typeof(AnnualCalendarTest)
);
public int Year
{
get { return (int)GetValue(YearProperty); }
set { SetValue(YearProperty, value); }
}
#region WatermarkProperty 水印
/// <summary>
/// 水印
/// </summary>
public static readonly DependencyProperty WatermarkProperty = DependencyProperty.RegisterAttached(
"Watermark", typeof(string), typeof(AnnualCalendarTest), new FrameworkPropertyMetadata(""));
public static string GetWatermark(DependencyObject d)
{
return (string)d.GetValue(WatermarkProperty);
}
public static void SetWatermark(DependencyObject obj, string value)
{
obj.SetValue(WatermarkProperty, value);
}
#endregion
#endregion
private ToggleButton[] GetToggleButtons(DependencyObject visual)
{
if (visual is ToggleButton toggleButton)
{
// assume the toggle button not contains any toggle button
return new ToggleButton[] { toggleButton };
}
List<ToggleButton> toggleButtons = new List<ToggleButton>();
int childCount = VisualTreeHelper.GetChildrenCount(visual);
for (int i = 0; i < childCount; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(visual, i);
if (child == null) { continue; }
ToggleButton[] childToggleButtons = GetToggleButtons(child);
toggleButtons.AddRange(childToggleButtons);
}
return toggleButtons.ToArray();
}
private int _month = 1;
private void ToggleButton_Click(object sender, RoutedEventArgs e)
{
ToggleButton tg = (ToggleButton)sender;
ToggleButton[] buttons = GetToggleButtons(tg.Parent);
foreach (var button in buttons)
{
if (object.ReferenceEquals(button, tg))
{
button.IsChecked = true;
}
else
{
button.IsChecked = false;
}
}
_month = int.Parse(tg.Tag.ToString());
DateText = ToDateText(Year, _month);
IsDropDownOpen = false;
}
/// <summary>
/// add year
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void AddBtn_Click(object sender, RoutedEventArgs e)
{
Year += 1;
DateText = ToDateText(Year, _month);
}
/// <summary>
/// less year
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void LessBtn_Click(object sender, RoutedEventArgs e)
{
Year -= 1;
DateText = ToDateText(Year, _month);
}
private string ToDateText(int year, int month)
{
string monthText = month < 10 ? $"0{month}" : month.ToString();
return $"{year}.{monthText}";
}
实现效果如下:
最后附上源码链接https://download.csdn.net/download/weixin_39552347/12657757