自定义 DateTimePicker 的边框和按钮
Posted
技术标签:
【中文标题】自定义 DateTimePicker 的边框和按钮【英文标题】:Customizing Border and Button of the DateTimePicker 【发布时间】:2021-02-07 13:16:41 【问题描述】:目标是创建类似于this question 的屏幕截图的 DateTimePicker。
第一次尝试覆盖 OnPaint:
public class MyDateTimePicker : DateTimePicker
private Image _image;
public MyDateTimePicker() : base()
SetStyle(ControlStyles.UserPaint | ControlStyles.ResizeRedraw |
ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
[Browsable(true)]
public override Color BackColor
get
return base.BackColor;
set
base.BackColor = value;
[Category("Appearance")]
public Color BorderColor get; set; = Color.Black;
[Category("Appearance")]
public Color TextColor get; set; = Color.Black;
[Category("Appearance")]
public Image Image
get
return _image;
set
_image = value;
Invalidate();
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
// Fill the Background
e.Graphics.FillRectangle(new SolidBrush(this.BackColor), 0, 0, ClientRectangle.Width, ClientRectangle.Height);
// Draw DateTime text
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(TextColor), 5, 2);
// Draw Icon
if (_image != null)
Rectangle im_rect = new Rectangle(ClientRectangle.Width - 20, 2, ClientRectangle.Height - 4, ClientRectangle.Height - 4);
e.Graphics.DrawImage(_image, im_rect);
// Draw Border
e.Graphics.DrawRectangle(Pens.Black, new Rectangle(0, 0, ClientRectangle.Width - 1, ClientRectangle.Height - 1));
此解决方案存在以下问题:日期字段不可点击、使用箭头键更改日期时出现文本伪影、按钮的可点击区域变窄。
第二个解决方案覆盖 WndProc:
public class MyDateTimePicker : DateTimePicker
private const int WM_PAINT = 0x000F;
private Color _borderColor = Color.Black;
public MyDateTimePicker()
[Category("Appearance")]
public Color BorderColor
get return _borderColor;
set
if (_borderColor != value)
_borderColor = value;
this.Invalidate();
protected override void WndProc(ref Message m)
switch (m.Msg)
case WM_PAINT:
base.WndProc(ref m);
using (var g = Graphics.FromHwnd(m.HWnd))
var rect = new Rectangle(0, 0, this.ClientSize.Width - 1, this.ClientSize.Height - 1);
g.DrawRectangle(new Pen(this.BorderColor), rect);
m.Result = IntPtr.Zero;
break;
default:
base.WndProc(ref m);
break;
此解决方案缺少按钮的自定义。也许有人知道如何以这种方式自定义按钮,或者如何解决第一个解决方案的问题?
如果可能的话,我想更改 DateTimePicker 的高度以匹配 ComboBox 的高度(目前它们相差 1px)。
【问题讨论】:
参见此处:How can I set the DateTimePicker dropdown to select Years or Months only?OnDropDown()
覆盖,您可以在其中获得 MonthCalendar 控件的句柄和 ShowMonCalToday()
,您可以在其中使用发送消息自定义其样式。 -- 您可以使用其句柄将 MonthCalendar 附加到 NativeWindow 并覆盖 WndProc 以绘制其部分。 -- 关于按钮,您必须在绘制 DateTimePicker 的其他部分时绘制它。参见,例如,Reza Aghaei 的回答 here。
感谢@Jimi 的回复!您能否建议如何使用 WndProc 强制不绘制原始按钮?
我检查了这个:我想改变 DateTimePicker 的高度以匹配 ComboBox 的高度(目前它们相差 1px)。 → 我检查了这个。我没有看到任何区别,可能是因为使用 125% 作为显示的缩放比例,还使用 app.manifest 在我的应用程序中启用了 dpiaware。
顺便说一下,我在 GitHub 上上传了我的代码的扩展版本。随意克隆/下载并基于此自定义。
【参考方案1】:
您可以自己处理WM_PAINT并绘制边框和按钮。要获得下拉菜单的准确大小,请发送DTM_GETDATETIMEPICKERINFO 消息。
下拉按钮的宽度可能会根据控件的大小和控件文本所需的空间而有所不同:
平面日期时间选择器
using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class FlatDateTimePicker : DateTimePicker
public FlatDateTimePicker()
SetStyle(ControlStyles.ResizeRedraw |
ControlStyles.OptimizedDoubleBuffer, true);
private Color borderColor = Color.DeepSkyBlue;
[DefaultValue(typeof(Color), "RoyalBlue")]
public Color BorderColor
get return borderColor;
set
if (borderColor != value)
borderColor = value;
Invalidate();
protected override void WndProc(ref Message m)
base.WndProc(ref m);
if (m.Msg == WM_PAINT)
var info = new DATETIMEPICKERINFO();
info.cbSize = Marshal.SizeOf(info);
SendMessage(Handle, DTM_GETDATETIMEPICKERINFO, IntPtr.Zero, ref info);
using (var g = Graphics.FromHwndInternal(Handle))
var clientRect = new Rectangle(0,0,Width, Height);
var buttonWidth = info.rcButton.R - info.rcButton.L;
var dropDownRect = new Rectangle(info.rcButton.L, info.rcButton.T,
buttonWidth, clientRect.Height);
if (RightToLeft == RightToLeft.Yes && RightToLeftLayout == true)
dropDownRect.X = clientRect.Width - dropDownRect.Right;
dropDownRect.Width += 1;
var middle = new Point(dropDownRect.Left + dropDownRect.Width / 2,
dropDownRect.Top + dropDownRect.Height / 2);
var arrow = new Point[]
new Point(middle.X - 3, middle.Y - 2),
new Point(middle.X + 4, middle.Y - 2),
new Point(middle.X, middle.Y + 2)
;
var borderAndButtonColor = Enabled ? BorderColor : Color.LightGray;
var arrorColor = BackColor;
using (var pen = new Pen(borderAndButtonColor))
g.DrawRectangle(pen, 0, 0,
clientRect.Width - 1, clientRect.Height - 1);
using (var brush = new SolidBrush(borderAndButtonColor))
g.FillRectangle(brush, dropDownRect);
g.FillPolygon(Brushes.Black, arrow);
const int WM_PAINT = 0xF;
const int DTM_FIRST = 0x1000;
const int DTM_GETDATETIMEPICKERINFO = DTM_FIRST + 14;
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, int Msg,
IntPtr wParam, ref DATETIMEPICKERINFO info);
[StructLayout(LayoutKind.Sequential)]
struct RECT
public int L, T, R, B;
[StructLayout(LayoutKind.Sequential)]
struct DATETIMEPICKERINFO
public int cbSize;
public RECT rcCheck;
public int stateCheck;
public RECT rcButton;
public int stateButton;
public IntPtr hwndEdit;
public IntPtr hwndUD;
public IntPtr hwndDropDown;
克隆或下载扩展版
我已经创建了这个答案的扩展版本,它支持以平面样式呈现上下按钮和复选框,还突出显示鼠标移动时的箭头,如下所示:
您可以下载或关闭代码:
r-aghaei/FlatDateTimePickerExample master.zip相关帖子
您可能还想看看以下平面样式控件:
Flat TextBox - Change border color of TextBox Flat ComboBox - Change border color and dropdown button of ComboBox Flat NumericUpDown - Change border color and spin buttons of NumericUpDown【讨论】:
感谢您的代码有效!您还可以建议如何在不更改字体的情况下调整控件的高度吗?还有一个问题——如果我减小按钮宽度,下面会出现旧按钮,有什么办法可以避免这种情况吗? 没问题 :) 对于更改日历大小,这是我知道的唯一解决方案:Change month calendar size 我想改变的不是日历的高度,而是文本框的高度。设置 MinimumSize 就可以了。-- 有没有关于改变按钮宽度的解决方案? 我在这里没有看到任何关于按钮大小的消息:Date and Time Picker Messages以上是关于自定义 DateTimePicker 的边框和按钮的主要内容,如果未能解决你的问题,请参考以下文章