123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690 |
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Drawing;
- using System.Drawing.Drawing2D;
- using System.Windows.Forms;
- using System.Windows.Forms.VisualStyles;
- namespace PaintDotNet.SystemLayer
- {
- /// <summary>
- /// 包含与用户界面相关的静态方法
- /// </summary>
- public static class UI
- {
- private static bool initScales = false;
- private static float xScale;
- private static float yScale;
- public static void FlashForm(Form form)
- {
- IntPtr hWnd = form.Handle;
- SafeNativeMethods.FlashWindow(hWnd, false);
- SafeNativeMethods.FlashWindow(hWnd, false);
- GC.KeepAlive(form);
- }
- /// <summary>
- /// In some circumstances, the window manager will draw the window larger than it reports
- /// its size to be. You can use this function to retrieve the size of this extra border
- /// padding.
- /// </summary>
- /// <param name="window"></param>
- /// <returns>
- /// An integer greater than or equal to zero that describes the size of the border padding
- /// which is not reported via the window's Size or Bounds property.
- /// </returns>
- /// <remarks>
- /// Note to implementors: This method may simply return 0. It is provided for use in Windows
- /// Vista when DWM+Aero is enabled in which case sizable FloatingToolForm windows do not
- /// visibly dock to the correct locations.
- /// </remarks>
- public static int GetExtendedFrameBounds(Form window)
- {
- int returnVal;
- if (OS.IsVistaOrLater)
- {
- unsafe
- {
- int* rcVal = stackalloc int[4];
- int hr = SafeNativeMethods.DwmGetWindowAttribute(
- window.Handle,
- NativeConstants.DWMWA_EXTENDED_FRAME_BOUNDS,
- (void*)rcVal,
- 4 * (uint)sizeof(int));
- if (hr >= 0)
- {
- returnVal = -rcVal[0];
- }
- else
- {
- returnVal = 0;
- }
- }
- }
- else
- {
- returnVal = 0;
- }
- GC.KeepAlive(window);
- return Math.Max(0, returnVal);
- }
- private static void InitScaleFactors(Control c)
- {
- if (c == null)
- {
- xScale = 1.0f;
- yScale = 1.0f;
- }
- else
- {
- using (Graphics g = c.CreateGraphics())
- {
- xScale = g.DpiX / 96.0f;
- yScale = g.DpiY / 96.0f;
- }
- }
- initScales = true;
- }
- public static void InitScaling(Control c)
- {
- if (!initScales)
- {
- InitScaleFactors(c);
- }
- }
- public static float ScaleWidth(float width)
- {
- return (float)Math.Round(width * GetXScaleFactor());
- }
- public static int ScaleWidth(int width)
- {
- return (int)Math.Round((float)width * GetXScaleFactor());
- }
- public static int ScaleHeight(int height)
- {
- return (int)Math.Round((float)height * GetYScaleFactor());
- }
- public static float ScaleHeight(float height)
- {
- return (float)Math.Round(height * GetYScaleFactor());
- }
- public static Size ScaleSize(Size size)
- {
- return new Size(ScaleWidth(size.Width), ScaleHeight(size.Height));
- }
- public static Point ScalePoint(Point pt)
- {
- return new Point(ScaleWidth(pt.X), ScaleHeight(pt.Y));
- }
- public static float GetXScaleFactor()
- {
- if (!initScales)
- {
- return 1;
- // throw new InvalidOperationException("Must call InitScaling() first");
- }
- return xScale;
- }
- public static float GetYScaleFactor()
- {
- if (!initScales)
- {
- return 1;
- //throw new InvalidOperationException("Must call InitScaling() first");
- }
- return yScale;
- }
- public static void DrawCommandButton(
- Graphics g,
- PushButtonState state,
- Rectangle rect,
- Color backColor,
- Control childControl)
- {
- VisualStyleElement element = null;
- int alpha = 255;
- if (OS.IsVistaOrLater)
- {
- const string className = "BUTTON";
- const int partID = NativeConstants.BP_COMMANDLINK;
- int stateID;
- switch (state)
- {
- case PushButtonState.Default:
- stateID = NativeConstants.CMDLS_DEFAULTED;
- break;
- case PushButtonState.Disabled:
- stateID = NativeConstants.CMDLS_DISABLED;
- break;
- case PushButtonState.Hot:
- stateID = NativeConstants.CMDLS_HOT;
- break;
- case PushButtonState.Normal:
- stateID = NativeConstants.CMDLS_NORMAL;
- break;
- case PushButtonState.Pressed:
- stateID = NativeConstants.CMDLS_PRESSED;
- break;
- default:
- throw new InvalidEnumArgumentException();
- }
- try
- {
- element = VisualStyleElement.CreateElement(className, partID, stateID);
- if (!VisualStyleRenderer.IsElementDefined(element))
- {
- element = null;
- }
- }
- catch (InvalidOperationException)
- {
- element = null;
- }
- }
- if (element == null)
- {
- switch (state)
- {
- case PushButtonState.Default:
- element = VisualStyleElement.Button.PushButton.Default;
- alpha = 95;
- break;
- case PushButtonState.Disabled:
- element = VisualStyleElement.Button.PushButton.Disabled;
- break;
- case PushButtonState.Hot:
- element = VisualStyleElement.Button.PushButton.Hot;
- break;
- case PushButtonState.Normal:
- alpha = 0;
- element = VisualStyleElement.Button.PushButton.Normal;
- break;
- case PushButtonState.Pressed:
- element = VisualStyleElement.Button.PushButton.Pressed;
- break;
- default:
- throw new InvalidEnumArgumentException();
- }
- }
- if (element != null)
- {
- try
- {
- VisualStyleRenderer renderer = new VisualStyleRenderer(element);
- renderer.DrawParentBackground(g, rect, childControl);
- renderer.DrawBackground(g, rect);
- }
- catch (Exception)
- {
- element = null;
- }
- }
- if (element == null)
- {
- ButtonRenderer.DrawButton(g, rect, state);
- }
- if (alpha != 255)
- {
- using (Brush backBrush = new SolidBrush(Color.FromArgb(255 - alpha, backColor)))
- {
- CompositingMode oldCM = g.CompositingMode;
- try
- {
- g.CompositingMode = CompositingMode.SourceOver;
- g.FillRectangle(backBrush, rect);
- }
- finally
- {
- g.CompositingMode = oldCM;
- }
- }
- }
- }
- /// <summary>
- /// Sets the control's redraw state.
- /// </summary>
- /// <param name="control">The control whose state should be modified.</param>
- /// <param name="enabled">The new state for redrawing ability.</param>
- /// <remarks>
- /// Note to implementors: This method is used by SuspendControlPainting() and ResumeControlPainting().
- /// This may be implemented as a no-op.
- /// </remarks>
- private static void SetControlRedrawImpl(Control control, bool enabled)
- {
- SafeNativeMethods.SendMessageW(control.Handle, NativeConstants.WM_SETREDRAW, enabled ? new IntPtr(1) : IntPtr.Zero, IntPtr.Zero);
- GC.KeepAlive(control);
- }
- private static Dictionary<Control, int> controlRedrawStack = new Dictionary<Control, int>();
- /// <summary>
- /// Suspends the control's ability to draw itself.
- /// </summary>
- /// <param name="control">The control to suspend drawing for.</param>
- /// <remarks>
- /// When drawing is suspended, any painting performed in the control's WM_PAINT, OnPaint(),
- /// WM_ERASEBKND, or OnPaintBackground() handlers is completely ignored. Invalidation rectangles
- /// are not accumulated during this period, so when drawing is resumed (with
- /// ResumeControlPainting()), it is usually a good idea to call Invalidate(true) on the control.
- /// This method must be matched at a later time by a corresponding call to ResumeControlPainting().
- /// If you call SuspendControlPainting() multiple times for the same control, then you must
- /// call ResumeControlPainting() once for each call.
- /// Note to implementors: Do not modify this method. Instead, modify SetControlRedrawImpl(),
- /// which may be implemented as a no-op.
- /// </remarks>
- public static void SuspendControlPainting(Control control)
- {
- int pushCount;
- if (controlRedrawStack.TryGetValue(control, out pushCount))
- {
- ++pushCount;
- }
- else
- {
- pushCount = 1;
- }
- if (pushCount == 1)
- {
- SetControlRedrawImpl(control, false);
- }
- controlRedrawStack[control] = pushCount;
- }
- /// <summary>
- /// Resumes the control's ability to draw itself.
- /// </summary>
- /// <param name="control">The control to suspend drawing for.</param>
- /// <remarks>
- /// This method must be matched by a preceding call to SuspendControlPainting(). If that method
- /// was called multiple times, then this method must be called a corresponding number of times
- /// in order to enable drawing.
- /// This method must be matched at a later time by a corresponding call to ResumeControlPainting().
- /// If you call SuspendControlPainting() multiple times for the same control, then you must
- /// call ResumeControlPainting() once for each call.
- /// Note to implementors: Do not modify this method. Instead, modify SetControlRedrawImpl(),
- /// which may be implemented as a no-op.
- /// </remarks>
- public static void ResumeControlPainting(Control control)
- {
- int pushCount;
- if (controlRedrawStack.TryGetValue(control, out pushCount))
- {
- --pushCount;
- }
- else
- {
- throw new InvalidOperationException("There was no previous matching SuspendControlPainting() for this control");
- }
- if (pushCount == 0)
- {
- SetControlRedrawImpl(control, true);
- controlRedrawStack.Remove(control);
- }
- else
- {
- controlRedrawStack[control] = pushCount;
- }
- }
- /// <summary>
- /// Queries whether painting is enabled for the given control.
- /// </summary>
- /// <param name="control">The control to query suspension for.</param>
- /// <returns>
- /// false if the control's painting has been suspended via a call to SuspendControlPainting(),
- /// otherwise true.
- /// </returns>
- /// <remarks>
- /// You may use the return value of this method to optimize away painting. If this
- /// method returns false, then you may skip your entire OnPaint() method. This saves
- /// processor time by avoiding all of the non-painting drawing and resource initialization
- /// and destruction that is typically contained in OnPaint().
- /// This method assumes painting suspension is being exclusively managed with Suspend-
- /// and ResumeControlPainting().
- /// </remarks>
- public static bool IsControlPaintingEnabled(Control control)
- {
- int pushCount;
- if (!controlRedrawStack.TryGetValue(control, out pushCount))
- {
- pushCount = 0;
- }
- return (pushCount == 0);
- }
- private static IntPtr hRgn = SafeNativeMethods.CreateRectRgn(0, 0, 1, 1);
- /// <summary>
- /// This method retrieves the update region of a control.
- /// </summary>
- /// <param name="control">The control to retrieve the update region for.</param>
- /// <returns>
- /// An array of rectangles specifying the area that has been invalidated, or
- /// null if this could not be determined.
- /// </returns>
- /// <remarks>
- /// This method is not thread safe.
- /// Note to implementors: This method may be implemented as a no-op. In this case, just return null.
- /// </remarks>
- public static Rectangle[] GetUpdateRegion(Control control)
- {
- SafeNativeMethods.GetUpdateRgn(control.Handle, hRgn, false);
- Rectangle[] scans;
- int area;
- PdnGraphics.GetRegionScans(hRgn, out scans, out area);
- GC.KeepAlive(control);
- return scans;
- }
- /// <summary>
- /// Sets a form's opacity.
- /// </summary>
- /// <param name="form"></param>
- /// <param name="opacity"></param>
- /// <remarks>
- /// Note to implementors: This may be implemented as just "form.Opacity = opacity".
- /// This method works around some visual clumsiness in .NET 2.0 related to
- /// transitioning between opacity == 1.0 and opacity != 1.0.</remarks>
- public static void SetFormOpacity(Form form, double opacity)
- {
- if (opacity < 0.0 || opacity > 1.0)
- {
- throw new ArgumentOutOfRangeException("opacity", "must be in the range [0, 1]");
- }
- uint exStyle = SafeNativeMethods.GetWindowLongW(form.Handle, NativeConstants.GWL_EXSTYLE);
- byte bOldAlpha = 255;
- if ((exStyle & NativeConstants.GWL_EXSTYLE) != 0)
- {
- uint dwOldKey;
- uint dwOldFlags;
- bool result = SafeNativeMethods.GetLayeredWindowAttributes(form.Handle, out dwOldKey, out bOldAlpha, out dwOldFlags);
- }
- byte bNewAlpha = (byte)(opacity * 255.0);
- uint newExStyle = exStyle;
- if (bNewAlpha != 255)
- {
- newExStyle |= NativeConstants.WS_EX_LAYERED;
- }
- if (newExStyle != exStyle || (newExStyle & NativeConstants.WS_EX_LAYERED) != 0)
- {
- if (newExStyle != exStyle)
- {
- SafeNativeMethods.SetWindowLongW(form.Handle, NativeConstants.GWL_EXSTYLE, newExStyle);
- }
- if ((newExStyle & NativeConstants.WS_EX_LAYERED) != 0)
- {
- SafeNativeMethods.SetLayeredWindowAttributes(form.Handle, 0, bNewAlpha, NativeConstants.LWA_ALPHA);
- }
- }
- GC.KeepAlive(form);
- }
- /// <summary>
- /// This WndProc implements click-through functionality. Some controls (MenuStrip, ToolStrip) will not
- /// recognize a click unless the form they are hosted in is active. So the first click will activate the
- /// form and then a second is required to actually make the click happen.
- /// </summary>
- /// <param name="m">The Message that was passed to your WndProc.</param>
- /// <returns>true if the message was processed, false if it was not</returns>
- /// <remarks>
- /// You should first call base.WndProc(), and then call this method. This method is only intended to
- /// change a return value, not to change actual processing before that.
- /// </remarks>
- internal static bool ClickThroughWndProc(ref Message m)
- {
- bool returnVal = false;
- if (m.Msg == NativeConstants.WM_MOUSEACTIVATE)
- {
- if (m.Result == (IntPtr)NativeConstants.MA_ACTIVATEANDEAT)
- {
- m.Result = (IntPtr)NativeConstants.MA_ACTIVATE;
- returnVal = true;
- }
- }
- return returnVal;
- }
- public static bool IsOurAppActive
- {
- get
- {
- foreach (Form form in Application.OpenForms)
- {
- if (form == Form.ActiveForm)
- {
- return true;
- }
- }
- return false;
- }
- }
- private static VisualStyleClass DetermineVisualStyleClass()
- {
- return Do.TryCatch(DetermineVisualStyleClassImpl, ex => VisualStyleClass.Other);
- }
- private static VisualStyleClass DetermineVisualStyleClassImpl()
- {
- VisualStyleClass vsClass;
- if (!VisualStyleInformation.IsSupportedByOS)
- {
- vsClass = VisualStyleClass.Classic;
- }
- else if (!VisualStyleInformation.IsEnabledByUser)
- {
- vsClass = VisualStyleClass.Classic;
- }
- else if (0 == string.Compare(VisualStyleInformation.Author, "MSX", StringComparison.InvariantCulture) &&
- 0 == string.Compare(VisualStyleInformation.DisplayName, "Aero style", StringComparison.InvariantCulture))
- {
- vsClass = VisualStyleClass.Aero;
- }
- else if (0 == string.Compare(VisualStyleInformation.Company, "Microsoft Corporation", StringComparison.InvariantCulture) &&
- 0 == string.Compare(VisualStyleInformation.Author, "Microsoft Design Team", StringComparison.InvariantCulture))
- {
- if (0 == string.Compare(VisualStyleInformation.DisplayName, "Windows XP style", StringComparison.InvariantCulture) || // Luna
- 0 == string.Compare(VisualStyleInformation.DisplayName, "Zune Style", StringComparison.InvariantCulture) || // Zune
- 0 == string.Compare(VisualStyleInformation.DisplayName, "Media Center style", StringComparison.InvariantCulture)) // Royale
- {
- vsClass = VisualStyleClass.Luna;
- }
- else
- {
- vsClass = VisualStyleClass.Other;
- }
- }
- else
- {
- vsClass = VisualStyleClass.Other;
- }
- return vsClass;
- }
- public static VisualStyleClass VisualStyleClass
- {
- get
- {
- return DetermineVisualStyleClass();
- }
- }
- public static void EnableShield(Button button, bool enableShield)
- {
- IntPtr hWnd = button.Handle;
- SafeNativeMethods.SendMessageW(
- hWnd,
- NativeConstants.BCM_SETSHIELD,
- IntPtr.Zero,
- enableShield ? new IntPtr(1) : IntPtr.Zero);
- GC.KeepAlive(button);
- }
- public static bool HideAllScrollBar(Control control)
- {
- return SafeNativeMethods.ShowScrollBar(control.Handle, 3, false);
- }
- public static bool HideHorizontalScrollBar(Control control)
- {
- return SafeNativeMethods.ShowScrollBar(control.Handle, NativeConstants.SB_HORZ, false);
- }
- public static bool HideVerticalScrollBar(Control control)
- {
- return SafeNativeMethods.ShowScrollBar(control.Handle, NativeConstants.SB_VERT, false);
- }
-
- public static void RestoreWindow(IWin32Window window)
- {
- IntPtr hWnd = window.Handle;
- SafeNativeMethods.ShowWindow(hWnd, NativeConstants.SW_RESTORE);
- GC.KeepAlive(window);
- }
- public static void ShowComboBox(ComboBox comboBox, bool show)
- {
- IntPtr hWnd = comboBox.Handle;
- SafeNativeMethods.SendMessageW(
- hWnd,
- NativeConstants.CB_SHOWDROPDOWN,
- show ? new IntPtr(1) : IntPtr.Zero,
- IntPtr.Zero);
- GC.KeepAlive(comboBox);
- }
- /// <summary>
- /// Disables the system menu "Close" menu command, as well as the "X" close button on the window title bar.
- /// </summary>
- /// <remarks>
- /// Note to implementors: This method may *not* be implemented as a no-op. The purpose is to make it so that
- /// calling the Close() method is the only way to close a dialog, which is something that can only be done
- /// programmatically.
- /// </remarks>
- public static void DisableCloseBox(IWin32Window window)
- {
- IntPtr hWnd = window.Handle;
- IntPtr hMenu = SafeNativeMethods.GetSystemMenu(hWnd, false);
- if (hMenu == IntPtr.Zero)
- {
- NativeMethods.ThrowOnWin32Error("GetSystemMenu() returned NULL");
- }
- int result = SafeNativeMethods.EnableMenuItem(
- hMenu,
- NativeConstants.SC_CLOSE,
- NativeConstants.MF_BYCOMMAND | NativeConstants.MF_GRAYED);
- bool bResult = SafeNativeMethods.DrawMenuBar(hWnd);
- if (!bResult)
- {
- NativeMethods.ThrowOnWin32Error("DrawMenuBar returned FALSE");
- }
- GC.KeepAlive(window);
- }
- internal static void InvokeThroughModalTrampoline(IWin32Window owner, Procedure<IWin32Window> invokeMe)
- {
- using (Form modalityFix = new Form())
- {
- modalityFix.ShowInTaskbar = false;
- modalityFix.TransparencyKey = modalityFix.BackColor;
- UI.SetFormOpacity(modalityFix, 0);
- modalityFix.ControlBox = false;
- modalityFix.FormBorderStyle = FormBorderStyle.None;
- Control ownerAsControl = owner as Control;
- if (ownerAsControl != null)
- {
- Form ownerForm = ownerAsControl.FindForm();
- if (ownerForm != null)
- {
- Rectangle clientRect = ownerForm.RectangleToScreen(ownerForm.ClientRectangle);
- modalityFix.Icon = ownerForm.Icon;
- modalityFix.Location = clientRect.Location;
- modalityFix.Size = clientRect.Size;
- modalityFix.StartPosition = FormStartPosition.Manual;
- }
- }
- modalityFix.Shown +=
- delegate (object sender, EventArgs e)
- {
- invokeMe(modalityFix);
- modalityFix.Close();
- };
- modalityFix.ShowDialog(owner);
- GC.KeepAlive(modalityFix);
- }
- }
- }
- }
|