WPF自定义控件分享

1、自定义的DatePicker和Calendar

<!-- Calendar.xaml -->
<UserControl
    x:Class="Base.View.usercontrol.Calendar"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:Base.View.usercontrol"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    x:Name="root"
    Width="290"
    Height="auto"
    FontFamily="Segoe UI"
    mc:Ignorable="d">
    <UserControl.Resources>
        <!-- Header Button Style -->
        <Style x:Key="HeaderButtonStyle" TargetType="Button">
            <Setter Property="Background" Value="Transparent" />
            <Setter Property="BorderThickness" Value="0" />
            <Setter Property="Padding" Value="8" />
            <Setter Property="FontSize" Value="14" />
            <Setter Property="FontWeight" Value="SemiBold" />
            <Setter Property="Foreground" Value="#555" />
            <Setter Property="Cursor" Value="Hand" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border Background="{TemplateBinding Background}" CornerRadius="4">
                            <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="#F0F0F0" />
                </Trigger>
            </Style.Triggers>
        </Style>

        <!-- Day Button Style -->
        <Style x:Key="CalendarDayButtonStyle" TargetType="Button">
            <Setter Property="Background" Value="Transparent" />
            <Setter Property="BorderThickness" Value="0" />
            <Setter Property="Padding" Value="8" />
            <Setter Property="FontSize" Value="13" />
            <Setter Property="Cursor" Value="Hand" />
            <Setter Property="HorizontalContentAlignment" Value="Center" />
            <Setter Property="VerticalContentAlignment" Value="Center" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border
                            Name="border"
                            Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            CornerRadius="15">
                            <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <!-- Month Button Style -->
        <Style x:Key="CalendarMonthButtonStyle" TargetType="Button" BasedOn="{StaticResource HeaderButtonStyle}">
            <Setter Property="FontSize" Value="14"/>
            <Setter Property="FontWeight" Value="Normal"/>
            <Setter Property="Padding" Value="10"/>
        </Style>
        
    </UserControl.Resources>
    <Border Padding="10" Background="White">
        <Grid>
            <!-- Day View Grid -->
            <Grid x:Name="DayViewGrid">
                <StackPanel>
                    <!-- Header -->
                    <DockPanel Margin="0,0,0,12">
                        <Button
                            Click="PrevMonth_Click"
                            Content="&lt;"
                            DockPanel.Dock="Left"
                            Style="{StaticResource HeaderButtonStyle}" />
                        <Button
                            Click="NextMonth_Click"
                            Content="&gt;"
                            DockPanel.Dock="Right"
                            Style="{StaticResource HeaderButtonStyle}" />

                        <Button x:Name="MonthYearHeader"
                                Style="{StaticResource HeaderButtonStyle}"
                                HorizontalContentAlignment="Center"
                                Click="MonthYearHeader_Click">
                            <TextBlock
                                HorizontalAlignment="Center"
                                VerticalAlignment="Center"
                                FontSize="16"
                                FontWeight="SemiBold"
                                Foreground="#333"
                                Text="{Binding CurrentMonthText, ElementName=root}" />
                        </Button>

                    </DockPanel>

                    <!-- Week headers -->
                    <UniformGrid Margin="0,0,0,8" Columns="7">
                        <TextBlock FontWeight="SemiBold" Foreground="#888" Text="日" TextAlignment="Center" />
                        <TextBlock FontWeight="SemiBold" Foreground="#888" Text="一" TextAlignment="Center" />
                        <TextBlock FontWeight="SemiBold" Foreground="#888" Text="二" TextAlignment="Center" />
                        <TextBlock FontWeight="SemiBold" Foreground="#888" Text="三" TextAlignment="Center" />
                        <TextBlock FontWeight="SemiBold" Foreground="#888" Text="四" TextAlignment="Center" />
                        <TextBlock FontWeight="SemiBold" Foreground="#888" Text="五" TextAlignment="Center" />
                        <TextBlock FontWeight="SemiBold" Foreground="#888" Text="六" TextAlignment="Center" />
                    </UniformGrid>

                    <!-- Dates -->
                    <UniformGrid x:Name="DayGrid" Columns="7" Rows="6"/>
                </StackPanel>
            </Grid>
            
            <!-- Month View Grid -->
            <Grid x:Name="MonthViewGrid" Visibility="Collapsed">
                <StackPanel>
                    <!-- Header -->
                    <DockPanel Margin="0,0,0,12">
                        <Button
                            Click="PrevYear_Click"
                            Content="&lt;"
                            DockPanel.Dock="Left"
                            Style="{StaticResource HeaderButtonStyle}" />
                        <Button
                            Click="NextYear_Click"
                            Content="&gt;"
                            DockPanel.Dock="Right"
                            Style="{StaticResource HeaderButtonStyle}" />

                        <TextBlock x:Name="YearTextBlock"
                                   HorizontalAlignment="Center"
                                   VerticalAlignment="Center"
                                   FontSize="16"
                                   FontWeight="SemiBold"
                                   Foreground="#333" />

                    </DockPanel>
                    
                    <!-- Months -->
                    <UniformGrid x:Name="MonthsGrid" Columns="3" Rows="4"/>
                </StackPanel>
            </Grid>
        </Grid>
    </Border>
