using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Text; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace HotelPms.Share.Windows.Util { public partial class FormBase : Form { #region API [DllImport("user32.dll")] public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam); [DllImport("user32.dll")] public static extern bool ReleaseCapture(); [DllImport("user32.dll")] public static extern int TrackPopupMenuEx(IntPtr hmenu, uint fuFlags, int x, int y, IntPtr hwnd, IntPtr lptpm); [DllImport("user32.dll")] public static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert); [DllImport("user32.dll")] public static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags); [DllImport("User32.dll", CharSet = CharSet.Auto)] public static extern bool GetMonitorInfo(HandleRef hmonitor, [In, Out] MONITORINFOEX info); public const int WM_NCLBUTTONDOWN = 0xA1; public const int HT_CAPTION = 0x2; public const int WM_MOUSEMOVE = 0x0200; public const int WM_LBUTTONDOWN = 0x0201; public const int WM_LBUTTONUP = 0x0202; public const int WM_LBUTTONDBLCLK = 0x0203; public const int WM_RBUTTONDOWN = 0x0204; private const int HTBOTTOMLEFT = 16; private const int HTBOTTOMRIGHT = 17; private const int HTLEFT = 10; private const int HTRIGHT = 11; private const int HTBOTTOM = 15; private const int HTTOP = 12; private const int HTTOPLEFT = 13; private const int HTTOPRIGHT = 14; private const int BORDER_WIDTH = 7; private const int WMSZ_TOP = 3; private const int WMSZ_TOPLEFT = 4; private const int WMSZ_TOPRIGHT = 5; private const int WMSZ_LEFT = 1; private const int WMSZ_RIGHT = 2; private const int WMSZ_BOTTOM = 6; private const int WMSZ_BOTTOMLEFT = 7; private const int WMSZ_BOTTOMRIGHT = 8; private readonly Dictionary _resizingLocationsToCmd = new Dictionary { {HTTOP, WMSZ_TOP}, {HTTOPLEFT, WMSZ_TOPLEFT}, {HTTOPRIGHT, WMSZ_TOPRIGHT}, {HTLEFT, WMSZ_LEFT}, {HTRIGHT, WMSZ_RIGHT}, {HTBOTTOM, WMSZ_BOTTOM}, {HTBOTTOMLEFT, WMSZ_BOTTOMLEFT}, {HTBOTTOMRIGHT, WMSZ_BOTTOMRIGHT} }; private const uint TPM_LEFTALIGN = 0x0000; private const uint TPM_RETURNCMD = 0x0100; private const int WM_SYSCOMMAND = 0x0112; private const int WS_MINIMIZEBOX = 0x20000; private const int WS_SYSMENU = 0x00080000; private const int MONITOR_DEFAULTTONEAREST = 2; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)] public class MONITORINFOEX { public int cbSize = Marshal.SizeOf(typeof(MONITORINFOEX)); public RECT rcMonitor = new RECT(); public RECT rcWork = new RECT(); public int dwFlags = 0; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] public char[] szDevice = new char[32]; } [StructLayout(LayoutKind.Sequential)] public struct RECT { public int left; public int top; public int right; public int bottom; public int Width() { return right - left; } public int Height() { return bottom - top; } } #endregion private enum ResizeDirection { BottomLeft, Left, Right, BottomRight, Bottom, None } private enum ButtonState { XOver, MaxOver, MinOver, HelpOver, XDown, MaxDown, MinDown, HelpDown, None } private readonly Cursor[] _resizeCursors = { Cursors.SizeNESW, Cursors.SizeWE, Cursors.SizeNWSE, Cursors.SizeWE, Cursors.SizeNS }; private ResizeDirection _resizeDir; private ButtonState _buttonState = ButtonState.None; private Rectangle _helpButtonBounds; private Rectangle _minButtonBounds; private Rectangle _maxButtonBounds; private Rectangle _xButtonBounds; private Rectangle _statusBarBounds; private bool _maximized; private Size _previousSize; private Point _previousLocation; private bool _headerMouseDown; private Font helpFont = new Font(FontFamily.GenericSansSerif, 16F); private StringFormat helpFormat = new StringFormat { LineAlignment = StringAlignment.Center, Alignment = StringAlignment.Center, FormatFlags = StringFormatFlags.NoWrap, }; private StringFormat titleFormat = new StringFormat { LineAlignment = StringAlignment.Center, FormatFlags = StringFormatFlags.NoWrap, }; private bool showHelpIcon = false; public bool ShowHelpIcon { get { return showHelpIcon; } set { showHelpIcon = value; Invalidate(); } } public bool Sizable { get; set; } = true; /// /// フォームの背景色 /// public Color BackgroundColor { get; set; } = Color.FromArgb(255, 43, 43, 43); /// /// タイトルバーの背景色 /// public Color TitleBarColor { get; set; } = Color.FromArgb(255, 60, 63, 65); /// /// 枠線色 /// public Color BorderColor { get; set; } = Color.FromArgb(31, 0, 0, 0); /// /// タイトルバーのマウスHover色 /// public Color TitleBarHoverColor { get; set; } = Color.FromArgb(255, 79, 82, 84); /// /// タイトルバーのマウスDown色 /// public Color TitleBarDownColor { get; set; } = Color.FromArgb(255, 79, 82, 84); public int TitlePadding { get; set; } = 14; private int titleBarHeight = 36; /// /// タイトルバーの高さ /// public int TitleBarHeight { get { return titleBarHeight; } set { titleBarHeight = value; Invalidate(); } } public event PaintEventHandler DrawTitle; public event MouseEventHandler HelpClick; public FormBase() { FormBorderStyle = FormBorderStyle.None; DoubleBuffered = true; SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.UserPaint, true); // This enables the form to trigger the MouseMove event even when mouse is over another control Application.AddMessageFilter(new MouseMessageFilter()); MouseMessageFilter.MouseMove += OnGlobalMouseMove; InitializeComponent(); } protected override void OnKeyDown(KeyEventArgs e) { if (e.Alt && e.KeyCode == Keys.F4 && LoadingObj.Visible) { e.Handled = true; return; } base.OnKeyDown(e); } protected void OnGlobalMouseMove(object sender, MouseEventArgs e) { if (IsDisposed) return; // Convert to client position and pass to Form.MouseMove var clientCursorPos = PointToClient(e.Location); var newE = new MouseEventArgs(MouseButtons.None, 0, clientCursorPos.X, clientCursorPos.Y, 0); OnMouseMove(newE); } protected override void WndProc(ref Message m) { base.WndProc(ref m); if (DesignMode || IsDisposed) return; if (m.Msg == WM_LBUTTONDBLCLK) { MaximizeWindow(!_maximized); } else if (m.Msg == WM_MOUSEMOVE && _maximized && _statusBarBounds.Contains(PointToClient(Cursor.Position)) && !(_minButtonBounds.Contains(PointToClient(Cursor.Position)) || _maxButtonBounds.Contains(PointToClient(Cursor.Position)) || _xButtonBounds.Contains(PointToClient(Cursor.Position)))) { if (_headerMouseDown) { _maximized = false; _headerMouseDown = false; var mousePoint = PointToClient(Cursor.Position); if (mousePoint.X < Width / 2) Location = mousePoint.X < _previousSize.Width / 2 ? new Point(Cursor.Position.X - mousePoint.X, Cursor.Position.Y - mousePoint.Y) : new Point(Cursor.Position.X - _previousSize.Width / 2, Cursor.Position.Y - mousePoint.Y); else Location = Width - mousePoint.X < _previousSize.Width / 2 ? new Point(Cursor.Position.X - _previousSize.Width + Width - mousePoint.X, Cursor.Position.Y - mousePoint.Y) : new Point(Cursor.Position.X - _previousSize.Width / 2, Cursor.Position.Y - mousePoint.Y); Size = _previousSize; ReleaseCapture(); SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0); } } else if (m.Msg == WM_LBUTTONDOWN && _statusBarBounds.Contains(PointToClient(Cursor.Position)) && !(_minButtonBounds.Contains(PointToClient(Cursor.Position)) || _maxButtonBounds.Contains(PointToClient(Cursor.Position)) || _xButtonBounds.Contains(PointToClient(Cursor.Position)))) { if (!_maximized) { ReleaseCapture(); SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0); } else { _headerMouseDown = true; } } else if (m.Msg == WM_RBUTTONDOWN) { Point cursorPos = PointToClient(Cursor.Position); if (_statusBarBounds.Contains(cursorPos) && !_minButtonBounds.Contains(cursorPos) && !_maxButtonBounds.Contains(cursorPos) && !_xButtonBounds.Contains(cursorPos)) { // Show default system menu when right clicking titlebar var id = TrackPopupMenuEx(GetSystemMenu(Handle, false), TPM_LEFTALIGN | TPM_RETURNCMD, Cursor.Position.X, Cursor.Position.Y, Handle, IntPtr.Zero); // Pass the command as a WM_SYSCOMMAND message SendMessage(Handle, WM_SYSCOMMAND, id, 0); } } else if (m.Msg == WM_NCLBUTTONDOWN) { // This re-enables resizing by letting the application know when the // user is trying to resize a side. This is disabled by default when using WS_SYSMENU. if (!Sizable) return; byte bFlag = 0; // Get which side to resize from if (_resizingLocationsToCmd.ContainsKey((int)m.WParam)) bFlag = (byte)_resizingLocationsToCmd[(int)m.WParam]; if (bFlag != 0) SendMessage(Handle, WM_SYSCOMMAND, 0xF000 | bFlag, (int)m.LParam); } else if (m.Msg == WM_LBUTTONUP) { _headerMouseDown = false; } } protected override CreateParams CreateParams { get { var par = base.CreateParams; // WS_SYSMENU: Trigger the creation of the system menu // WS_MINIMIZEBOX: Allow minimizing from taskbar par.Style = par.Style | WS_MINIMIZEBOX | WS_SYSMENU; // Turn on the WS_MINIMIZEBOX style flag //par.ExStyle |= 0x02000000; // 用双缓冲绘制窗口的所有子控件 return par; } } private void MaximizeWindow(bool maximize) { if (!MaximizeBox || !ControlBox) return; _maximized = maximize; if (maximize) { var monitorHandle = MonitorFromWindow(Handle, MONITOR_DEFAULTTONEAREST); var monitorInfo = new MONITORINFOEX(); GetMonitorInfo(new HandleRef(null, monitorHandle), monitorInfo); _previousSize = Size; _previousLocation = Location; Size = new Size(monitorInfo.rcWork.Width(), monitorInfo.rcWork.Height()); Location = new Point(monitorInfo.rcWork.left, monitorInfo.rcWork.top); } else { Size = _previousSize; Location = _previousLocation; } } /// /// 全部ここに初期化が必要!!! /// /// protected override void OnPaint(PaintEventArgs e) { Padding = new Padding(1, TitleBarHeight, 1, 1); _helpButtonBounds = new Rectangle((Width - TitlePadding / 2) - 4 * TitleBarHeight, 0, TitleBarHeight, TitleBarHeight); _minButtonBounds = new Rectangle((Width - TitlePadding / 2) - 3 * TitleBarHeight, 0, TitleBarHeight, TitleBarHeight); _maxButtonBounds = new Rectangle((Width - TitlePadding / 2) - 2 * TitleBarHeight, 0, TitleBarHeight, TitleBarHeight); _xButtonBounds = new Rectangle((Width - TitlePadding / 2) - TitleBarHeight, 0, TitleBarHeight, TitleBarHeight); _statusBarBounds = new Rectangle(0, 0, Width, TitleBarHeight); var g = e.Graphics; //g.TextRenderingHint = TextRenderingHint.AntiAlias; g.Clear(BackgroundColor); using (var titleBarBrush = new SolidBrush(TitleBarColor)) { g.FillRectangle(titleBarBrush, _statusBarBounds); } using (var borderPen = new Pen(BorderColor, 1)) { g.DrawLine(borderPen, new Point(0, Height - 1), new Point(Width - 1, Height - 1)); //Draw border } // Determine whether or not we even should be drawing the buttons. bool showMin = MinimizeBox && ControlBox; bool showMax = MaximizeBox && ControlBox; // When MaximizeButton == false, the minimize button will be painted in its place using (var hoverBrush = new SolidBrush(TitleBarHoverColor)) { if (_buttonState == ButtonState.MinOver && showMin) { g.FillRectangle(hoverBrush, showMax ? _minButtonBounds : _maxButtonBounds); } if (_buttonState == ButtonState.MaxOver && showMax) { g.FillRectangle(hoverBrush, _maxButtonBounds); } if (_buttonState == ButtonState.HelpOver && showHelpIcon) { g.FillRectangle(hoverBrush, _helpButtonBounds); } } using (var downBrush = new SolidBrush(TitleBarDownColor)) { if (_buttonState == ButtonState.MinDown && showMin) { g.FillRectangle(downBrush, showMax ? _minButtonBounds : _maxButtonBounds); } if (_buttonState == ButtonState.MaxDown && showMax) { g.FillRectangle(downBrush, _maxButtonBounds); } if (_buttonState == ButtonState.HelpDown && showHelpIcon) { g.FillRectangle(downBrush, _helpButtonBounds); } } if (_buttonState == ButtonState.XOver && ControlBox) { using (var hoverBrushX = new SolidBrush(Color.FromArgb(255, 232, 17, 35))) { g.FillRectangle(hoverBrushX, _xButtonBounds); } } if (_buttonState == ButtonState.XDown && ControlBox) { using (var downBrushX = new SolidBrush(Color.FromArgb(255, 232, 17, 35))) { g.FillRectangle(downBrushX, _xButtonBounds); } } using (var buttonPen = new Pen(Color.White, 2)) { // Minimize button. if (showMin) { int x = showMax ? _minButtonBounds.X : _maxButtonBounds.X; int y = showMax ? _minButtonBounds.Y : _maxButtonBounds.Y; g.DrawLine( buttonPen, x + (int)(_minButtonBounds.Width * 0.33), y + (int)(_minButtonBounds.Height * 0.66), x + (int)(_minButtonBounds.Width * 0.66), y + (int)(_minButtonBounds.Height * 0.66) ); } // Maximize button if (showMax) { g.DrawRectangle( buttonPen, _maxButtonBounds.X + (int)(_maxButtonBounds.Width * 0.33), _maxButtonBounds.Y + (int)(_maxButtonBounds.Height * 0.36), (int)(_maxButtonBounds.Width * 0.39), (int)(_maxButtonBounds.Height * 0.31) ); } // Close button if (ControlBox) { g.DrawLine( buttonPen, _xButtonBounds.X + (int)(_xButtonBounds.Width * 0.33), _xButtonBounds.Y + (int)(_xButtonBounds.Height * 0.33), _xButtonBounds.X + (int)(_xButtonBounds.Width * 0.66), _xButtonBounds.Y + (int)(_xButtonBounds.Height * 0.66) ); g.DrawLine( buttonPen, _xButtonBounds.X + (int)(_xButtonBounds.Width * 0.66), _xButtonBounds.Y + (int)(_xButtonBounds.Height * 0.33), _xButtonBounds.X + (int)(_xButtonBounds.Width * 0.33), _xButtonBounds.Y + (int)(_xButtonBounds.Height * 0.66)); } } if (showHelpIcon) { g.DrawString("?", helpFont, Brushes.White, _helpButtonBounds, helpFormat); } //Form title if (DrawTitle != null) { DrawTitle(this, e); } else { g.DrawString(Text, this.Font, Brushes.White, new Rectangle(TitlePadding, 0, Width, TitleBarHeight), titleFormat); } } protected override void OnMouseUp(MouseEventArgs e) { if (DesignMode) return; UpdateButtons(e, true); base.OnMouseUp(e); ReleaseCapture(); } protected override void OnMouseDown(MouseEventArgs e) { if (DesignMode) return; UpdateButtons(e); if (e.Button == MouseButtons.Left && !_maximized) ResizeForm(_resizeDir); base.OnMouseDown(e); } protected override void OnMouseLeave(EventArgs e) { base.OnMouseLeave(e); if (DesignMode) return; _buttonState = ButtonState.None; Invalidate(); } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); if (DesignMode) return; if (Sizable) { //True if the mouse is hovering over a child control var isChildUnderMouse = GetChildAtPoint(e.Location) != null; if (e.Location.X < BORDER_WIDTH && e.Location.Y > Height - BORDER_WIDTH && !isChildUnderMouse && !_maximized) { _resizeDir = ResizeDirection.BottomLeft; Cursor = Cursors.SizeNESW; } else if (e.Location.X < BORDER_WIDTH && !isChildUnderMouse && !_maximized) { _resizeDir = ResizeDirection.Left; Cursor = Cursors.SizeWE; } else if (e.Location.X > Width - BORDER_WIDTH && e.Location.Y > Height - BORDER_WIDTH && !isChildUnderMouse && !_maximized) { _resizeDir = ResizeDirection.BottomRight; Cursor = Cursors.SizeNWSE; } else if (e.Location.X > Width - BORDER_WIDTH && !isChildUnderMouse && !_maximized) { _resizeDir = ResizeDirection.Right; Cursor = Cursors.SizeWE; } else if (e.Location.Y > Height - BORDER_WIDTH && !isChildUnderMouse && !_maximized) { _resizeDir = ResizeDirection.Bottom; Cursor = Cursors.SizeNS; } else { _resizeDir = ResizeDirection.None; //Only reset the cursor when needed, this prevents it from flickering when a child control changes the cursor to its own needs if (_resizeCursors.Contains(Cursor)) { Cursor = Cursors.Default; } } } UpdateButtons(e); } private void UpdateButtons(MouseEventArgs e, bool up = false) { if (DesignMode) return; var oldState = _buttonState; bool showMin = MinimizeBox && ControlBox; bool showMax = MaximizeBox && ControlBox; if (e.Button == MouseButtons.Left && !up) { if (showMin && !showMax && _maxButtonBounds.Contains(e.Location)) { _buttonState = ButtonState.MinDown; } else if (showMin && showMax && _minButtonBounds.Contains(e.Location)) { _buttonState = ButtonState.MinDown; } else if (showMax && _maxButtonBounds.Contains(e.Location)) { _buttonState = ButtonState.MaxDown; } else if (ControlBox && _xButtonBounds.Contains(e.Location)) { _buttonState = ButtonState.XDown; } else if (showHelpIcon && _helpButtonBounds.Contains(e.Location)) { _buttonState = ButtonState.HelpDown; } else { _buttonState = ButtonState.None; } } else { if (showMin && !showMax && _maxButtonBounds.Contains(e.Location)) { _buttonState = ButtonState.MinOver; if (oldState == ButtonState.MinDown && up) WindowState = FormWindowState.Minimized; } else if (showMin && showMax && _minButtonBounds.Contains(e.Location)) { _buttonState = ButtonState.MinOver; if (oldState == ButtonState.MinDown && up) WindowState = FormWindowState.Minimized; } else if (MaximizeBox && ControlBox && _maxButtonBounds.Contains(e.Location)) { _buttonState = ButtonState.MaxOver; if (oldState == ButtonState.MaxDown && up) MaximizeWindow(!_maximized); } else if (ControlBox && _xButtonBounds.Contains(e.Location)) { _buttonState = ButtonState.XOver; if (oldState == ButtonState.XDown && up && !LoadingObj.Visible) { Close(); } } else if (showHelpIcon && _helpButtonBounds.Contains(e.Location)) { _buttonState = ButtonState.HelpOver; if (oldState == ButtonState.HelpDown && up && !LoadingObj.Visible && HelpClick != null) { HelpClick(this, new MouseEventArgs(MouseButtons.Left, 0, e.Location.X, e.Location.Y, 0)); } } else _buttonState = ButtonState.None; } //if (oldState != _buttonState) Invalidate(); Invalidate(); } private void ResizeForm(ResizeDirection direction) { if (DesignMode) return; var dir = -1; switch (direction) { case ResizeDirection.BottomLeft: dir = HTBOTTOMLEFT; break; case ResizeDirection.Left: dir = HTLEFT; break; case ResizeDirection.Right: dir = HTRIGHT; break; case ResizeDirection.BottomRight: dir = HTBOTTOMRIGHT; break; case ResizeDirection.Bottom: dir = HTBOTTOM; break; } ReleaseCapture(); if (dir != -1) { SendMessage(Handle, WM_NCLBUTTONDOWN, dir, 0); } } } }