Top / .NET備忘録 / 99.小ネタ / 17.色を変更できる DateTimePicker

ForeColor,BackColor を変更できるようにした DateTimePicker です。

(1) ビットマップを作成
(2) WM_PRINTCLIENT でビットマップに描画
(3) ビットマップを加工してコントロールに転送

という手順になります。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace VB6Control {

    public class VBDateTimePicker : DateTimePicker {

        // ForeColor,BackColor は非表示属性になっているので表示属性にする

        [Browsable(true)]
        [EditorBrowsable(EditorBrowsableState.Always)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public override Color BackColor { 
            get {
                return base.BackColor;
            }
            set {
                if (BackColor != value) {
                    base.BackColor = value;
                    if (IsHandleCreated) {
                        Invalidate();
                    }
                }
            }
        }

        [Browsable(true)]
        [EditorBrowsable(EditorBrowsableState.Always)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public override Color ForeColor {
            get {
                return base.ForeColor;
            }
            set {
                if (ForeColor != value) {
                    base.ForeColor = value;
                    if (IsHandleCreated) {
                        Invalidate();
                    }
                }
            }
        }

        private const int WM_PAINT = 0x000F;
        private const int WM_PRINTCLIENT = 0x0318;

        protected override void WndProc(ref Message m) {
            switch (m.Msg) {
                case WM_PAINT:
                    WMPaint(ref m);
                    break;

                case WM_PRINTCLIENT:
                    WMPrintClient(ref m);
                    break;

                default:
                    base.WndProc(ref m);
                    break;
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct PAINTSTRUCT {
            public IntPtr hdc;
            public bool fErase;
            public int rcPaint_left;
            public int rcPaint_top;
            public int rcPaint_right;
            public int rcPaint_bottom;
            public bool fRestore;
            public bool fIncUpdate;
            public int reserved1;
            public int reserved2;
            public int reserved3;
            public int reserved4;
            public int reserved5;
            public int reserved6;
            public int reserved7;
            public int reserved8;
        }

        [DllImport("user32.dll")]
        private static extern IntPtr BeginPaint(HandleRef hWnd, ref PAINTSTRUCT lpPaint);

        [DllImport("user32.dll")]
        private static extern bool EndPaint(HandleRef hWnd, ref PAINTSTRUCT lpPaint);

        private void WMPaint(ref Message m) {
            if (m.WParam == IntPtr.Zero) {
                HandleRef hwnd = new HandleRef(this, m.HWnd);
                PAINTSTRUCT ps = new PAINTSTRUCT();
                IntPtr dc = BeginPaint(hwnd, ref ps);
                HandleRef hdc = new HandleRef(this, dc);
                Rectangle clip = Rectangle.FromLTRB(ps.rcPaint_left, ps.rcPaint_top, ps.rcPaint_right, ps.rcPaint_bottom);
                try {
                    if (clip.Width > 0 && clip.Height > 0) {
                        // ダブルバッファ処理を行う
                        var bufferContext = BufferedGraphicsManager.Current;
                        using (var bufferedGraphics = bufferContext.Allocate(dc, clip)) {
                            using (Graphics g = bufferedGraphics.Graphics) {
                                DrawClientArea(g, clip);
                                bufferedGraphics.Render();
                            }
                        }
                    }
                } finally {
                    EndPaint(hwnd, ref ps);
                }
            } else {
                DrawClientArea(m.WParam);
            }

        }

        private void WMPrintClient(ref Message m) {
            DrawClientArea(m.WParam);
        }

        private void DrawClientArea(IntPtr dc) {
            var clip = this.ClientRectangle;
            if (clip.Width > 0 && clip.Height > 0) {
                using (var g = Graphics.FromHdc(dc)) {
                    DrawClientArea(g, clip);
                }
            }
        }

        private void DrawClientArea(Graphics g, Rectangle clip) {

            // 背景をクリア
            g.Clear(BackColor);

            // bitmap を作成
            using (var bmp = new Bitmap(ClientSize.Width, ClientSize.Height)) {

                // 作成した bitmap に描画してもらう
                using (var bmpGraphics = Graphics.FromImage(bmp)) {
                    bmpGraphics.Clear(SystemColors.Window);
                    var m = Message.Create(this.Handle, WM_PRINTCLIENT, bmpGraphics.GetHdc(), IntPtr.Zero);
                    base.WndProc(ref m);
                    bmpGraphics.ReleaseHdc();
                }

                // ボタンの描画
                Rectangle buttonBackGround = ButtonRectangle;
                g.DrawImage(bmp, buttonBackGround, buttonBackGround, GraphicsUnit.Pixel);

                // 背景色を透過
                bmp.MakeTransparent(SystemColors.Window);

                // ボタン領域を除いたリージョンを作成
                var region = new Region(clip);
                region.Exclude(buttonBackGround);
                var state = g.Save(); // リージョン適用前にステータスをバックアップ
                g.Clip = region;

                if (Enabled && !ForeColor.Equals(SystemColors.WindowText)) {
                    // 前景色が WindowText でなければ置換して描画
                    var lst = new List<ColorMap>();
                    lst.Add(new ColorMap() { OldColor = SystemColors.WindowText, NewColor = ForeColor });
                    var ia = new ImageAttributes();
                    ia.SetRemapTable(lst.ToArray());
                    g.DrawImage(bmp, ClientRectangle, 0, 0, ClientRectangle.Width, ClientRectangle.Height, GraphicsUnit.Pixel, ia);
                } else {
                    g.DrawImage(bmp, ClientRectangle);
                }

                // バックアップを戻す
                g.Restore(state); 
            }
        }

        // ボタンの領域を取得する

        private const int DTM_FIRST = 0x1000;
        private const int DTM_GETDATETIMEPICKERINFO = DTM_FIRST + 14;

        [StructLayout(LayoutKind.Sequential)]
        private struct RECT {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }

        [StructLayout(LayoutKind.Sequential)]
        private class 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;
            public DateTimePickerInfo() {
                this.cbSize = Marshal.SizeOf(this);
            }
        }

        [DllImport(ExternDll.User32, CharSet = CharSet.Auto)]
        private extern static IntPtr SendMessage(HandleRef hwndRef, int wMsg, IntPtr wParam, DateTimePickerInfo lParam);

        private Rectangle ButtonRectangle {
            get {
                var hwnd = new HandleRef(this, this.Handle);
                var info = new DateTimePickerInfo();
                var result = SendMessage(hwnd, DTM_GETDATETIMEPICKERINFO, IntPtr.Zero, info);
                var button = info.rcButton;
                return Rectangle.FromLTRB(button.Left, button.Top, button.Right, button.Bottom);
            }
        }
    }
}



トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   最終更新のRSS
Last-modified: 2018-11-09 (金) 17:57:12 (4d)