</UserControl>
//Calendar.xaml.cs
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.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Base.View.usercontrol
{
    /// <summary>
    /// Calendar.xaml 的交互逻辑
    /// </summary>
    public partial class Calendar : UserControl, System.ComponentModel.INotifyPropertyChanged
    {
        public static readonly RoutedEvent DateSelectedEvent = EventManager.RegisterRoutedEvent(
            name: "DateSelected",
            routingStrategy: RoutingStrategy.Bubble,
            handlerType: typeof(RoutedEventHandler),
            ownerType: typeof(Calendar));

        public event RoutedEventHandler DateSelected
        {
            add { AddHandler(DateSelectedEvent, value); }
            remove { RemoveHandler(DateSelectedEvent, value); }
        }

        public Calendar()
        {
            InitializeComponent();
            DisplayMonth(DateTime.Today);
        }
        public static readonly DependencyProperty SelectedDateProperty =
            DependencyProperty.Register("SelectedDate", typeof(DateTime?), typeof(Calendar),
                new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

        public DateTime? SelectedDate
        {
            get => (DateTime?)GetValue(SelectedDateProperty);
            set
            {
                SetValue(SelectedDateProperty, value);
                DisplayMonth(value ?? DateTime.Today);
            }
        }

        private DateTime currentMonth;
        private DateTime _currentViewDate;
        public string CurrentMonthText => currentMonth.ToString("yyyy 年 MM 月");

        private void DisplayMonth(DateTime date)
        {
            currentMonth = new DateTime(date.Year, date.Month, 1);
            DayGrid.Children.Clear();
            OnPropertyChanged(nameof(CurrentMonthText));

            int firstDayOfWeek = (int)currentMonth.DayOfWeek;
            DateTime startDate = currentMonth.AddDays(-firstDayOfWeek);

            for (int i = 0; i < 42; i++)
            {
                DateTime displayDate = startDate.AddDays(i);
                bool isCurrentMonth = displayDate.Month == currentMonth.Month;

                var btn = new Button
                {
                    Content = displayDate.Day.ToString(),
                    Margin = new Thickness(2),
                    Tag = displayDate,
                    Style = (Style)FindResource("CalendarDayButtonStyle")
                };

                if (!isCurrentMonth)
                {
                    btn.Foreground = Brushes.LightGray;
                }
                
                if (displayDate.Date == DateTime.Today.Date && isCurrentMonth)
                {
                    btn.BorderBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#0078D7"));
                    btn.BorderThickness = new Thickness(1);
                }

                if (SelectedDate.HasValue && SelectedDate.Value.Date == displayDate.Date)
                {
                    btn.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#0078D7"));
                    btn.Foreground = Brushes.White;
                }

                btn.MouseEnter += (s, e) =>
                {
                    if (SelectedDate.HasValue && SelectedDate.Value.Date == displayDate.Date) return;
                    ((Button)s).Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F0F0F0"));
                };

                btn.MouseLeave += (s, e) =>
                {
                    if (SelectedDate.HasValue && SelectedDate.Value.Date == displayDate.Date) return;
                    ((Button)s).Background = Brushes.Transparent;
                };

                btn.Click += (s, e) =>
                {
                    var clickedDate = (DateTime)((Button)s).Tag;
                    if (clickedDate.Month == currentMonth.Month)
                    {
                        SelectedDate = clickedDate;
                        RaiseEvent(new RoutedEventArgs(DateSelectedEvent, this));
                    }
                    else
                    {
                        DisplayMonth(clickedDate);
                    }
                };

                DayGrid.Children.Add(btn);
            }
        }

        private void DisplayMonthView()
        {
            DayViewGrid.Visibility = Visibility.Collapsed;
            MonthViewGrid.Visibility = Visibility.Visible;
            YearTextBlock.Text = _currentViewDate.Year.ToString();
            MonthsGrid.Children.Clear();
            
            for (int i = 1; i <= 12; i++)
            {
                var btn = new Button
                {
                    Content = $"{i}月",
                    Tag = i,
                    Style = (Style)FindResource("CalendarMonthButtonStyle")
                };

                if (i == currentMonth.Month && _currentViewDate.Year == currentMonth.Year)
                {
                    btn.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#0078D7"));
                    btn.Foreground = Brushes.White;
                }

                btn.Click += (s, e) =>
                {
                    var selectedMonth = (int)((Button)s).Tag;
                    DisplayMonth(new DateTime(_currentViewDate.Year, selectedMonth, 1));
                    MonthViewGrid.Visibility = Visibility.Collapsed;
                    DayViewGrid.Visibility = Visibility.Visible;
                };
                MonthsGrid.Children.Add(btn);
            }
        }


        private void PrevMonth_Click(object sender, RoutedEventArgs e)
        {
            DisplayMonth(currentMonth.AddMonths(-1));
        }

        private void NextMonth_Click(object sender, RoutedEventArgs e)
        {
            DisplayMonth(currentMonth.AddMonths(1));
        }

        private void MonthYearHeader_Click(object sender, RoutedEventArgs e)
        {
            _currentViewDate = new DateTime(currentMonth.Year, currentMonth.Month, 1);
            DisplayMonthView();
        }

        private void PrevYear_Click(object sender, RoutedEventArgs e)
        {
            _currentViewDate = _currentViewDate.AddYears(-1);
            DisplayMonthView();
        }

        private void NextYear_Click(object sender, RoutedEventArgs e)
        {
            _currentViewDate = _currentViewDate.AddYears(1);
            DisplayMonthView();
        }

        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string name)
        {
            PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(name));
        }
    }
}
<!-- DatePicker.xaml -->
<UserControl
    x:Class="Base.View.usercontrol.DatePicker"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:converter="clr-namespace:Base.Converter"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:Base.View.usercontrol"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    x:Name="root"
    Width="{Binding ControlWidth, RelativeSource={RelativeSource Self}}"
    Height="{Binding ControlHeight, RelativeSource={RelativeSource Self}}"
    d:DesignHeight="200"
    d:DesignWidth="200"
    mc:Ignorable="d">
    <UserControl.Resources>
        <BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter" />
        <converter:BoolToCollapsedVisibilityConverter x:Key="BoolToCollapsedVisibilityConverter" />
        <converter:ClearButtonVisibilityConverter x:Key="ClearButtonVisibilityConverter" />
        <Style x:Key="TransparentButtonStyle" TargetType="Button">
            <Setter Property="Background" Value="Transparent" />
            <Setter Property="BorderBrush" Value="Transparent" />
            <Setter Property="BorderThickness" Value="0" />
            <Setter Property="Padding" Value="0" />
            <Setter Property="Cursor" Value="Hand" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <ContentPresenter />
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </UserControl.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="auto" />
            <ColumnDefinition Width="auto" />
        </Grid.ColumnDefinitions>

        <!--  可选显示的文本框  -->
        <TextBox
            x:Name="DateTextBox"
            Grid.Column="0"
            Margin="0"
            VerticalContentAlignment="Center"
            IsReadOnly="True"
            Text="{Binding SelectedDate, ElementName=root, StringFormat='yyyy-MM-dd'}"
            Visibility="{Binding ShowTextBox, ElementName=root, Converter={StaticResource BoolToCollapsedVisibilityConverter}}" />

        <!--  Clear Button  -->
        <Button
            Grid.Column="1"
            Click="ClearDate_Click"
            Style="{StaticResource TransparentButtonStyle}"
            VerticalAlignment="Center" Margin="2,0">
            <Button.Visibility>
                <MultiBinding Converter="{StaticResource ClearButtonVisibilityConverter}">
                    <Binding Path="ShowClearButton" ElementName="root" />
                    <Binding Path="SelectedDate" ElementName="root" />
                </MultiBinding>
            </Button.Visibility>
            <Path
                Width="10"
                Height="10"
                Data="M0,0 L1,1 M0,1 L1,0"
                Stroke="Gray"
                StrokeThickness="1.5" />
        </Button>


        <!--  图标按钮  -->
        <Button
            Grid.Column="2"
            Click="ToggleCalendar_Click"
            Style="{StaticResource TransparentButtonStyle}">
            <Image
                Width="{Binding IconWidth, ElementName=root}"
                Height="{Binding IconHeight, ElementName=root}"
                RenderOptions.BitmapScalingMode="HighQuality"
                SnapsToDevicePixels="True"
                Source="{Binding IconSource, ElementName=root}" />
        </Button>

        <!--  弹出的Calendar  -->
        <Popup
            x:Name="CalendarPopup"
            AllowsTransparency="True"
            Placement="Bottom"
            PlacementTarget="{Binding ElementName=root}"
            StaysOpen="False">
            <Border
                Padding="8"
                Background="White"
                BorderBrush="#E0E0E0"
                BorderThickness="1"
                CornerRadius="5">
                <Border.Effect>
                    <DropShadowEffect
                        BlurRadius="15"
                        Direction="270"
                        Opacity="0.1"
                        ShadowDepth="2"
                        Color="Black" />
                </Border.Effect>
                <local:Calendar DateSelected="Calendar_DateSelected" SelectedDate="{Binding SelectedDate, ElementName=root, Mode=TwoWay}" />
            </Border>
        </Popup>
    </Grid>
