自定义DateTimePicker的边框和按钮 [英] Customizing Border and Button of the DateTimePicker

查看:119
本文介绍了自定义DateTimePicker的边框和按钮的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目标是创建类似于

 使用系统;使用System.ComponentModel;使用System.Drawing;使用System.Runtime.InteropServices;使用System.Windows.Forms;公共类FlatDateTimePicker:DateTimePicker{公共FlatDateTimePicker(){SetStyle(ControlStyles.ResizeRedraw |ControlStyles.OptimizedDoubleBuffer,true);}私人颜色borderColor = Color.DeepSkyBlue;[DefaultValue(typeof(Color),"RoyalBlue"))公共颜色BorderColor{得到{return borderColor;}放{如果(borderColor!=值){borderColor =值;Invalidate();}}}受保护的覆盖无效WndProc(参考消息m){base.WndProc(ref m);如果(m.Msg == WM_PAINT){var info = new DATETIMEPICKERINFO();info.cbSize = Marshal.SizeOf(info);SendMessage(句柄,DTM_GETDATETIMEPICKERINFO,IntPtr.Zero,引用信息);使用(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);如果(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 =新Point []{新点(中间X-3,中间Y-2),新点(中间X + 4,中间Y-2),新的Point(middle.X,middle.Y + 2)};var borderAndButtonColor =启用?BorderColor:Color.LightGray;var arrorColor = BackColor;使用(var pen = new Pen(borderAndButtonColor))g.DrawRectangle(pen,0,0,clientRect.Width-1,clientRect.Height-1);使用(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"))静态外部IntPtr SendMessage(IntPtr hWnd,int Msg,IntPtr wParam,参考DATETIMEPICKERINFO信息);[StructLayout(LayoutKind.Sequential)]结构RECT{public int L,T,R,B;}[StructLayout(LayoutKind.Sequential)]结构DATETIMEPICKERINFO{public int cbSize;公共RECT rcCheck;public int stateCheck;公共RECT rcButton;public int stateButton;公共IntPtr hwndEdit;公共IntPtr hwndUD;公共IntPtr hwndDropDown;}} 

克隆或下载扩展版本

我已经创建了此答案的扩展版本,该版本支持以平面样式呈现上下按钮和复选框,并在鼠标移动时突出显示箭头,如下所示:

您可以下载或关闭代码:

相关帖子

您可能还想看看以下平面样式控件:

Goal is to create DateTimePicker similar to the screen shot of this question.

First attempt overriding 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));
    }
} 

This solution has the following issues: date fields are not clickable, text artifacts when changing date with arrow keys, narrow clickable area of the button.

Second solution overriding 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;
        }
    }
}

This solution lacks the customization of the button. Maybe anyone knows how to customize button in this way, or how to solve issues of the first solution?

Also if it is possible I would like to change the height of DateTimePicker to match height of ComboBox (currently they differ by 1px).

解决方案

You can handle WM_PAINT and draw the border and button yourself. To get the accurate size of the dropdown, send DTM_GETDATETIMEPICKERINFO message.

The width of the dropdown button may vary depending to the size of the control and the space required by the text of the control:

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;
    }
}

Clone or Download Extended version

I have created an extended version of this answer, which supports rendering the up-down button and the checkbox in flat style, also highlighting the arrow on mouse move, something like this:

You can download or close the code:

Related Posts

You may also want to take a look at the following flat style controls:

这篇关于自定义DateTimePicker的边框和按钮的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