</UserControl>
//DatePicker.xaml.cs
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.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Base.View.usercontrol
{
    /// <summary>
    /// DatePicker.xaml 的交互逻辑
    /// </summary>
    public partial class DatePicker : UserControl
    {
        public DatePicker()
        {
            InitializeComponent();
        }
        public static readonly DependencyProperty SelectedDateProperty =
    DependencyProperty.Register("SelectedDate", typeof(DateTime?), typeof(DatePicker),
        new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

        public DateTime? SelectedDate
        {
            get => (DateTime?)GetValue(SelectedDateProperty);
            set => SetValue(SelectedDateProperty, value);
        }

        public static readonly DependencyProperty ShowTextBoxProperty =
            DependencyProperty.Register("ShowTextBox", typeof(bool), typeof(DatePicker), new PropertyMetadata(true));

        public bool ShowTextBox
        {
            get => (bool)GetValue(ShowTextBoxProperty);
            set => SetValue(ShowTextBoxProperty, value);
        }

        public static readonly DependencyProperty IconSourceProperty =
            DependencyProperty.Register("IconSource", typeof(ImageSource), typeof(DatePicker), new PropertyMetadata(new BitmapImage(new Uri("pack://application:,,,/Base;component/resources/calendar.png"))));

        public ImageSource IconSource
        {
            get => (ImageSource)GetValue(IconSourceProperty);
            set => SetValue(IconSourceProperty, value);
        }

        // 控件默认宽度
        public static readonly DependencyProperty ControlWidthProperty =
            DependencyProperty.Register("ControlWidth", typeof(double), typeof(DatePicker), new PropertyMetadata(200.0));

        public double ControlWidth
        {
            get => (double)GetValue(ControlWidthProperty);
            set => SetValue(ControlWidthProperty, value);
        }

        // 控件默认高度
        public static readonly DependencyProperty ControlHeightProperty =
            DependencyProperty.Register("ControlHeight", typeof(double), typeof(DatePicker), new PropertyMetadata(30.0));

        // 图标宽度
        public static readonly DependencyProperty IconWidthProperty =
            DependencyProperty.Register("IconWidth", typeof(double), typeof(DatePicker), new PropertyMetadata(16.0));

        public double IconWidth
        {
            get => (double)GetValue(IconWidthProperty);
            set => SetValue(IconWidthProperty, value);
        }

        // 图标高度
        public static readonly DependencyProperty IconHeightProperty =
            DependencyProperty.Register("IconHeight", typeof(double), typeof(DatePicker), new PropertyMetadata(16.0));

        public double IconHeight
        {
            get => (double)GetValue(IconHeightProperty);
            set => SetValue(IconHeightProperty, value);
        }

        public static readonly DependencyProperty CloseOnSelectProperty =
            DependencyProperty.Register("CloseOnSelect", typeof(bool), typeof(DatePicker), new PropertyMetadata(true));

        public bool CloseOnSelect
        {
            get { return (bool)GetValue(CloseOnSelectProperty); }
            set { SetValue(CloseOnSelectProperty, value); }
        }

        public static readonly DependencyProperty ShowClearButtonProperty =
            DependencyProperty.Register("ShowClearButton", typeof(bool), typeof(DatePicker), new PropertyMetadata(false));

        public bool ShowClearButton
        {
            get { return (bool)GetValue(ShowClearButtonProperty); }
            set { SetValue(ShowClearButtonProperty, value); }
        }

        private void ToggleCalendar_Click(object sender, RoutedEventArgs e)
        {
            CalendarPopup.IsOpen = !CalendarPopup.IsOpen;
        }

        private void Calendar_DateSelected(object sender, RoutedEventArgs e)
        {
            if (CloseOnSelect)
            {
                CalendarPopup.IsOpen = false;
            }
        }

        private void ClearDate_Click(object sender, RoutedEventArgs e)
        {
            SelectedDate = null;
        }
    }
}

//ClearButtonVisibilityConverter.cs
using System;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Data;

namespace Base.Converter
{
    public class ClearButtonVisibilityConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (values.Length < 2)
                return Visibility.Collapsed;

            var showClearButton = values[0] as bool?;
            var selectedDate = values[1];

            if (showClearButton == true && selectedDate != null)
            {
                return Visibility.Visible;
            }

            return Visibility.Collapsed;
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
} 
//BoolToCollapsedVisibilityConverter.cs
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;

namespace Base.Converter
{
    public class BoolToCollapsedVisibilityConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return (value is bool flag && flag) ? Visibility.Visible : Visibility.Collapsed;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return (value is Visibility v && v == Visibility.Visible);
        }
    }

}

使用方法

    xmlns:base="clr-namespace:Base.View.usercontrol;assembly=Base"
...
                <base:DatePicker
                    CloseOnSelect="True" //选中后关闭窗口
                    ControlWidth="180" //整个控件的宽度
                    IconHeight="20" //图标高度
                    IconSource="/Resources/Calendar.png" //图标源URI
                    IconWidth="20" //图标宽度
                    SelectedDate="{Binding Date}" //选择的日期
                    ShowClearButton="True" //清空按钮是否显示(逻辑没做)
                    ShowTextBox="True" /> //显示文本框

显示效果如图

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